diff --git a/include/retdec/bin2llvmir/optimizations/param_return/collector/collector.h b/include/retdec/bin2llvmir/optimizations/param_return/collector/collector.h new file mode 100644 index 000000000..67e3c4c17 --- /dev/null +++ b/include/retdec/bin2llvmir/optimizations/param_return/collector/collector.h @@ -0,0 +1,101 @@ +/** +* @file include/retdec/bin2llvmir/optimizations/param_return/collector/collector.h +* @brief Collects possible arguments and returns of functions. +* @copyright (c) 2019 Avast Software, licensed under the MIT license +*/ + +#ifndef RETDEC_BIN2LLVMIR_OPTIMIZATIONS_PARAM_RETURN_COLLECTOR_COLLECTOR_H +#define RETDEC_BIN2LLVMIR_OPTIMIZATIONS_PARAM_RETURN_COLLECTOR_COLLECTOR_H + +#include +#include + +#include +#include + +#include "retdec/bin2llvmir/analyses/reaching_definitions.h" +#include "retdec/bin2llvmir/optimizations/param_return/data_entries.h" +#include "retdec/bin2llvmir/providers/abi/abi.h" + +namespace retdec { +namespace bin2llvmir { + +class Collector +{ + public: + typedef std::unique_ptr Ptr; + + public: + Collector( + const Abi* abi, + llvm::Module* m, + const ReachingDefinitionsAnalysis* rda); + + virtual ~Collector(); + + public: + virtual void collectCallArgs(CallEntry* ce) const; + virtual void collectCallRets(CallEntry* ce) const; + + virtual void collectDefArgs(DataFlowEntry* de) const; + virtual void collectDefRets(DataFlowEntry* de) const; + + virtual void collectCallSpecificTypes(CallEntry* ce) const; + + protected: + + void collectRetStores(ReturnEntry* re) const; + + void collectStoresBeforeInstruction( + llvm::Instruction* i, + std::vector& stores) const; + + void collectLoadsAfterInstruction( + llvm::Instruction* i, + std::vector& loads) const; + + bool collectLoadsAfterInstruction( + llvm::Instruction* i, + std::vector& loads, + std::set& excluded) const; + + void collectStoresInSinglePredecessors( + llvm::Instruction* i, + std::vector& stores) const; + + void collectStoresRecursively( + llvm::Instruction* i, + std::vector& stores, + std::map>& seen) const; + + bool collectStoresInInstructionBlock( + llvm::Instruction* i, + std::set& values, + std::vector& stores) const; + + protected: + bool extractFormatString(CallEntry* ce) const; + + bool storesString(llvm::StoreInst* si, std::string& str) const; + llvm::Value* getRoot(llvm::Value* i, bool first = true) const; + + protected: + const Abi* _abi; + llvm::Module* _module; + const ReachingDefinitionsAnalysis* _rda; +}; + +class CollectorProvider +{ + public: + static Collector::Ptr createCollector( + const Abi* abi, + llvm::Module* m, + const ReachingDefinitionsAnalysis* rda); +}; + +} // namespace bin2llvmir +} // namespace retdec + +#endif diff --git a/include/retdec/bin2llvmir/optimizations/param_return/collector/pic32.h b/include/retdec/bin2llvmir/optimizations/param_return/collector/pic32.h new file mode 100644 index 000000000..e69ae762b --- /dev/null +++ b/include/retdec/bin2llvmir/optimizations/param_return/collector/pic32.h @@ -0,0 +1,32 @@ +/** +* @file include/retdec/bin2llvmir/optimizations/param_return/collector/pic32.h +* @brief Pic32 specific collection algorithms. +* @copyright (c) 2019 Avast Software, licensed under the MIT license +*/ + +#ifndef RETDEC_BIN2LLVMIR_OPTIMIZATIONS_PARAM_RETURN_COLLECTOR_PIC32_H +#define RETDEC_BIN2LLVMIR_OPTIMIZATIONS_PARAM_RETURN_COLLECTOR_PIC32_H + +#include "retdec/bin2llvmir/optimizations/param_return/collector/collector.h" + +namespace retdec { +namespace bin2llvmir { + +class CollectorPic32 : public Collector +{ + public: + CollectorPic32( + const Abi* abi, + llvm::Module* m, + const ReachingDefinitionsAnalysis* rda); + + virtual ~CollectorPic32() override; + + public: + virtual void collectCallSpecificTypes(CallEntry* ce) const override; +}; + +} // namespace bin2llvmir +} // namespace retdec + +#endif diff --git a/include/retdec/bin2llvmir/optimizations/param_return/data_entries.h b/include/retdec/bin2llvmir/optimizations/param_return/data_entries.h new file mode 100644 index 000000000..c59e93272 --- /dev/null +++ b/include/retdec/bin2llvmir/optimizations/param_return/data_entries.h @@ -0,0 +1,185 @@ +/** +* @file include/retdec/bin2llvmir/optimizations/param_return/data_entries.h +* @brief Data entries for parameter analysis. +* @copyright (c) 2019 Avast Software, licensed under the MIT license +*/ + +#ifndef RETDEC_BIN2LLVMIR_OPTIMIZATIONS_PARAM_RETURN_DATA_ENTRIES_H +#define RETDEC_BIN2LLVMIR_OPTIMIZATIONS_PARAM_RETURN_DATA_ENTRIES_H + +#include + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +#include +#include + +namespace retdec { +namespace bin2llvmir { + +class ReturnEntry +{ + public: + ReturnEntry(llvm::ReturnInst* r); + + public: + void addRetStore(llvm::StoreInst* st); + + void setRetStores(std::vector&& stores); + void setRetStores(const std::vector& stores); + void setRetValues(std::vector&& values); + + void setRetValues(const std::vector& values); + + public: + llvm::ReturnInst* getRetInstruction() const; + + const std::vector& retStores() const; + const std::vector& retValues() const; + + + protected: + llvm::ReturnInst* _retInst = nullptr; + + std::vector _retStores; + std::vector _retValues; +}; + +class CallableEntry +{ + public: + bool isVoidarg() const; + + void addArg(llvm::Value* arg); + + void setVoidarg(bool voidarg = true); + void setArgTypes( + std::vector&& types, + std::vector&& names = {}); + + public: + const std::vector& args() const; + const std::vector& argTypes() const; + const std::vector& argNames() const; + + protected: + std::vector _args; + std::vector _argTypes; + std::vector _argNames; + + protected: + bool _voidarg = false; +}; + +class FunctionEntry : public CallableEntry +{ + public: + bool isVariadic() const; + bool isWrapper() const; + + public: + void addRetEntry(const ReturnEntry& ret); + ReturnEntry* createRetEntry(llvm::ReturnInst* ret); + + void setArgs(std::vector&& args); + void setVariadic(bool variadic = true); + void setWrappedCall(llvm::CallInst* wrap); + void setRetType(llvm::Type* type); + void setRetValue(llvm::Value* val); + void setCallingConvention(const CallingConvention::ID& cc); + + public: + llvm::Value* getRetValue() const; + llvm::Type* getRetType() const; + llvm::CallInst* getWrappedCall() const; + CallingConvention::ID getCallingConvention() const; + + const std::vector& retEntries() const; + std::vector& retEntries(); + + private: + llvm::CallInst* _wrap = nullptr; + llvm::Type* _retType = nullptr; + llvm::Value* _retVal = nullptr; + bool _variadic = false; + CallingConvention::ID _callconv = CallingConvention::ID::CC_UNKNOWN; + + std::vector _retEntries; +}; + +class CallEntry : public CallableEntry +{ + // Constructor. + // + public: + CallEntry( + llvm::CallInst* call, + const FunctionEntry* base = nullptr); + + // Usage data. + // + public: + void addRetLoad(llvm::LoadInst* load); + + void setFormatString(const std::string& fmt); + void setArgStores(std::vector&& stores); + void setArgs(std::vector&& args); + void setRetLoads(std::vector&& loads); + void setRetValues(std::vector&& values); + + llvm::CallInst* getCallInstruction() const; + const FunctionEntry* getBaseFunction() const; + std::string getFormatString() const; + + public: + const std::vector& argStores() const; + const std::vector& retValues() const; + const std::vector& retLoads() const; + + private: + const FunctionEntry* _baseFunction; + + llvm::CallInst* _callInst = nullptr; + std::string _fmtStr = ""; + + std::vector _retLoads; + std::vector _retValues; + std::vector _argStores; +}; + +class DataFlowEntry : public FunctionEntry +{ + // Constructor + // + public: + DataFlowEntry(llvm::Value* called); + + // Type information + // + public: + bool isFunction() const; + bool isValue() const; + bool hasDefinition() const; + + llvm::Function* getFunction() const; + llvm::Value* getValue() const; + + void setCalledValue(llvm::Value* called); + + // Usage data. + // + public: + CallEntry* createCallEntry(llvm::CallInst *call); + const std::vector& callEntries() const; + std::vector& callEntries(); + + private: + llvm::Value* _calledValue = nullptr; + + std::vector _calls; +}; + +} // namespace bin2llvmir +} // namespace retdec + +#endif diff --git a/include/retdec/bin2llvmir/optimizations/param_return/filter/filter.h b/include/retdec/bin2llvmir/optimizations/param_return/filter/filter.h new file mode 100644 index 000000000..46bb0c37b --- /dev/null +++ b/include/retdec/bin2llvmir/optimizations/param_return/filter/filter.h @@ -0,0 +1,199 @@ +/** +* @file include/retdec/bin2llvmir/optimizations/param_return/filter/filter.h +* @brief Filters potentail values according to calling convention. +* @copyright (c) 2019 Avast Software, licensed under the MIT license +*/ + +#ifndef RETDEC_BIN2LLVMIR_OPTIMIZATIONS_PARAM_RETURN_FILTER_FILTER_H +#define RETDEC_BIN2LLVMIR_OPTIMIZATIONS_PARAM_RETURN_FILTER_FILTER_H + +#include + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" +#include "retdec/bin2llvmir/optimizations/param_return/collector/collector.h" +#include "retdec/bin2llvmir/optimizations/param_return/data_entries.h" + +namespace retdec { +namespace bin2llvmir { + +class FilterableLayout +{ + public: + enum class Order { + ORD_GPR, + ORD_GPR_GROUP, + ORD_FPR, + ORD_FPR_GROUP, + ORD_DOUBR, + ORD_DOUBR_GROUP, + ORD_VECR, + ORD_VECR_GROUP, + ORD_STACK, + ORD_STACK_GROUP + }; + + public: + std::vector gpRegisters; + std::vector fpRegisters; + std::vector doubleRegisters; + std::vector vectorRegisters; + std::vector stacks; + std::vector knownTypes; + std::vector knownOrder; +}; + +typedef FilterableLayout::Order OrderID; + +class Filter +{ + public: + typedef std::unique_ptr Ptr; + + public: + Filter(const Abi* _abi, const CallingConvention* _cc); + virtual ~Filter(); + + void filterDefinition(DataFlowEntry* de) const; + void filterCalls(DataFlowEntry* de) const; + void filterCallsVariadic( + DataFlowEntry* de, + const Collector* collector) const; + + void estimateRetValue(DataFlowEntry* de) const; + + protected: + virtual void filterDefinitionArgs( + FilterableLayout& args, + bool isVoidarg) const; + + virtual void filterCallArgs( + FilterableLayout& args, + bool isVoidarg) const; + + virtual void filterCallArgsByDefLayout( + FilterableLayout& args, + const FilterableLayout& def) const; + + virtual void filterRets( + FilterableLayout& rets) const; + + virtual void filterRetsByDefLayout( + FilterableLayout& args, + const FilterableLayout& def) const; + + virtual void filterArgsByKnownTypes(FilterableLayout& lay) const; + virtual void filterRetsByKnownTypes(FilterableLayout& lay) const; + + protected: + void leaveCommonArgs( + std::vector& allArgs) const; + + void leaveCommonRets( + std::vector& allRets) const; + + void leaveCommon( + std::vector& allRets) const; + + void orderFiterableLayout(FilterableLayout& lay) const; + + void orderStacks( + std::vector& stacks, + bool asc = true) const; + + void orderRegistersBy( + std::vector& regs, + const std::vector& orderedVector) const; + + protected: + FilterableLayout createArgsFilterableLayout( + const std::vector& group, + const std::vector& knownTypes) const; + + FilterableLayout createRetsFilterableLayout( + const std::vector& group, + llvm::Type* knownType) const; + + FilterableLayout createRetsFilterableLayout( + const std::vector& group, + const std::vector& knownTypes) const; + + virtual FilterableLayout separateArgValues( + const std::vector& paramValues) const; + + virtual FilterableLayout separateRetValues( + const std::vector& paramValues) const; + + virtual std::vector createGroupedArgValues( + const FilterableLayout& lay) const; + + virtual std::vector createGroupedRetValues( + const FilterableLayout& lay) const; + + FilterableLayout separateValues( + const std::vector& paramValues, + const std::vector& gpRegs, + const std::vector& fpRegs, + const std::vector& doubleRegs, + const std::vector& vecRegs) const; + + std::vector createGroupedValues( + const FilterableLayout& lay) const; + + std::vector expandTypes( + const std::vector& types) const; + + protected: + std::size_t fetchGPRegsForType( + llvm::Type* type, + FilterableLayout& lay) const; + + std::size_t fetchFPRegsForType( + llvm::Type* type, + FilterableLayout& lay) const; + + std::size_t fetchDoubleRegsForType( + llvm::Type* type, + FilterableLayout& lay) const; + + std::size_t fetchVecRegsForType( + llvm::Type* type, + FilterableLayout& lay) const; + + std::size_t fetchRegsForType( + llvm::Type* type, + std::vector& store, + const std::vector& regs, + std::size_t maxRegsPerObject) const; + + protected: + std::size_t getNumberOfStacksForType(llvm::Type* type) const; + + protected: + void leaveOnlyPositiveStacks(FilterableLayout& lay) const; + void leaveOnlyContinuousStack(FilterableLayout& lay) const; + void leaveOnlyContinuousArgRegisters(FilterableLayout& lay) const; + void leaveOnlyContinuousRetRegisters(FilterableLayout& lay) const; + void leaveSameStacks(FilterableLayout& lay, const FilterableLayout& fig) const; + + void leaveOnlyContinuousRegisters( + std::vector& regs, + const std::vector& templRegs) const; + + void createContinuousArgRegisters(FilterableLayout& lay) const; + + protected: + const Abi* _abi; + const CallingConvention* _cc; +}; + +class FilterProvider +{ + public: + static Filter::Ptr createFilter(Abi* abi, const CallingConvention::ID& id); +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/optimizations/param_return/filter/ms_x64.h b/include/retdec/bin2llvmir/optimizations/param_return/filter/ms_x64.h new file mode 100644 index 000000000..cb6e9c967 --- /dev/null +++ b/include/retdec/bin2llvmir/optimizations/param_return/filter/ms_x64.h @@ -0,0 +1,38 @@ +/** +* @file include/retdec/bin2llvmir/optimizations/param_return/filter/ms_x64.h +* @brief Microsoft x64 specific filtration of registers. +* @copyright (c) 2019 Avast Software, licensed under the MIT license +*/ + +#ifndef RETDEC_BIN2LLVMIR_OPTIMIZATIONS_PARAM_RETURN_FILTER_MS_X64_H +#define RETDEC_BIN2LLVMIR_OPTIMIZATIONS_PARAM_RETURN_FILTER_MS_X64_H + +#include "retdec/bin2llvmir/optimizations/param_return/filter/filter.h" + +namespace retdec { +namespace bin2llvmir { + +class MSX64Filter : public Filter +{ + public: + MSX64Filter(const Abi* _abi, const CallingConvention* _cc); + virtual ~MSX64Filter() override; + + virtual void filterDefinitionArgs( + FilterableLayout& args, + bool isVoidarg) const override; + + virtual void filterCallArgs( + FilterableLayout& args, + bool isVoidarg) const override; + + virtual void filterArgsByKnownTypes(FilterableLayout& lay) const override; + + private: + void leaveOnlyAlternatingArgRegisters(FilterableLayout& lay) const; +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/optimizations/param_return/param_return.h b/include/retdec/bin2llvmir/optimizations/param_return/param_return.h index 78ee990fe..abda626a5 100644 --- a/include/retdec/bin2llvmir/optimizations/param_return/param_return.h +++ b/include/retdec/bin2llvmir/optimizations/param_return/param_return.h @@ -1,7 +1,7 @@ /** * @file include/retdec/bin2llvmir/optimizations/param_return/param_return.h * @brief Detect functions' parameters and returns. -* @copyright (c) 2017 Avast Software, licensed under the MIT license +* @copyright (c) 2019 Avast Software, licensed under the MIT license */ #ifndef RETDEC_BIN2LLVMIR_OPTIMIZATIONS_PARAM_RETURN_PARAM_RETURN_H @@ -16,6 +16,8 @@ #include #include "retdec/bin2llvmir/analyses/reaching_definitions.h" +#include "retdec/bin2llvmir/optimizations/param_return/collector/collector.h" +#include "retdec/bin2llvmir/optimizations/param_return/data_entries.h" #include "retdec/bin2llvmir/providers/abi/abi.h" #include "retdec/bin2llvmir/providers/config.h" #include "retdec/bin2llvmir/providers/debugformat.h" @@ -25,115 +27,6 @@ namespace retdec { namespace bin2llvmir { -class CallEntry -{ - public: - CallEntry(llvm::CallInst* c); - - public: - void filterRegisters(Abi* _abi, Config* _config); - void filterSort(Config* _config); - void filterLeaveOnlyContinuousStackOffsets(Config* _config); - void filterLeaveOnlyNeededStackOffsets(Abi* _abi, Config* _config); - - void extractFormatString(ReachingDefinitionsAnalysis& _RDA); - - public: - llvm::CallInst* call = nullptr; - std::vector possibleArgStores; - std::vector possibleRetLoads; - std::string formatStr; -}; - -class ReturnEntry -{ - public: - ReturnEntry(llvm::ReturnInst* r); - - public: - llvm::ReturnInst* ret = nullptr; - std::vector possibleRetStores; -}; - -class DataFlowEntry -{ - public: - DataFlowEntry( - llvm::Module* m, - ReachingDefinitionsAnalysis& rda, - Config* c, - Abi* abi, - FileImage* img, - DebugFormat* dbg, - Lti* lti, - llvm::Value* v); - - bool isFunctionEntry() const; - bool isValueEntry() const; - llvm::Value* getValue() const; - llvm::Function* getFunction() const; - void dump() const; - - void addCall(llvm::CallInst* call); - - void filter(); - - void applyToIr(); - void applyToIrOrdinary(); - void applyToIrVariadic(); - void connectWrappers(); - - private: - void addArgLoads(); - void addRetStores(); - void addCallArgs(llvm::CallInst* call, CallEntry& ce); - void addCallReturns(llvm::CallInst* call, CallEntry& ce); - - void callsFilterCommonRegisters(); - void callsFilterSameNumberOfStacks(); - - void setTypeFromExtraInfo(); - void setTypeFromUseContext(); - void setReturnType(); - void setArgumentTypes(); - - void filterRegistersArgLoads(); - void filterSortArgLoads(); - - llvm::CallInst* isSimpleWrapper(llvm::Function* fnc); - - public: - llvm::Module* _module = nullptr; - ReachingDefinitionsAnalysis& _RDA; - Config* _config = nullptr; - Abi* _abi = nullptr; - FileImage* _image = nullptr; - Lti* _lti = nullptr; - - llvm::Value* called = nullptr; - retdec::config::Function* configFnc = nullptr; - retdec::config::Function* dbgFnc = nullptr; - - // In caller. - // - std::vector calls; - - // In called function. - // - std::vector argLoads; - std::vector retStores; - - // Result. - // - bool typeSet = false; - llvm::Type* retType = nullptr; - std::vector argTypes; - std::map specialArgStorage; - bool isVarArg = false; - llvm::CallInst* wrappedCall = nullptr; - std::vector argNames; -}; - class ParamReturn : public llvm::ModulePass { public: @@ -150,17 +43,50 @@ class ParamReturn : public llvm::ModulePass private: bool run(); - void dumpInfo(); + void dumpInfo() const; + void dumpInfo(const DataFlowEntry& de) const; + void dumpInfo(const CallEntry& ce) const; + void dumpInfo(const ReturnEntry& de) const; + // Collection of functions. + // + private: void collectAllCalls(); - std::string extractFormatString(llvm::CallInst* call); + DataFlowEntry createDataFlowEntry(llvm::Value* calledValue) const; + + private: + void collectExtraData(DataFlowEntry* de) const; + void collectExtraData(CallEntry* ce) const; + + void collectCallSpecificTypes(CallEntry* ce) const; + + // Collection of functions usage data. + // + private: + void addDataFromCall(DataFlowEntry* dataflow, llvm::CallInst* call) const; + + // Optimizations. + // + private: + llvm::CallInst* getWrapper(llvm::Function* fnc) const; + llvm::Type* extractType(llvm::Value* from) const; + + // Filtration of collected functions arguments. + // + private: void filterCalls(); - void filterSort(CallEntry& ce); - void filterLeaveOnlyContinuousStackOffsets(CallEntry& ce); - void filterLeaveOnlyNeededStackOffsets(CallEntry& ce); + void modifyType(DataFlowEntry& de) const; + // Modification of functions in IR. + // + private: void applyToIr(); + void applyToIr(DataFlowEntry& de); + void connectWrappers(const DataFlowEntry& de); + + std::map> fetchLoadsOfCalls( + const std::vector& calls) const; private: llvm::Module* _module = nullptr; @@ -172,6 +98,7 @@ class ParamReturn : public llvm::ModulePass std::map _fnc2calls; ReachingDefinitionsAnalysis _RDA; + Collector::Ptr _collector; }; } // namespace bin2llvmir diff --git a/include/retdec/bin2llvmir/providers/abi/abi.h b/include/retdec/bin2llvmir/providers/abi/abi.h index 8e0bc2e81..eca8192cf 100644 --- a/include/retdec/bin2llvmir/providers/abi/abi.h +++ b/include/retdec/bin2llvmir/providers/abi/abi.h @@ -16,6 +16,7 @@ #include "retdec/bin2llvmir/providers/asm_instruction.h" #include "retdec/bin2llvmir/providers/config.h" +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" //#include "retdec/capstone2llvmir/x86/x86_defs.h" @@ -39,24 +40,31 @@ class Abi // Registers. // public: - bool isRegister(const llvm::Value* val); - bool isRegister(const llvm::Value* val, uint32_t r); + bool isRegister(const llvm::Value* val) const; + bool isRegister(const llvm::Value* val, uint32_t r) const; bool isFlagRegister(const llvm::Value* val); - bool isStackPointerRegister(const llvm::Value* val); + bool isStackPointerRegister(const llvm::Value* val) const; bool isZeroRegister(const llvm::Value* val); - virtual bool isGeneralPurposeRegister(const llvm::Value* val) = 0; + virtual bool isGeneralPurposeRegister(const llvm::Value* val) const = 0; - llvm::GlobalVariable* getRegister(uint32_t r, bool use = true); - uint32_t getRegisterId(const llvm::Value* r); + llvm::GlobalVariable* getRegister(uint32_t r, bool use = true) const; + uint32_t getRegisterId(const llvm::Value* r) const; const std::vector& getRegisters() const; - llvm::GlobalVariable* getStackPointerRegister(); - llvm::GlobalVariable* getZeroRegister(); + llvm::GlobalVariable* getStackPointerRegister() const; + llvm::GlobalVariable* getZeroRegister() const; + + std::size_t getRegisterByteSize(uint32_t r) const; void addRegister(uint32_t id, llvm::GlobalVariable* reg); llvm::GlobalVariable* getSyscallIdRegister(); llvm::GlobalVariable* getSyscallReturnRegister(); llvm::GlobalVariable* getSyscallArgumentRegister(unsigned n); + + // Stacks. + // + public: + bool isStackVariable(const llvm::Value* val) const; // Instructions. // @@ -67,23 +75,43 @@ class Abi // Types. // public: - std::size_t getTypeByteSize(llvm::Type* t) const; - std::size_t getTypeBitSize(llvm::Type* t) const; + virtual std::size_t getTypeByteSize(llvm::Type* t) const; + virtual std::size_t getTypeBitSize(llvm::Type* t) const; llvm::IntegerType* getDefaultType() const; llvm::PointerType* getDefaultPointerType() const; + std::size_t getWordSize() const; static std::size_t getTypeByteSize(llvm::Module* m, llvm::Type* t); static std::size_t getTypeBitSize(llvm::Module* m, llvm::Type* t); static llvm::IntegerType* getDefaultType(llvm::Module* m); + static llvm::Type* getDefaultFPType(llvm::Module* m); static llvm::PointerType* getDefaultPointerType(llvm::Module* m); + static std::size_t getWordSize(llvm::Module* m); // Architectures. // public: bool isMips() const; + bool isMips64() const; bool isArm() const; + bool isArm64() const; bool isX86() const; + bool isX64() const; bool isPowerPC() const; + bool isPowerPC64() const; + bool isPic32() const; + + // Calling conventions. + // + public: + CallingConvention* getCallingConvention( + const CallingConvention::ID& cc); + CallingConvention* getDefaultCallingConvention(); + + // Config. + // + public: + Config* getConfig() const; // Private data - misc. // @@ -116,6 +144,13 @@ class Abi uint32_t _regSyscallId = REG_INVALID; /// Register that is always equal to zero - not every arch have this. uint32_t _regZeroReg = REG_INVALID; + + // Private data - calling convention + // + protected: + std::map _id2cc; + CallingConvention::ID _defcc; + }; class AbiProvider diff --git a/include/retdec/bin2llvmir/providers/abi/arm.h b/include/retdec/bin2llvmir/providers/abi/arm.h index 33c58dd82..b4f9348c7 100644 --- a/include/retdec/bin2llvmir/providers/abi/arm.h +++ b/include/retdec/bin2llvmir/providers/abi/arm.h @@ -23,7 +23,7 @@ class AbiArm : public Abi // Registers. // public: - virtual bool isGeneralPurposeRegister(const llvm::Value* val) override; + virtual bool isGeneralPurposeRegister(const llvm::Value* val) const override; // Instructions. // diff --git a/include/retdec/bin2llvmir/providers/abi/arm64.h b/include/retdec/bin2llvmir/providers/abi/arm64.h new file mode 100644 index 000000000..ea9078f96 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/abi/arm64.h @@ -0,0 +1,37 @@ +/** + * @file include/retdec/bin2llvmir/providers/abi/arm64.h + * @brief ABI information for ARM64. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_ABI_ARM64_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_ABI_ARM64_H + +#include "retdec/bin2llvmir/providers/abi/abi.h" + +namespace retdec { +namespace bin2llvmir { + +class AbiArm64 : public Abi +{ + // Ctors, dtors. + // + public: + AbiArm64(llvm::Module* m, Config* c); + virtual ~AbiArm64(); + + // Registers. + // + public: + virtual bool isGeneralPurposeRegister(const llvm::Value* val) const override; + + // Instructions. + // + public: + virtual bool isNopInstruction(cs_insn* insn) override; +}; + +} // namespace bin2llvmir +} // namespace retdec + +#endif diff --git a/include/retdec/bin2llvmir/providers/abi/mips.h b/include/retdec/bin2llvmir/providers/abi/mips.h index af144ea6c..a573f6998 100644 --- a/include/retdec/bin2llvmir/providers/abi/mips.h +++ b/include/retdec/bin2llvmir/providers/abi/mips.h @@ -23,7 +23,7 @@ class AbiMips : public Abi // Registers. // public: - virtual bool isGeneralPurposeRegister(const llvm::Value* val) override; + virtual bool isGeneralPurposeRegister(const llvm::Value* val) const override; // Instructions. // diff --git a/include/retdec/bin2llvmir/providers/abi/mips64.h b/include/retdec/bin2llvmir/providers/abi/mips64.h new file mode 100644 index 000000000..7239227b6 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/abi/mips64.h @@ -0,0 +1,37 @@ +/** + * @file include/retdec/bin2llvmir/providers/abi/mips64.h + * @brief ABI information for MIPS. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_ABI_MIPS64_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_ABI_MIPS64_H + +#include "retdec/bin2llvmir/providers/abi/abi.h" + +namespace retdec { +namespace bin2llvmir { + +class AbiMips64 : public Abi +{ + // Ctors, dtors. + // + public: + AbiMips64(llvm::Module* m, Config* c); + virtual ~AbiMips64(); + + // Registers. + // + public: + virtual bool isGeneralPurposeRegister(const llvm::Value* val) const override; + + // Instructions. + // + public: + virtual bool isNopInstruction(cs_insn* insn) override; +}; + +} // namespace bin2llvmir +} // namespace retdec + +#endif diff --git a/include/retdec/bin2llvmir/providers/abi/ms_x64.h b/include/retdec/bin2llvmir/providers/abi/ms_x64.h new file mode 100644 index 000000000..8c64e5176 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/abi/ms_x64.h @@ -0,0 +1,37 @@ +/** + * @file include/retdec/bin2llvmir/providers/abi/ms_x64.h + * @brief ABI information for x86_64. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_ABI_MS_64_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_ABI_MS_64_H + +#include "retdec/bin2llvmir/providers/abi/abi.h" + +namespace retdec { +namespace bin2llvmir { + +class AbiMS_X64 : public Abi +{ + // Ctors, dtors. + // + public: + AbiMS_X64(llvm::Module* m, Config* c); + virtual ~AbiMS_X64(); + + // Registers. + // + public: + virtual bool isGeneralPurposeRegister(const llvm::Value* val) const override; + + // Instructions. + // + public: + virtual bool isNopInstruction(cs_insn* insn) override; +}; + +} // namespace bin2llvmir +} // namespace retdec + +#endif diff --git a/include/retdec/bin2llvmir/providers/abi/pic32.h b/include/retdec/bin2llvmir/providers/abi/pic32.h new file mode 100644 index 000000000..e22863a33 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/abi/pic32.h @@ -0,0 +1,43 @@ +/** + * @file include/retdec/bin2llvmir/providers/abi/pic32.h + * @brief ABI information for MIPS. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_ABI_PIC32_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_ABI_PIC32_H + +#include "retdec/bin2llvmir/providers/abi/abi.h" + +namespace retdec { +namespace bin2llvmir { + +class AbiPic32 : public Abi +{ + // Ctors, dtors. + // + public: + AbiPic32(llvm::Module* m, Config* c); + virtual ~AbiPic32(); + + // Types + // + public: + virtual std::size_t getTypeByteSize(llvm::Type* t) const override; + virtual std::size_t getTypeBitSize(llvm::Type* t) const override; + + // Registers. + // + public: + virtual bool isGeneralPurposeRegister(const llvm::Value* val) const override; + + // Instructions. + // + public: + virtual bool isNopInstruction(cs_insn* insn) override; +}; + +} // namespace bin2llvmir +} // namespace retdec + +#endif diff --git a/include/retdec/bin2llvmir/providers/abi/powerpc.h b/include/retdec/bin2llvmir/providers/abi/powerpc.h index 655087d17..0cf1df36d 100644 --- a/include/retdec/bin2llvmir/providers/abi/powerpc.h +++ b/include/retdec/bin2llvmir/providers/abi/powerpc.h @@ -23,7 +23,7 @@ class AbiPowerpc : public Abi // Registers. // public: - virtual bool isGeneralPurposeRegister(const llvm::Value* val) override; + virtual bool isGeneralPurposeRegister(const llvm::Value* val) const override; // Instructions. // diff --git a/include/retdec/bin2llvmir/providers/abi/powerpc64.h b/include/retdec/bin2llvmir/providers/abi/powerpc64.h new file mode 100644 index 000000000..1ad5b49d1 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/abi/powerpc64.h @@ -0,0 +1,37 @@ +/** + * @file include/retdec/bin2llvmir/providers/abi/powerpc64.h + * @brief ABI information for PowerPC 64. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_ABI_POWERPC64_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_ABI_POWERPC64_H + +#include "retdec/bin2llvmir/providers/abi/abi.h" + +namespace retdec { +namespace bin2llvmir { + +class AbiPowerpc64 : public Abi +{ + // Ctors, dtors. + // + public: + AbiPowerpc64(llvm::Module* m, Config* c); + virtual ~AbiPowerpc64(); + + // Registers. + // + public: + virtual bool isGeneralPurposeRegister(const llvm::Value* val) const override; + + // Instructions. + // + public: + virtual bool isNopInstruction(cs_insn* insn) override; +}; + +} // namespace bin2llvmir +} // namespace retdec + +#endif diff --git a/include/retdec/bin2llvmir/providers/abi/x64.h b/include/retdec/bin2llvmir/providers/abi/x64.h new file mode 100644 index 000000000..2b5a1b34e --- /dev/null +++ b/include/retdec/bin2llvmir/providers/abi/x64.h @@ -0,0 +1,37 @@ +/** + * @file include/retdec/bin2llvmir/providers/abi/x64.h + * @brief ABI information for x86_64. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_ABI_X64_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_ABI_X64_H + +#include "retdec/bin2llvmir/providers/abi/abi.h" + +namespace retdec { +namespace bin2llvmir { + +class AbiX64 : public Abi +{ + // Ctors, dtors. + // + public: + AbiX64(llvm::Module* m, Config* c); + virtual ~AbiX64(); + + // Registers. + // + public: + virtual bool isGeneralPurposeRegister(const llvm::Value* val) const override; + + // Instructions. + // + public: + virtual bool isNopInstruction(cs_insn* insn) override; +}; + +} // namespace bin2llvmir +} // namespace retdec + +#endif diff --git a/include/retdec/bin2llvmir/providers/abi/x86.h b/include/retdec/bin2llvmir/providers/abi/x86.h index 2a135274a..d9cd25bf1 100644 --- a/include/retdec/bin2llvmir/providers/abi/x86.h +++ b/include/retdec/bin2llvmir/providers/abi/x86.h @@ -23,12 +23,18 @@ class AbiX86 : public Abi // Registers. // public: - virtual bool isGeneralPurposeRegister(const llvm::Value* val) override; + virtual bool isGeneralPurposeRegister(const llvm::Value* val) const override; // Instructions. // public: virtual bool isNopInstruction(cs_insn* insn) override; + + + // Calling conventions. + // + private: + CallingConvention::ID fetchDefaultCC() const; }; } // namespace bin2llvmir diff --git a/include/retdec/bin2llvmir/providers/calling_convention/arm/arm_conv.h b/include/retdec/bin2llvmir/providers/calling_convention/arm/arm_conv.h new file mode 100644 index 000000000..b72f8112f --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/arm/arm_conv.h @@ -0,0 +1,33 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/arm/arm_conv.h + * @brief Calling conventions of ARM architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_ARM_CONV_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_ARM_CONV_H + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class ArmCallingConvention: public CallingConvention +{ + // Ctors, dtors. + // + public: + ArmCallingConvention(const Abi* a); + virtual ~ArmCallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); + +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/arm64/arm64_conv.h b/include/retdec/bin2llvmir/providers/calling_convention/arm64/arm64_conv.h new file mode 100644 index 000000000..ee9bf298c --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/arm64/arm64_conv.h @@ -0,0 +1,33 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/arm64/arm64_conv.h + * @brief Calling conventions of ARM64 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_ARM64_CONV_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_ARM64_CONV_H + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class Arm64CallingConvention: public CallingConvention +{ + // Ctors, dtors. + // + public: + Arm64CallingConvention(const Abi* a); + virtual ~Arm64CallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); + +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/calling_convention.h b/include/retdec/bin2llvmir/providers/calling_convention/calling_convention.h new file mode 100644 index 000000000..a4b7e9f30 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/calling_convention.h @@ -0,0 +1,154 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/calling_convention.h + * @brief Calling convention information. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_CALL_CONV_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_CALL_CONV_H + +#include + +#include + +#include "retdec/config/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class Abi; + +class CallingConvention +{ + // Typedefs. + // + public: + typedef std::unique_ptr Ptr; + typedef retdec::config::CallingConventionID ID; + + typedef Ptr (*ConstructorMethod)(const Abi*); + + // Constants. + // + public: + static const bool RTL; + static const bool LTR; + + // Ctors, dtors. + // + public: + CallingConvention(const Abi* abi); + virtual ~CallingConvention(); + + // Registers. + // + public: + const std::vector& getParamRegisters() const; + const std::vector& getParamFPRegisters() const; + const std::vector& getParamDoubleRegisters() const; + const std::vector& getParamVectorRegisters() const; + + const std::vector& getReturnRegisters() const; + const std::vector& getReturnFPRegisters() const; + const std::vector& getReturnDoubleRegisters() const; + const std::vector& getReturnVectorRegisters() const; + + bool usesFPRegistersForParameters() const; + + std::size_t getMaxNumOfRegsPerParam() const; + std::size_t getMaxNumOfFPRegsPerParam() const; + std::size_t getMaxNumOfDoubleRegsPerParam() const; + std::size_t getMaxNumOfVectorRegsPerParam() const; + + std::size_t getMaxNumOfRegsPerReturn() const; + std::size_t getMaxNumOfFPRegsPerReturn() const; + std::size_t getMaxNumOfDoubleRegsPerReturn() const; + std::size_t getMaxNumOfVectorRegsPerReturn() const; + + // Stacks. + public: + bool getStackParamOrder() const; + bool usesStackForParameters() const; + bool passesLargeObjectsByReference() const; + bool respectsRegisterCouples() const; + + virtual std::size_t getMaxBytesPerStackParam() const; + + // Values. + public: + virtual bool valueCanBeParameter(const llvm::Value* val) const; + virtual bool canHoldReturnValue(const llvm::Value* val) const; + + // Private data - misc. + // + protected: + const Abi* _abi; + CallingConvention::ID _ccType; + + // Private data - registers. + // + protected: + std::vector _paramRegs {}; + std::vector _paramFPRegs {}; + std::vector _paramDoubleRegs {}; + std::vector _paramVectorRegs {}; + + std::vector _returnRegs {}; + std::vector _returnFPRegs {}; + std::vector _returnDoubleRegs {}; + std::vector _returnVectorRegs {}; + + // Private data - registers informational. + // + protected: + size_t _numOfRegsPerParam = 1; + size_t _numOfFPRegsPerParam = 1; + size_t _numOfDoubleRegsPerParam = 1; + size_t _numOfVectorRegsPerParam = 1; + + size_t _numOfRegsPerReturn = 1; + size_t _numOfFPRegsPerReturn = 1; + size_t _numOfDoubleRegsPerReturn = 1; + size_t _numOfVectorRegsPerReturn = 1; + + // Private data - stacks informational. + // + protected: + bool _stackParamOrder = RTL; + bool _largeObjectsPassedByReference = false; + bool _respectsRegCouples = false; +}; + +class CallingConventionProvider +{ + // Private constructor. + // + private: + CallingConventionProvider(); + + // Destructor, singleton method. + public: + ~CallingConventionProvider(); + static CallingConventionProvider* getProvider(); + + // Factory methods. + public: + void registerCC( + const CallingConvention::ID& cc, + const CallingConvention::ConstructorMethod& con); + + CallingConvention::Ptr createCallingConvention( + const CallingConvention::ID& cc, + const Abi* a) const; + + // Private data - constrctor methods. + private: + std::vector _id2cc; + +}; + +} // namespace bin2llvmir +} // namespace retdec + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/mips/mips_conv.h b/include/retdec/bin2llvmir/providers/calling_convention/mips/mips_conv.h new file mode 100644 index 000000000..0af41dadf --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/mips/mips_conv.h @@ -0,0 +1,33 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/mips/mips_conv.h + * @brief Calling convention of MIPS architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_MIPS_CONV_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_MIPS_CONV_H + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class MipsCallingConvention: public CallingConvention +{ + // Ctors, dtors. + // + public: + MipsCallingConvention(const Abi* a); + virtual ~MipsCallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); + +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/mips/mips_psp.h b/include/retdec/bin2llvmir/providers/calling_convention/mips/mips_psp.h new file mode 100644 index 000000000..8b1eacb13 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/mips/mips_psp.h @@ -0,0 +1,27 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/mips/mips_psp.h + * @brief Calling convention of MIPS architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_MIPS_MIPS_PSP_CONV_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_MIPS_MIPS_PSP_CONV_H + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class MipsPSPCallingConvention: public CallingConvention +{ + // Ctors, dtors. + // + public: + MipsPSPCallingConvention(const Abi* a); + virtual ~MipsPSPCallingConvention(); +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/mips64/mips64_conv.h b/include/retdec/bin2llvmir/providers/calling_convention/mips64/mips64_conv.h new file mode 100644 index 000000000..2ade02f52 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/mips64/mips64_conv.h @@ -0,0 +1,33 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/mips64/mips64_conv.h + * @brief Calling convention of Mips64 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_MIPS64_MIPS64_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_MIPS64_MIPS64_H + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class Mips64CallingConvention: public CallingConvention +{ + // Ctors, dtors. + // + public: + Mips64CallingConvention(const Abi* a); + virtual ~Mips64CallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); + +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/pic32/pic32_conv.h b/include/retdec/bin2llvmir/providers/calling_convention/pic32/pic32_conv.h new file mode 100644 index 000000000..3af3180f6 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/pic32/pic32_conv.h @@ -0,0 +1,33 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/pic32/pic32_conv.h + * @brief Calling conventions of PIC32 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_PIC32_PIC32_CONV_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_PIC32_PIC32_CONV_H + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class Pic32CallingConvention: public CallingConvention +{ + // Ctors, dtors. + // + public: + Pic32CallingConvention(const Abi* a); + virtual ~Pic32CallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); + +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/powerpc/powerpc_conv.h b/include/retdec/bin2llvmir/providers/calling_convention/powerpc/powerpc_conv.h new file mode 100644 index 000000000..ac2b11eca --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/powerpc/powerpc_conv.h @@ -0,0 +1,33 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/powerpc/powerpc_conv.h + * @brief Calling conventions of PowerPC architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_POWERPC_POWERPC_CONV_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_POWERPC_POWERPC_CONV_H + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class PowerPCCallingConvention: public CallingConvention +{ + // Ctors, dtors. + // + public: + PowerPCCallingConvention(const Abi* a); + virtual ~PowerPCCallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); + +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/powerpc64/powerpc64_conv.h b/include/retdec/bin2llvmir/providers/calling_convention/powerpc64/powerpc64_conv.h new file mode 100644 index 000000000..e8fd47637 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/powerpc64/powerpc64_conv.h @@ -0,0 +1,33 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/powerpc64/powerpc64_conv.h + * @brief Calling conventions of PowerPC64 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_PPC64_PPC64_CONV_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_PPC64_PPC64_CONV_H + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class PowerPC64CallingConvention: public CallingConvention +{ + // Ctors, dtors. + // + public: + PowerPC64CallingConvention(const Abi* a); + virtual ~PowerPC64CallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); + +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/x64/x64_conv.h b/include/retdec/bin2llvmir/providers/calling_convention/x64/x64_conv.h new file mode 100644 index 000000000..0ca84ac24 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/x64/x64_conv.h @@ -0,0 +1,26 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/x64/x64_conv.h + * @brief Calling convention of X64 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X64_X64_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X64_X64_H + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class X64CallingConvention : public CallingConvention +{ + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/x64/x64_microsoft.h b/include/retdec/bin2llvmir/providers/calling_convention/x64/x64_microsoft.h new file mode 100644 index 000000000..bf8cdbb64 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/x64/x64_microsoft.h @@ -0,0 +1,27 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/x64/x64_microsoft.h + * @brief MS Windows calling convention of X64 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X64_X64_MICROSOFT_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X64_X64_MICROSOFT_H + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class MicrosoftX64CallingConvention : public CallingConvention +{ + // Ctors, dtors. + // + public: + MicrosoftX64CallingConvention(const Abi* a); + virtual ~MicrosoftX64CallingConvention(); +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/x64/x64_systemv.h b/include/retdec/bin2llvmir/providers/calling_convention/x64/x64_systemv.h new file mode 100644 index 000000000..9e7ecaf98 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/x64/x64_systemv.h @@ -0,0 +1,27 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/x64/x64_systemv.h + * @brief System V calling convention of X64 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X64_X64_SYSV_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X64_X64_SYSV_H + +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class SystemVX64CallingConvention : public CallingConvention +{ + // Ctors, dtors. + // + public: + SystemVX64CallingConvention(const Abi* a); + virtual ~SystemVX64CallingConvention(); +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_cdecl.h b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_cdecl.h new file mode 100644 index 000000000..28c6fe708 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_cdecl.h @@ -0,0 +1,32 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/x86/x86_cdecl.h + * @brief Cdecl calling convention of x86 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_CDECL_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_CDECL_H + +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_conv.h" + +namespace retdec { +namespace bin2llvmir { + +class CdeclCallingConvention: public X86CallingConvention +{ + // Ctors, dtors. + // + public: + CdeclCallingConvention(const Abi* a); + virtual ~CdeclCallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_conv.h b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_conv.h new file mode 100644 index 000000000..2f683b70c --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_conv.h @@ -0,0 +1,31 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/x86/x86_conv.h + * @brief Common calling convention of x86 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_CONV_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_CONV_H + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" + +namespace retdec { +namespace bin2llvmir { + +class X86CallingConvention: public CallingConvention +{ + // Ctors. + public: + X86CallingConvention(const Abi* a); + + // Stacks. + // + public: + virtual std::size_t getMaxBytesPerStackParam() const override; +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_fastcall.h b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_fastcall.h new file mode 100644 index 000000000..6030da656 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_fastcall.h @@ -0,0 +1,41 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/x86/x86_fastcall.h + * @brief Fastcall calling convention of x86 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_FASTCALL_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_FASTCALL_H + +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_conv.h" + +namespace retdec { +namespace bin2llvmir { + +class FastcallCallingConvention: public X86CallingConvention +{ + // Ctors, dtors. + // + public: + FastcallCallingConvention(const Abi* a); + virtual ~FastcallCallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); +}; + +class PascalFastcallCallingConvention: public X86CallingConvention +{ + // Ctors, dtors. + // + public: + PascalFastcallCallingConvention(const Abi* a); + virtual ~PascalFastcallCallingConvention(); +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_pascal.h b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_pascal.h new file mode 100644 index 000000000..626411097 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_pascal.h @@ -0,0 +1,32 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/x86/x86_pascal.h + * @brief Pascal calling convention of x86 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_PASCAL_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_PASCAL_H + +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_conv.h" + +namespace retdec { +namespace bin2llvmir { + +class PascalCallingConvention: public X86CallingConvention +{ + // Ctors, dtors. + // + public: + PascalCallingConvention(const Abi* a); + virtual ~PascalCallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_thiscall.h b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_thiscall.h new file mode 100644 index 000000000..96d2a91c6 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_thiscall.h @@ -0,0 +1,32 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/x86/x86_thiscall.h + * @brief Thiscall calling convention of x86 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_THISCALL_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_THISCALL_H + +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_conv.h" + +namespace retdec { +namespace bin2llvmir { + +class ThiscallCallingConvention: public X86CallingConvention +{ + // Ctors, dtors. + // + public: + ThiscallCallingConvention(const Abi* a); + virtual ~ThiscallCallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); +}; + +} +} + +#endif diff --git a/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_watcom.h b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_watcom.h new file mode 100644 index 000000000..97ca05f60 --- /dev/null +++ b/include/retdec/bin2llvmir/providers/calling_convention/x86/x86_watcom.h @@ -0,0 +1,32 @@ +/** + * @file include/retdec/bin2llvmir/providers/calling_convention/x86/x86_watcom.h + * @brief Common calling convention of x86 architecture. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#ifndef RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_WATCOM_H +#define RETDEC_BIN2LLVMIR_PROVIDERS_CALL_CONV_X86_X86_WATCOM_H + +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_conv.h" + +namespace retdec { +namespace bin2llvmir { + +class WatcomCallingConvention: public X86CallingConvention +{ + // Ctors, dtors. + // + public: + WatcomCallingConvention(const Abi* a); + virtual ~WatcomCallingConvention(); + + // Construcor method. + // + public: + static CallingConvention::Ptr create(const Abi* a); +}; + +} +} + +#endif diff --git a/include/retdec/config/architecture.h b/include/retdec/config/architecture.h index af6579194..28470d953 100644 --- a/include/retdec/config/architecture.h +++ b/include/retdec/config/architecture.h @@ -25,9 +25,11 @@ class Architecture bool isUnknown() const; bool isKnown() const; bool isMips() const; + bool isMips64() const; bool isPic32() const; bool isMipsOrPic32() const; bool isArm() const; + bool isArm64() const; bool isThumb() const; bool isArmOrThumb() const; bool isX86() const; @@ -35,6 +37,7 @@ class Architecture bool isX86_32() const; bool isX86_64() const; bool isPpc() const; + bool isPpc64() const; bool isEndianLittle() const; bool isEndianBig() const; bool isEndianKnown() const; diff --git a/include/retdec/config/calling_convention.h b/include/retdec/config/calling_convention.h index af90e95f8..9743a9826 100644 --- a/include/retdec/config/calling_convention.h +++ b/include/retdec/config/calling_convention.h @@ -81,7 +81,7 @@ class CallingConvention Json::Value getJsonValue() const; void readJsonValue(const Json::Value& val); - private: + public: enum class eCallingConvention { CC_UNKNOWN = 0, @@ -96,14 +96,32 @@ class CallingConvention CC_SPOILED, CC_SPECIALE, CC_SPECIALP, - CC_SPECIAL + CC_SPECIAL, + CC_WATCOM, + CC_X64, + CC_ARM, + CC_ARM64, + CC_MIPS, + CC_MIPS64, + CC_POWERPC, + CC_POWERPC64, + CC_PIC32, + CC_ENDING, }; - eCallingConvention _callingConvention = eCallingConvention::CC_UNKNOWN; + + eCallingConvention getID() const; + + friend std::ostream& operator<< (std::ostream &out, const eCallingConvention& cc); private: CallingConvention(eCallingConvention cc); + + private: + eCallingConvention _callingConvention = eCallingConvention::CC_UNKNOWN; }; +typedef CallingConvention::eCallingConvention CallingConventionID; + } // namespace config } // namespace retdec diff --git a/scripts/retdec-decompiler.py b/scripts/retdec-decompiler.py index b03bfd7ef..17f3feaf8 100644 --- a/scripts/retdec-decompiler.py +++ b/scripts/retdec-decompiler.py @@ -38,8 +38,8 @@ def parse_args(args): parser.add_argument('-a', '--arch', dest='arch', metavar='ARCH', - choices=['mips', 'pic32', 'arm', 'thumb', 'powerpc', 'x86'], - help='Specify target architecture [mips|pic32|arm|thumb|powerpc|x86].' + choices=['mips', 'pic32', 'arm', 'thumb', 'powerpc', 'x86', 'x86-64'], + help='Specify target architecture [mips|pic32|arm|thumb|powerpc|x86|x86-64].' ' Required if it cannot be autodetected from the input (e.g. raw mode, Intel HEX).') parser.add_argument('-e', '--endian', @@ -907,7 +907,7 @@ def decompile(self): # Check whether the correct target architecture was specified. if self.arch in ['arm', 'thumb']: ords_dir = config.ARM_ORDS_DIR - elif self.arch in ['x86']: + elif self.arch in ['x86', 'x86-64']: ords_dir = config.X86_ORDS_DIR elif self.arch in ['powerpc', 'mips', 'pic32']: pass @@ -917,23 +917,35 @@ def decompile(self): self._cleanup() utils.print_error('Unsupported target architecture \'%s\'. Supported architectures: ' - 'Intel x86, ARM, ARM + Thumb, MIPS, PIC32, PowerPC.' % self.arch) + 'Intel x86, Intel x86-64, ARM, ARM + Thumb, MIPS, PIC32, PowerPC.' % self.arch) return 1 # Check file class (e.g. 'ELF32', 'ELF64'). At present, we can only decompile 32-bit files. # Note: we prefer to report the 'unsupported architecture' error (above) than this 'generic' error. fileclass, _, _ = CmdRunner.run_cmd([config.CONFIGTOOL, self.config_file, '--read', '--file-class'], buffer_output=True) - if fileclass not in ['16', '32']: + if fileclass not in ['16', '32', '64']: if self.args.generate_log: self._generate_log() self._cleanup() utils.print_error( - 'Unsupported target format \'%s%s\'. Supported formats: ELF32, PE32, Intel HEX 32, Mach-O 32.' % ( + 'Unsupported target format \'%s%s\'. Supported formats: ELF32, ELF64, PE32, Intel HEX 32, Mach-O 32.' % ( self.format.upper(), fileclass)) return 1 + # TODO this should be somehow connected somewhere else + if fileclass == '64' and self.arch in ['arm', 'mips', 'pic32', 'powerpc', 'x86']: + if self.args.generate_log: + self.generate_log() + + self._cleanup() + utils.print_error( + 'Unsupported target format and architecture combination: \'%s%s\' + \'%s\'.' % ( + self.format.upper(), fileclass, self.arch)) + + return 1 + # Set path to statically linked code signatures. # # TODO: Using ELF for IHEX is ok, but for raw, we probably should somehow decide between ELF and PE, @@ -961,6 +973,8 @@ def decompile(self): if sig_arch == 'pic32': sig_arch = 'mips' + elif sig_arch == 'x86-64': + sig_arch = 'x86'; signatures_dir = os.path.join(config.GENERIC_SIGNATURES_DIR, sig_format, fileclass, sig_endian, sig_arch) diff --git a/src/bin2llvmir/CMakeLists.txt b/src/bin2llvmir/CMakeLists.txt index f71322fdb..d1dd647fa 100644 --- a/src/bin2llvmir/CMakeLists.txt +++ b/src/bin2llvmir/CMakeLists.txt @@ -48,7 +48,12 @@ set(BIN2LLVMIR_SOURCES optimizations/inst_opt/inst_opt.cpp optimizations/local_vars/local_vars.cpp optimizations/main_detection/main_detection.cpp + optimizations/param_return/collector/collector.cpp + optimizations/param_return/collector/pic32.cpp + optimizations/param_return/filter/filter.cpp + optimizations/param_return/filter/ms_x64.cpp optimizations/param_return/param_return.cpp + optimizations/param_return/data_entries.cpp optimizations/phi2seq/phi2seq.cpp optimizations/provider_init/provider_init.cpp optimizations/x86_addr_spaces/x86_addr_spaces_pass.cpp @@ -66,9 +71,33 @@ set(BIN2LLVMIR_SOURCES optimizations/unreachable_funcs/unreachable_funcs.cpp providers/abi/abi.cpp providers/abi/arm.cpp + providers/abi/arm64.cpp providers/abi/mips.cpp + providers/abi/mips64.cpp + providers/abi/ms_x64.cpp + providers/abi/pic32.cpp providers/abi/powerpc.cpp + providers/abi/powerpc64.cpp + providers/abi/x64.cpp providers/abi/x86.cpp + providers/calling_convention/calling_convention.cpp + providers/calling_convention/arm/arm_conv.cpp + providers/calling_convention/arm64/arm64_conv.cpp + providers/calling_convention/mips/mips_conv.cpp + providers/calling_convention/mips/mips_psp.cpp + providers/calling_convention/mips64/mips64_conv.cpp + providers/calling_convention/pic32/pic32_conv.cpp + providers/calling_convention/powerpc/powerpc_conv.cpp + providers/calling_convention/powerpc64/powerpc64_conv.cpp + providers/calling_convention/x64/x64_conv.cpp + providers/calling_convention/x64/x64_microsoft.cpp + providers/calling_convention/x64/x64_systemv.cpp + providers/calling_convention/x86/x86_cdecl.cpp + providers/calling_convention/x86/x86_conv.cpp + providers/calling_convention/x86/x86_fastcall.cpp + providers/calling_convention/x86/x86_pascal.cpp + providers/calling_convention/x86/x86_thiscall.cpp + providers/calling_convention/x86/x86_watcom.cpp providers/asm_instruction.cpp providers/config.cpp providers/debugformat.cpp diff --git a/src/bin2llvmir/optimizations/decoder/decoder.cpp b/src/bin2llvmir/optimizations/decoder/decoder.cpp index f24293855..e892048d0 100644 --- a/src/bin2llvmir/optimizations/decoder/decoder.cpp +++ b/src/bin2llvmir/optimizations/decoder/decoder.cpp @@ -781,8 +781,8 @@ bool Decoder::getJumpTargetsFromInstruction( SymbolicTree st(_RDA, l->getPointerOperand(), nullptr, 8); st.simplifyNode(); - ConstantInt* ci = nullptr; - if (match(st, m_ConstantInt(ci))) + auto* ci = dyn_cast(st.value); + if (ci && !ci->isNegative()) { Address t(ci->getZExtValue()); auto sz = _abi->getTypeByteSize(l->getType()); diff --git a/src/bin2llvmir/optimizations/param_return/collector/collector.cpp b/src/bin2llvmir/optimizations/param_return/collector/collector.cpp new file mode 100644 index 000000000..86f642fcd --- /dev/null +++ b/src/bin2llvmir/optimizations/param_return/collector/collector.cpp @@ -0,0 +1,626 @@ +/** +* @file src/bin2llvmir/optimizations/param_return/collector/collector.cpp +* @brief Collects possible arguments and returns of functions. +* @copyright (c) 2019 Avast Software, licensed under the MIT license +*/ + +#include + +#include +#include + +#include "retdec/bin2llvmir/optimizations/param_return/collector/collector.h" +#include "retdec/bin2llvmir/optimizations/param_return/collector/pic32.h" + +using namespace retdec::utils; +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +Collector::Collector( + const Abi* abi, + Module* m, + const ReachingDefinitionsAnalysis* rda) : + _abi(abi), + _module(m), + _rda(rda) +{ +} + +Collector::~Collector() +{ +} + +void Collector::collectCallArgs(CallEntry* ce) const +{ + std::vector foundStores; + + collectStoresBeforeInstruction( + ce->getCallInstruction(), + foundStores); + + ce->setArgStores(std::move(foundStores)); +} + +void Collector::collectCallRets(CallEntry* ce) const +{ + std::vector foundLoads; + + collectLoadsAfterInstruction( + ce->getCallInstruction(), + foundLoads); + + ce->setRetLoads(std::move(foundLoads)); +} + +void Collector::collectDefArgs(DataFlowEntry* dataflow) const +{ + if (!dataflow->hasDefinition()) + { + return; + } + + auto* f = dataflow->getFunction(); + + std::set added; + for (auto it = inst_begin(f), end = inst_end(f); it != end; ++it) + { + if (auto* l = dyn_cast(&*it)) + { + auto* ptr = l->getPointerOperand(); + if (!_abi->isGeneralPurposeRegister(ptr) && !_abi->isStackVariable(ptr)) + { + continue; + } + + auto* use = _rda->getUse(l); + if (use == nullptr) + { + continue; + } + + if ((use->defs.empty() || use->isUndef()) + && added.find(ptr) == added.end()) + { + dataflow->addArg(ptr); + added.insert(ptr); + } + } + } +} + +void Collector::collectDefRets(DataFlowEntry* dataflow) const +{ + if (!dataflow->hasDefinition()) + { + return; + } + + auto* f = dataflow->getFunction(); + + for (auto it = inst_begin(f), end = inst_end(f); it != end; ++it) + { + if (auto* r = dyn_cast(&*it)) + { + ReturnEntry* re = dataflow->createRetEntry(r); + collectRetStores(re); + } + } +} + +void Collector::collectRetStores(ReturnEntry* re) const +{ + std::vector foundStores; + +// TODO: +// This method should be used only after +// speed comparation of below methods. +// +// In this implementation of parameter +// analysis return type is estimated +// only as last option from colelcted +// values. This iss reason why quicklier +// but not reliable method is used +// instead of more reliable one. +// +// collectStoresBeforeInstruction( +// re->getRetInstruction(), +// foundStores); + + collectStoresInSinglePredecessors( + re->getRetInstruction(), + foundStores); + + re->setRetStores(std::move(foundStores)); +} + +void Collector::collectStoresBeforeInstruction( + llvm::Instruction* i, + std::vector& stores) const +{ + if (i == nullptr) + { + return; + } + + std::map> seenBlocks; + + auto* block = i->getParent(); + + // In case of recursive call of same basic block. + std::set afterValues; + std::vector afterStores; + collectStoresInInstructionBlock( + &block->back(), + afterValues, + afterStores); + + seenBlocks[block] = std::move(afterValues); + + collectStoresRecursively(i->getPrevNode(), stores, seenBlocks); + + auto& values = seenBlocks[block]; + + stores.insert( + stores.end(), + std::make_move_iterator(afterStores.begin()), + std::make_move_iterator(afterStores.end())); + + stores.erase( + std::remove_if( + stores.begin(), + stores.end(), + [values](StoreInst* s) + { + return values.find( + s->getPointerOperand()) == values.end(); + }), + stores.end()); +} + +void Collector::collectStoresInSinglePredecessors( + llvm::Instruction* i, + std::vector& stores) const +{ + if (i == nullptr) + { + return; + } + + std::set seenBbs; + std::set disqualifiedValues; + + auto* b = i->getParent(); + seenBbs.insert(b); + Instruction* prev = i; + + while (true) + { + if (prev == &b->front()) + { + auto* spb = b->getSinglePredecessor(); + if (spb && !spb->empty() + && seenBbs.find(spb) == seenBbs.end()) + { + b = spb; + prev = &b->back(); + seenBbs.insert(b); + } + else + { + break; + } + } + else + { + prev = prev->getPrevNode(); + } + if (prev == nullptr) + { + break; + } + + if (isa(prev) || isa(prev)) + { + break; + } + else if (auto* store = dyn_cast(prev)) + { + auto* ptr = store->getPointerOperand(); + + if (disqualifiedValues.find(ptr) == disqualifiedValues.end() + && (_abi->isRegister(ptr) || _abi->isStackVariable(ptr))) + { + stores.push_back(store); + disqualifiedValues.insert(ptr); + } + } + else if (auto* load = dyn_cast(prev)) + { + auto* ptr = load->getPointerOperand(); + disqualifiedValues.insert(ptr); + } + } +} + +void Collector::collectStoresRecursively( + Instruction* i, + std::vector& stores, + std::map>& seen) const +{ + if (i == nullptr) + { + return; + } + + auto* block = i->getParent(); + + std::set values; + if (!collectStoresInInstructionBlock(i, values, stores)) + { + seen[block] = std::move(values); + return; + } + + seen.emplace(std::make_pair(block, values)); + std::set commonValues; + + for (BasicBlock* pred : predecessors(block)) + { + if (seen.find(pred) == seen.end()) + { + collectStoresRecursively( + &pred->back(), + stores, + seen); + } + + auto& foundValues = seen[pred]; + if (foundValues.empty()) + { + // Shorcut -> intersection would be empty set. + commonValues.clear(); + break; + } + + if (commonValues.empty()) + { + commonValues = foundValues; + } + else + { + std::set intersection; + std::set_intersection( + commonValues.begin(), + commonValues.end(), + foundValues.begin(), + foundValues.end(), + std::inserter(intersection, intersection.begin())); + + commonValues = std::move(intersection); + } + } + + values.insert(commonValues.begin(), commonValues.end()); + seen[block] = values; +} + + +bool Collector::collectStoresInInstructionBlock( + Instruction* start, + std::set& values, + std::vector& stores) const +{ + if (start == nullptr) + { + return false; + } + + std::set excluded; + + auto* block = start->getParent(); + + for (auto* inst = start; true; inst = inst->getPrevNode()) + { + if (inst == nullptr) + { + return false; + } + if (auto* call = dyn_cast(inst)) + { + auto* calledFnc = call->getCalledFunction(); + if (calledFnc == nullptr || !calledFnc->isIntrinsic()) + { + return false; + } + } + else if (isa(inst)) + { + return false; + } + else if (auto* store = dyn_cast(inst)) + { + auto* val = store->getValueOperand(); + auto* ptr = store->getPointerOperand(); + + if (!_abi->isRegister(ptr) && !_abi->isStackVariable(ptr)) + { + excluded.insert(ptr); + } + if (auto* l = dyn_cast(val)) + { + if (l->getPointerOperand() != store->getPointerOperand()) + { + excluded.insert(l->getPointerOperand()); + } + } + + if (excluded.find(ptr) == excluded.end()) + { + stores.push_back(store); + values.insert(ptr); + excluded.insert(ptr); + excluded.insert(val); + } + } + if (inst == &block->front()) + { + return true; + } + } + + return true; +} + +void Collector::collectLoadsAfterInstruction( + llvm::Instruction* start, + std::vector& loads) const +{ + if (start == nullptr) + { + return; + } + + std::queue next; + std::set excludedValues; + std::set seen; + + BasicBlock* beginBB = start->getParent(); + next.push(start->getNextNode()); + + while (!next.empty()) + { + auto* i = next.front(); + next.pop(); + + auto* block = i->getParent(); + seen.insert(block); + + if (collectLoadsAfterInstruction(i, loads, excludedValues)) + { + for (auto suc : successors(block)) + { + if (seen.find(suc) == seen.end()) + { + next.push(&suc->front()); + } + else if (suc == beginBB) + { + next.push(&beginBB->front()); + beginBB = nullptr; + } + } + } + } +} + +bool Collector::collectLoadsAfterInstruction( + llvm::Instruction* start, + std::vector& loads, + std::set& excluded) const +{ + if (start == nullptr) + { + return false; + } + + auto* block = start->getParent(); + for (auto* inst = start; true; inst = inst->getNextNode()) + { + if (inst == nullptr) + { + return false; + } + if (auto* call = dyn_cast(inst)) + { + auto* calledFnc = call->getCalledFunction(); + if (calledFnc == nullptr || !calledFnc->isIntrinsic()) + { + return false; + } + } + else if (isa(inst)) + { + return false; + } + else if (auto* store = dyn_cast(inst)) + { + auto* ptr = store->getPointerOperand(); + excluded.insert(ptr); + } + else if (auto* load = dyn_cast(inst)) + { + auto* ptr = load->getPointerOperand(); + + if (excluded.find(ptr) == excluded.end() + && ( _abi->isGeneralPurposeRegister(ptr) || _abi->isStackVariable(ptr) )) + { + loads.push_back(load); + } + } + + if (inst == &block->back()) + { + return true; + } + } + + return true; +} + +void Collector::collectCallSpecificTypes(CallEntry* ce) const +{ + if (!ce->getBaseFunction()->isVariadic()) + { + return; + } + + + if (!extractFormatString(ce)) + { + return; + } + + auto wrappedCall = ce->getBaseFunction()->getWrappedCall(); + + auto trueCall = wrappedCall ? wrappedCall : ce->getCallInstruction(); + + ce->setArgTypes( + llvm_utils::parseFormatString( + _module, + ce->getFormatString(), + trueCall->getCalledFunction()) + ); +} + +bool Collector::extractFormatString(CallEntry* ce) const +{ + for (auto& i : ce->args()) + { + auto inst = std::find_if( + ce->argStores().begin(), + ce->argStores().end(), + [i](StoreInst *st) + { + return st->getPointerOperand() == i; + }); + + if (inst != ce->argStores().end()) + { + std::string str; + if (storesString(*inst, str)) + { + ce->setFormatString(str); + return true; + } + } + } + + return false; +} + +bool Collector::storesString(StoreInst* si, std::string& str) const +{ + auto* v = getRoot(si->getValueOperand()); + auto* gv = dyn_cast_or_null(v); + + if (gv == nullptr || !gv->hasInitializer()) + { + return false; + } + + auto* init = dyn_cast_or_null(gv->getInitializer()); + if (init == nullptr) + { + if (auto* i = dyn_cast(gv->getInitializer())) + { + if (auto* igv = dyn_cast(i->getOperand(0))) + { + init = dyn_cast_or_null(igv->getInitializer()); + } + } + } + + if (init == nullptr || !init->isString()) + { + return false; + } + + str = init->getAsString(); + return true; +} + +llvm::Value* Collector::getRoot(llvm::Value* i, bool first) const +{ + static std::set seen; + if (first) + { + seen.clear(); + } + if (seen.count(i)) + { + return i; + } + seen.insert(i); + + i = llvm_utils::skipCasts(i); + if (auto* ii = dyn_cast(i)) + { + if (auto* u = _rda->getUse(ii)) + { + if (u->defs.size() == 1) + { + auto* d = (*u->defs.begin())->def; + if (auto* s = dyn_cast(d)) + { + return getRoot(s->getValueOperand(), false); + } + else + { + return d; + } + } + else if (auto* l = dyn_cast(ii)) + { + return getRoot(l->getPointerOperand(), false); + } + else + { + return i; + } + } + else if (auto* l = dyn_cast(ii)) + { + return getRoot(l->getPointerOperand(), false); + } + else + { + return i; + } + } + + return i; +} + +// +//============================================================================= +// CollectorProvider +//============================================================================= +// + +Collector::Ptr CollectorProvider::createCollector( + const Abi* abi, + Module* m, + const ReachingDefinitionsAnalysis* rda) +{ + if (abi->isPic32()) + { + return std::make_unique(abi, m, rda); + } + + return std::make_unique(abi, m, rda); +} + +} +} diff --git a/src/bin2llvmir/optimizations/param_return/collector/pic32.cpp b/src/bin2llvmir/optimizations/param_return/collector/pic32.cpp new file mode 100644 index 000000000..af16b418e --- /dev/null +++ b/src/bin2llvmir/optimizations/param_return/collector/pic32.cpp @@ -0,0 +1,48 @@ +/** +* @file src/bin2llvmir/optimizations/param_return/collector/pic32.cpp +* @brief Pic32 specific collection algorithms. +* @copyright (c) 2019 Avast Software, licensed under the MIT license +*/ + +#include "retdec/bin2llvmir/optimizations/param_return/collector/pic32.h" + +using namespace retdec::utils; +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +CollectorPic32::CollectorPic32( + const Abi* abi, + llvm::Module* m, + const ReachingDefinitionsAnalysis* rda) : + Collector(abi, m, rda) +{ +} + +CollectorPic32::~CollectorPic32() +{ +} + +void CollectorPic32::collectCallSpecificTypes(CallEntry* ce) const +{ + Collector::collectCallSpecificTypes(ce); + + std::vector argTypes; + for (auto t : ce->argTypes()) + { + if (t->isDoubleTy()) + { + argTypes.push_back(Type::getFloatTy(_module->getContext())); + } + else + { + argTypes.push_back(t); + } + } + + ce->setArgTypes(std::move(argTypes)); +} + +} +} diff --git a/src/bin2llvmir/optimizations/param_return/data_entries.cpp b/src/bin2llvmir/optimizations/param_return/data_entries.cpp new file mode 100644 index 000000000..55bf8f935 --- /dev/null +++ b/src/bin2llvmir/optimizations/param_return/data_entries.cpp @@ -0,0 +1,449 @@ +/** +* @file src/bin2llvmir/optimizations/param_return/data_entries.cpp +* @brief Data entries for parameter analysis. +* @copyright (c) 2019 Avast Software, licensed under the MIT license +*/ + +#include + +#include "retdec/bin2llvmir/optimizations/param_return/data_entries.h" + +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +// +//============================================================================= +// ReturnEntry +//============================================================================= +// + +ReturnEntry::ReturnEntry(llvm::ReturnInst* r) : + _retInst(r) +{ +} + +void ReturnEntry::addRetStore(llvm::StoreInst* st) +{ + _retStores.push_back(st); + + if (std::find( + _retValues.begin(), + _retValues.end(), + st->getPointerOperand()) != _retValues.end()) + { + _retValues.push_back(st->getPointerOperand()); + } +} + +void ReturnEntry::setRetStores(std::vector&& stores) +{ + _retStores = std::move(stores); + + std::set vals; + for (auto& i: _retStores) + { + vals.insert(i->getPointerOperand()); + } + + _retValues = std::vector( + std::make_move_iterator(vals.begin()), + std::make_move_iterator(vals.end())); +} + +void ReturnEntry::setRetStores(const std::vector& stores) +{ + _retStores = stores; + + std::set vals; + for (auto& i: _retStores) + { + vals.insert(i->getPointerOperand()); + } + + _retValues = std::vector( + std::make_move_iterator(vals.begin()), + std::make_move_iterator(vals.end())); +} + +void ReturnEntry::setRetValues(std::vector&& values) +{ + _retStores.erase(std::remove_if( + _retStores.begin(), + _retStores.end(), + [values](StoreInst* st) + { + auto* op = st->getPointerOperand(); + return std::find( + values.begin(), + values.end(), op) == values.end(); + }), + _retStores.end()); + + _retValues = std::move(values); +} + +void ReturnEntry::setRetValues(const std::vector& values) +{ + _retStores.erase(std::remove_if( + _retStores.begin(), + _retStores.end(), + [values](StoreInst* st) + { + auto* op = st->getPointerOperand(); + return std::find( + values.begin(), + values.end(), op) == values.end(); + }), + _retStores.end()); + + _retValues = values; +} + +ReturnInst* ReturnEntry::getRetInstruction() const +{ + return _retInst; +} + +const std::vector& ReturnEntry::retStores() const +{ + return _retStores; +} + +const std::vector& ReturnEntry::retValues() const +{ + return _retValues; +} + +// +//============================================================================= +// CallableEntry +//============================================================================= +// + +bool CallableEntry::isVoidarg() const +{ + return _voidarg; +} + +void CallableEntry::addArg(llvm::Value* arg) +{ + _args.push_back(arg); +} + +void CallableEntry::setVoidarg(bool voidarg) +{ + _voidarg = voidarg; +} + +void CallableEntry::setArgTypes( + std::vector&& types, + std::vector&& names) +{ + _argTypes = std::move(types); + _argNames = std::move(names); + + if (_argTypes.size() > _argNames.size()) + { + _argNames.resize(_argTypes.size(), ""); + } + else if (_argTypes.size() < _argNames.size()) + { + _argTypes.resize(_argNames.size(), nullptr); + } + + if (_argTypes.empty()) + { + setVoidarg(); + } +} + +const std::vector& CallableEntry::args() const +{ + return _args; +} + +const std::vector& CallableEntry::argTypes() const +{ + return _argTypes; +} + +const std::vector& CallableEntry::argNames() const +{ + return _argNames; +} + +// +//============================================================================= +// FunctionEntry +//============================================================================= +// + +bool FunctionEntry::isVariadic() const +{ + return _variadic; +} + +bool FunctionEntry::isWrapper() const +{ + return _wrap != nullptr; +} + +void FunctionEntry::addRetEntry(const ReturnEntry& ret) +{ + _retEntries.push_back(ret); +} + +ReturnEntry* FunctionEntry::createRetEntry(llvm::ReturnInst* ret) +{ + _retEntries.push_back(ReturnEntry(ret)); + + return &(_retEntries.back()); +} + +void FunctionEntry::setVariadic(bool variadic) +{ + _variadic = variadic; +} + +void FunctionEntry::setArgs(std::vector&& args) +{ + _args = std::move(args); +} + +void FunctionEntry::setWrappedCall(llvm::CallInst* wrap) +{ + _wrap = wrap; +} + +void FunctionEntry::setRetType(llvm::Type* type) +{ + _retType = type; +} + +void FunctionEntry::setRetValue(llvm::Value* val) +{ + _retVal = val; +} + +void FunctionEntry::setCallingConvention(const CallingConvention::ID& cc) +{ + if (cc == CallingConvention::ID::CC_VOIDARG) + { + setVoidarg(); + } + else + { + _callconv = cc; + } +} + +llvm::Type* FunctionEntry::getRetType() const +{ + return _retType; +} + +llvm::Value* FunctionEntry::getRetValue() const +{ + return _retVal; +} + +llvm::CallInst* FunctionEntry::getWrappedCall() const +{ + return _wrap; +} + +CallingConvention::ID FunctionEntry::getCallingConvention() const +{ + return _callconv; +} + +const std::vector& FunctionEntry::retEntries() const +{ + return _retEntries; +} + +std::vector& FunctionEntry::retEntries() +{ + return _retEntries; +} + +// +//============================================================================= +// CallEntry +//============================================================================= +// + +CallEntry::CallEntry(CallInst* call, const FunctionEntry* base) : + _baseFunction(base), + _callInst(call) +{ +} + +void CallEntry::addRetLoad(LoadInst* load) +{ + _retLoads.push_back(load); + _retValues.push_back(load->getPointerOperand()); + + // TODO duplicity and pointer operand? +} + +void CallEntry::setFormatString(const std::string &fmt) +{ + _fmtStr = fmt; +} + +void CallEntry::setArgStores(std::vector&& stores) +{ + _argStores = std::move(stores); + + std::set vals; + for (auto& i : _argStores) + { + vals.insert(i->getPointerOperand()); + } + + _args.assign( + std::make_move_iterator(vals.begin()), + std::make_move_iterator(vals.end())); +} + +void CallEntry::setArgs(std::vector&& args) +{ + _argStores.erase( + std::remove_if( + _argStores.begin(), + _argStores.end(), + [args](StoreInst* st) + { + auto* op = st->getPointerOperand(); + return std::find( + args.begin(), + args.end(), op) == args.end(); + }), + _argStores.end()); + + _args = std::move(args); +} + +void CallEntry::setRetLoads(std::vector&& loads) +{ + _retLoads = std::move(loads); + + std::set vals; + for (auto& i: _retLoads) + { + vals.insert(i->getPointerOperand()); + } + _retValues = std::vector( + std::make_move_iterator(vals.begin()), + std::make_move_iterator(vals.end())); +} + +void CallEntry::setRetValues(std::vector&& values) +{ + _retLoads.erase(std::remove_if( + _retLoads.begin(), + _retLoads.end(), + [values](llvm::LoadInst* st) + { + auto* op = st->getPointerOperand(); + return std::find( + values.begin(), + values.end(), op) == values.end(); + }), + _retLoads.end()); + + _retValues = std::move(values); +} + +CallInst* CallEntry::getCallInstruction() const +{ + return _callInst; +} + +const FunctionEntry* CallEntry::getBaseFunction() const +{ + return _baseFunction; +} + +std::string CallEntry::getFormatString() const +{ + return _fmtStr; +} + +const std::vector& CallEntry::argStores() const +{ + return _argStores; +} + +const std::vector& CallEntry::retValues() const +{ + return _retValues; +} + +const std::vector& CallEntry::retLoads() const +{ + return _retLoads; +} + +// +//============================================================================= +// DataFlowEntry +//============================================================================= +// + +DataFlowEntry::DataFlowEntry(Value* called): + _calledValue(called) +{ +} + +bool DataFlowEntry::isFunction() const +{ + return getFunction() != nullptr; +} + +bool DataFlowEntry::isValue() const +{ + return _calledValue && !isFunction(); +} + +bool DataFlowEntry::hasDefinition() const +{ + return isFunction() && !getFunction()->empty(); +} + +Function* DataFlowEntry::getFunction() const +{ + return dyn_cast_or_null(_calledValue); +} + +Value* DataFlowEntry::getValue() const +{ + return _calledValue; +} + +void DataFlowEntry::setCalledValue(llvm::Value* called) +{ + _calledValue = called; +} + +CallEntry* DataFlowEntry::createCallEntry(CallInst* call) +{ + _calls.push_back(CallEntry(call, this)); + return &(_calls.back()); +} + +const std::vector& DataFlowEntry::callEntries() const +{ + return _calls; +} + +std::vector& DataFlowEntry::callEntries() +{ + return _calls; +} + +} +} diff --git a/src/bin2llvmir/optimizations/param_return/filter/filter.cpp b/src/bin2llvmir/optimizations/param_return/filter/filter.cpp new file mode 100644 index 000000000..ae6223601 --- /dev/null +++ b/src/bin2llvmir/optimizations/param_return/filter/filter.cpp @@ -0,0 +1,1353 @@ +/** +* @file src/bin2llvmir/optimizations/param_return/filter/filter.cpp +* @brief Filters potentail values according to calling convention. +* @copyright (c) 2019 Avast Software, licensed under the MIT license +*/ + +#include + +#include "retdec/bin2llvmir/optimizations/param_return/filter/filter.h" +#include "retdec/bin2llvmir/optimizations/param_return/filter/ms_x64.h" + +using namespace retdec::utils; +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +// +//============================================================================= +// Filter +//============================================================================= +// + +Filter::Filter( + const Abi* abi, + const CallingConvention* cc) : + _abi(abi), + _cc(cc) +{ +} + +Filter::~Filter() +{ +} + +void Filter::estimateRetValue(DataFlowEntry* de) const +{ + auto retValue = de->getRetValue(); + auto retType = de->getRetType(); + + if (retType == nullptr) + { + if (!de->retEntries().empty() + && !de->retEntries().front().retValues().empty()) + { + retType = de->retEntries().front().retValues().front()->getType(); + if (auto* p = dyn_cast(retType)) + { + retType = p->getElementType(); + } + retValue = de->retEntries().front().retValues().front(); + } + else + { + // This is hack -> retdec expects generation of + // implicit return for every funtion definition + // but not external calls. + // + // In fact this should return void type here. + // This is why retType is not set to any type. + if (!_cc->getReturnRegisters().empty()) + { + retValue = _abi->getRegister(_cc->getReturnRegisters().front()); + } + } + } + else + { +// TODO: double-read-modf.x86.clang-3.2.O0.g.elf +// In test above return type is found from configuration to be +// double but collector finds only stores to EAX which results in failure in +// decompilation. +// +// if (!de->retEntries().empty() +// && !de->retEntries().front().retValues().empty()) +// { +// retValue = de->retEntries().front().retValues().front(); +// } +// else + { + if (!_cc->getReturnRegisters().empty()) + { + retValue = _abi->getRegister(_cc->getReturnRegisters().front()); + } + + if (retType->isFloatingPointTy() && !_cc->getReturnFPRegisters().empty()) + { + retValue = _abi->getRegister(_cc->getReturnFPRegisters().front()); + } + + if (retType->isDoubleTy() && !_cc->getReturnDoubleRegisters().empty()) + { + retValue = _abi->getRegister(_cc->getReturnDoubleRegisters().front()); + } + } + } + + de->setRetType(retType); + de->setRetValue(retValue); +} + +void Filter::filterDefinition(DataFlowEntry* de) const +{ + if (!de->hasDefinition()) + { + return; + } + + FilterableLayout defArgs = createArgsFilterableLayout(de->args(), de->argTypes()); + filterDefinitionArgs(defArgs, de->isVoidarg()); + + de->setArgs(createGroupedArgValues(defArgs)); + + if (de->retEntries().empty()) + { + return; + } + + std::vector defRets; + for (auto& ret : de->retEntries()) + { + defRets.push_back( + createRetsFilterableLayout(ret.retValues(), de->getRetType())); + } + + leaveCommonRets(defRets); + filterRets(defRets.front()); + + FilterableLayout retTempl = defRets.front(); + + for (auto& ret : de->retEntries()) + { + filterRetsByDefLayout(defRets.front(), retTempl); + ret.setRetValues(createGroupedValues(defRets.front())); + + defRets.erase(defRets.begin()); + } +} + +void Filter::filterCalls(DataFlowEntry* de) const +{ + if (de->callEntries().empty()) + { + return; + } + + std::vector callArgs, callArgsCopy; + std::vector callRets; + + for (auto& call : de->callEntries()) + { + callArgs.push_back( + createArgsFilterableLayout(call.args(), de->argTypes())); + callRets.push_back( + createRetsFilterableLayout( + call.retValues(), + de->getRetType())); + } + + callArgsCopy = callArgs; + + FilterableLayout retTempl, argTempl; + + if (!callArgs.empty()) + { + if (!de->isVoidarg() && de->argTypes().empty()) + { + leaveCommonArgs(callArgs); + } + filterCallArgs(callArgs.front(), de->isVoidarg()); + argTempl = callArgs.front(); + } + + if (!callRets.empty()) + { + leaveCommonRets(callRets); + filterRets(callRets.front()); + retTempl = callRets.front(); + } + + if (de->hasDefinition()) + { + FilterableLayout defArgs; + defArgs = createArgsFilterableLayout( + de->args(), + de->argTypes()); + if (!de->isVoidarg() && !de->argTypes().empty()) + { + // This function is called because + // in case when we have info about + // types and order of the parameters + // we would loose it by plain creation + // of template. + // This order is estimated in function + // below. + filterArgsByKnownTypes(defArgs); + } + else if (de->args().empty()) + { + filterCallArgsByDefLayout(defArgs, argTempl); + de->setArgs(createGroupedArgValues(defArgs)); + } + else if (argTempl.stacks.size() > defArgs.stacks.size()) + { + if (argTempl.gpRegisters.size() == defArgs.gpRegisters.size() + && argTempl.fpRegisters.size() == defArgs.fpRegisters.size() + && argTempl.doubleRegisters.size() == defArgs.doubleRegisters.size() + && argTempl.vectorRegisters.size() == defArgs.vectorRegisters.size()) + { + leaveSameStacks(defArgs, argTempl); + de->setArgs(createGroupedArgValues(defArgs)); + } + } + + if (!de->retEntries().empty()) + { + retTempl = createRetsFilterableLayout( + de->retEntries().front().retValues(), + de->getRetType()); + } + + argTempl = std::move(defArgs); + } + + for (auto& call : de->callEntries()) + { + filterCallArgsByDefLayout(callArgsCopy.front(), argTempl); + filterRetsByDefLayout(callRets.front(), retTempl); + + call.setArgs(createGroupedArgValues(callArgsCopy.front())); + call.setRetValues(createGroupedRetValues(callRets.front())); + + callArgsCopy.erase(callArgsCopy.begin()); + callRets.erase(callRets.begin()); + } +} + +void Filter::filterCallsVariadic(DataFlowEntry* de, const Collector* collector) const +{ + if (de->callEntries().empty()) + { + return; + } + + std::vector callArgs; + std::vector callRets; + + for (auto& call : de->callEntries()) + { + auto argTypes = de->argTypes(); + + FilterableLayout argLayout = createArgsFilterableLayout(call.args(), {}); + + // To collect specific types, we need ordered values. + // Collector will find first occourence of string and parse it. + call.setArgs(createGroupedArgValues(argLayout)); + collector->collectCallSpecificTypes(&call); + + argTypes.insert( + argTypes.end(), + call.argTypes().begin(), + call.argTypes().end()); + + argLayout.knownTypes = std::move(argTypes); + + callArgs.push_back(argLayout); + callRets.push_back( + createRetsFilterableLayout( + call.retValues(), + call.getBaseFunction()->getRetType())); + } + + FilterableLayout retTempl; + + if (de->hasDefinition() && !de->retEntries().empty()) + { + retTempl = createRetsFilterableLayout( + de->retEntries().front().retValues(), + de->getRetType()); + } + else if (!callRets.empty()) + { + leaveCommonRets(callRets); + filterRets(callRets.front()); + retTempl = callRets.front(); + } + + for (auto& call : de->callEntries()) + { + filterCallArgs(callArgs.front(), de->isVoidarg() && !call.argTypes().empty()); + filterRetsByDefLayout(callRets.front(), retTempl); + + call.setArgs(createGroupedArgValues(callArgs.front())); + call.setRetValues(createGroupedRetValues(callRets.front())); + + callArgs.erase(callArgs.begin()); + callRets.erase(callRets.begin()); + } +} + +void Filter::filterDefinitionArgs(FilterableLayout& args, bool isVoidarg) const +{ + leaveOnlyPositiveStacks(args); + + if (isVoidarg) + { + args.gpRegisters.clear(); + args.fpRegisters.clear(); + args.doubleRegisters.clear(); + args.vectorRegisters.clear(); + args.stacks.clear(); + } + else if (!args.knownTypes.empty()) + { + filterArgsByKnownTypes(args); + } + else + { + createContinuousArgRegisters(args); + } + + leaveOnlyContinuousStack(args); +} + +void Filter::filterCallArgs(FilterableLayout& args, bool isVoidarg) const +{ + if (isVoidarg) + { + args.gpRegisters.clear(); + args.fpRegisters.clear(); + args.doubleRegisters.clear(); + args.vectorRegisters.clear(); + args.stacks.clear(); + } + else if (!args.knownTypes.empty()) + { + filterArgsByKnownTypes(args); + } + else + { + leaveOnlyContinuousArgRegisters(args); + } + + leaveOnlyContinuousStack(args); +} + +void Filter::filterCallArgsByDefLayout( + FilterableLayout& args, + const FilterableLayout& defArgs) const +{ + args.gpRegisters = std::vector(defArgs.gpRegisters); + args.fpRegisters = std::vector(defArgs.fpRegisters); + args.doubleRegisters = std::vector(defArgs.doubleRegisters); + args.vectorRegisters = std::vector(defArgs.vectorRegisters); + args.knownOrder = defArgs.knownOrder; + + leaveOnlyContinuousStack(args); + leaveSameStacks(args, defArgs); +} + +void Filter::filterRets(FilterableLayout& rets) const +{ + if (!rets.knownTypes.empty() && rets.knownTypes.front()) + { + filterRetsByKnownTypes(rets); + } + else + { + leaveOnlyContinuousRetRegisters(rets); + } +} + +void Filter::filterRetsByDefLayout( + FilterableLayout& rets, + const FilterableLayout& defRets) const +{ + rets.gpRegisters = std::vector(defRets.gpRegisters); + rets.fpRegisters = std::vector(defRets.fpRegisters); + rets.doubleRegisters = std::vector(defRets.doubleRegisters); + rets.vectorRegisters = std::vector(defRets.vectorRegisters); + rets.knownOrder = defRets.knownOrder; + + leaveOnlyContinuousStack(rets); + leaveSameStacks(rets, defRets); +} + +void Filter::filterArgsByKnownTypes(FilterableLayout& lay) const +{ + FilterableLayout newLayout; + auto& gpRegs = _cc->getParamRegisters(); + auto& fpRegs = _cc->getParamFPRegisters(); + auto& doubleRegs = _cc->getParamDoubleRegisters(); + auto& vecRegs = _cc->getParamVectorRegisters(); + + // Indexes of registers to be used next as particular parameter. + auto sIt = lay.stacks.begin(); + + std::size_t gpEnd = gpRegs.size(); + std::size_t fpEnd = fpRegs.size(); + std::size_t doubleEnd = doubleRegs.size(); + std::size_t vecEnd = vecRegs.size(); + + std::vector types = expandTypes(lay.knownTypes); + + for (auto t: types) + { + std::size_t requiredStacks = 0; + OrderID stackOrd = OrderID::ORD_STACK; + + if (!doubleRegs.empty() && t->isDoubleTy()) + { + if (newLayout.doubleRegisters.size() < doubleEnd) + { + requiredStacks = fetchDoubleRegsForType(t, newLayout); + stackOrd = OrderID::ORD_STACK_GROUP; + } + } + else if (!fpRegs.empty() && t->isFloatingPointTy()) + { + if (newLayout.fpRegisters.size() < fpEnd) + { + requiredStacks = fetchFPRegsForType(t, newLayout); + stackOrd = OrderID::ORD_STACK_GROUP; + } + } + else if (!vecRegs.empty() && t->isVectorTy()) + { + if (newLayout.vectorRegisters.size() < vecEnd) + { + requiredStacks = fetchVecRegsForType(t, newLayout); + stackOrd = OrderID::ORD_STACK_GROUP; + } + } + else if (!gpRegs.empty()) + { + if (newLayout.gpRegisters.size() < gpEnd) + { + requiredStacks = fetchGPRegsForType(t, newLayout); + stackOrd = OrderID::ORD_STACK_GROUP; + } + } + + + if (!requiredStacks && stackOrd == OrderID::ORD_STACK) + { + requiredStacks = getNumberOfStacksForType(t); + } + + for (std::size_t i = 0; i < requiredStacks; i++) + { + if (sIt != lay.stacks.end()) + { + newLayout.stacks.push_back(*sIt); + sIt++; + } + else + { + newLayout.stacks.push_back(nullptr); + } + + newLayout.knownOrder.push_back( + i == 0 ? stackOrd : + OrderID::ORD_STACK_GROUP); + } + } + + lay = newLayout; +} + +std::vector Filter::expandTypes(const std::vector& types) const +{ + if (_cc->passesLargeObjectsByReference()) + { + return types; + } + else + { + std::vector expanded; + + std::deque toExpand( + types.begin(), + types.end()); + + while (!toExpand.empty()) + { + auto t = toExpand.front(); + toExpand.pop_front(); + + if (auto* st = dyn_cast(t)) + { + for (auto& e : st->elements()) + { + toExpand.push_back(e); + } + } + else + { + expanded.push_back(t); + } + } + + return expanded; + } +} + +size_t Filter::fetchGPRegsForType(Type* type, FilterableLayout& lay) const +{ + std::size_t sizeBefore = lay.gpRegisters.size(); + std::size_t reqStacks = fetchRegsForType( + type, + lay.gpRegisters, + _cc->getParamRegisters(), + _cc->getMaxNumOfRegsPerParam()); + + std::size_t change = lay.gpRegisters.size() - sizeBefore; + if (change) + { + lay.knownOrder.push_back(OrderID::ORD_GPR); + lay.knownOrder.resize( + lay.knownOrder.size() + change - 1, OrderID::ORD_GPR_GROUP); + } + + return reqStacks; +} + +size_t Filter::fetchFPRegsForType(Type* type, FilterableLayout& lay) const +{ + std::size_t sizeBefore = lay.fpRegisters.size(); + std::size_t reqStacks = fetchRegsForType( + type, + lay.fpRegisters, + _cc->getParamFPRegisters(), + _cc->getMaxNumOfFPRegsPerParam()); + + std::size_t change = lay.fpRegisters.size() - sizeBefore; + if (change) + { + lay.knownOrder.push_back(OrderID::ORD_FPR); + lay.knownOrder.resize( + lay.knownOrder.size() + change - 1, OrderID::ORD_FPR_GROUP); + } + + return reqStacks; +} + +size_t Filter::fetchDoubleRegsForType(Type* type, FilterableLayout& lay) const +{ + std::size_t sizeBefore = lay.doubleRegisters.size(); + std::size_t reqStacks = fetchRegsForType( + type, + lay.doubleRegisters, + _cc->getParamDoubleRegisters(), + _cc->getMaxNumOfDoubleRegsPerParam()); + + std::size_t change = lay.doubleRegisters.size() - sizeBefore; + if (change) + { + lay.knownOrder.push_back(OrderID::ORD_DOUBR); + lay.knownOrder.resize( + lay.knownOrder.size() + change - 1, OrderID::ORD_DOUBR_GROUP); + } + + return reqStacks; +} + +size_t Filter::fetchVecRegsForType(Type* type, FilterableLayout& lay) const +{ + std::size_t sizeBefore = lay.vectorRegisters.size(); + std::size_t reqStacks = fetchRegsForType( + type, + lay.vectorRegisters, + _cc->getParamVectorRegisters(), + _cc->getMaxNumOfVectorRegsPerParam()); + + std::size_t change = lay.vectorRegisters.size() - sizeBefore; + if (change) + { + lay.knownOrder.push_back(OrderID::ORD_VECR); + lay.knownOrder.resize( + lay.knownOrder.size() + change - 1, OrderID::ORD_VECR_GROUP); + } + + return reqStacks; +} + +size_t Filter::fetchRegsForType( + Type* type, + std::vector& store, + const std::vector& regs, + std::size_t maxRegsPerObject) const +{ + if (regs.empty()) + { + return getNumberOfStacksForType(type); + } + + Type* registerType = _abi->getRegister(regs.front())->getType(); + std::size_t registerSize = _abi->getTypeByteSize(registerType); + std::size_t typeSize = type->isVoidTy() ? + _abi->getWordSize() : _abi->getTypeByteSize(type); + + if (typeSize <= registerSize) + { + if (regs.size() <= store.size()) + { + return getNumberOfStacksForType(registerType); + } + + auto reg = regs[store.size()]; + store.push_back(reg); + + return 0; + } + + if ((typeSize > registerSize) + && (typeSize <= registerSize*maxRegsPerObject)) + { + std::size_t numberOfRegs = typeSize / registerSize; + auto regIt = store.size(); + + if (_cc->respectsRegisterCouples()) + { + if ((regIt+1)%2 == 0) + { + regIt++; + } + } + + for (std::size_t i = 0; i < numberOfRegs; i++) + { + if (regs.size() <= regIt) + { + return getNumberOfStacksForType(registerType)*(numberOfRegs-i); + } + + auto reg = regs[regIt]; + store.push_back(reg); + regIt++; + } + + return 0; + } + + if (_cc->passesLargeObjectsByReference()) + { + if (regs.size() <= store.size()) + { + return getNumberOfStacksForType(registerType); + } + + auto reg = regs[store.size()]; + store.push_back(reg); + + return 0; + } + + return getNumberOfStacksForType(type); +} + +size_t Filter::getNumberOfStacksForType(Type* type) const +{ + auto maxBytesPerParam = _cc->getMaxBytesPerStackParam(); + + if (maxBytesPerParam == 0) + { + return 0; + } + + std::size_t num = _abi->getTypeByteSize(type) / maxBytesPerParam; + + return num < 1 ? 1 : num; +} + +void Filter::filterRetsByKnownTypes(FilterableLayout& lay) const +{ + std::vector regGPValues, regFPValues, regDoubleValues, regVecValues; + + auto& gpRegs = _cc->getReturnRegisters(); + auto& fpRegs = _cc->getReturnFPRegisters(); + auto& doubleRegs = _cc->getReturnDoubleRegisters(); + auto& vecRegs = _cc->getReturnVectorRegisters(); + + Type* retType = lay.knownTypes.empty() ? nullptr + : lay.knownTypes.front(); + + if (retType == nullptr) + { + return; + } + + if (retType->isVectorTy() && !vecRegs.empty()) + { + std::size_t typeSize = _abi->getTypeByteSize(retType); + Type* registerType = _abi->getRegister(vecRegs.front())->getType(); + std::size_t registerSize = _abi->getTypeByteSize(registerType); + + if (typeSize <= registerSize || + (typeSize > registerSize*vecRegs.size())) + { + regVecValues.push_back(vecRegs.front()); + } + + std::size_t numOfRegs = typeSize/registerSize; + for (std::size_t i = 0; i < numOfRegs && i < vecRegs.size(); i++) + { + regVecValues.push_back(vecRegs[i]); + } + } + else if (retType->isDoubleTy() && !doubleRegs.empty()) + { + std::size_t typeSize = _abi->getTypeByteSize(retType); + Type* registerType = _abi->getRegister(doubleRegs.front())->getType(); + std::size_t registerSize = _abi->getTypeByteSize(registerType); + + if (typeSize <= registerSize || + (typeSize > registerSize*doubleRegs.size())) + { + regDoubleValues.push_back(doubleRegs.front()); + } + + std::size_t numOfRegs = typeSize/registerSize; + for (std::size_t i = 0; i < numOfRegs && i < doubleRegs.size(); i++) + { + regDoubleValues.push_back(doubleRegs[i]); + } + } + else if (retType->isFloatingPointTy() && !fpRegs.empty()) + { + std::size_t typeSize = _abi->getTypeByteSize(retType); + Type* registerType = _abi->getRegister(fpRegs.front())->getType(); + std::size_t registerSize = _abi->getTypeByteSize(registerType); + + if (typeSize <= registerSize || + (typeSize > registerSize*fpRegs.size())) + { + regFPValues.push_back(fpRegs.front()); + } + + std::size_t numOfRegs = typeSize/registerSize; + for (std::size_t i = 0; i < numOfRegs && i < fpRegs.size(); i++) + { + regFPValues.push_back(fpRegs[i]); + } + } + else if (!retType->isVoidTy()) + { + assert(!gpRegs.empty()); + + std::size_t typeSize = _abi->getTypeByteSize(retType); + Type* registerType = _abi->getRegister(gpRegs.front())->getType(); + std::size_t registerSize = _abi->getTypeByteSize(registerType); + + if (typeSize <= registerSize || + (typeSize > registerSize*gpRegs.size())) + { + regGPValues.push_back(gpRegs.front()); + } + + std::size_t numOfRegs = typeSize/registerSize; + for (std::size_t i = 0; i < numOfRegs && i < gpRegs.size(); i++) + { + regGPValues.push_back(gpRegs[i]); + } + } + + lay.gpRegisters = std::move(regGPValues); + lay.fpRegisters = std::move(regFPValues); + lay.doubleRegisters = std::move(regDoubleValues); + lay.vectorRegisters = std::move(regVecValues); + lay.knownTypes = {retType}; +} + + +void Filter::leaveCommonArgs(std::vector& allArgs) const +{ + leaveCommon(allArgs); +} + +void Filter::leaveCommonRets(std::vector& allRets) const +{ + leaveCommon(allRets); +} + +void Filter::leaveCommon(std::vector& lays) const +{ + if (lays.empty()) + { + return; + } + + auto& firstGPR = lays.front().gpRegisters; + auto& firstFPR = lays.front().fpRegisters; + auto& firstDR = lays.front().doubleRegisters; + auto& firstVR = lays.front().vectorRegisters; + + std::set commonGPR(firstGPR.begin(), firstGPR.end()); + std::set commonFPR(firstFPR.begin(), firstFPR.end()); + std::set commonDR(firstDR.begin(), firstDR.end()); + std::set commonVR(firstVR.begin(), firstVR.end()); + + std::size_t minStacks = lays.front().stacks.size(); + + for (auto& lay : lays) + { + + auto& gpr = lay.gpRegisters; + auto& fpr = lay.fpRegisters; + auto& dr = lay.doubleRegisters; + auto& vr = lay.vectorRegisters; + + commonGPR.insert(gpr.begin(), gpr.end()); + commonFPR.insert(fpr.begin(), fpr.end()); + commonDR.insert(dr.begin(), dr.end()); + commonVR.insert(vr.begin(), vr.end()); + + // if (lay.stacks.empty()) + // { + // continue; + // } + // else if (!minStacks || (minStacks > lay.stacks.size())) + if (minStacks > lay.stacks.size()) + { + minStacks = lay.stacks.size(); + } + } + + for (auto& lay : lays) + { + lay.gpRegisters.assign(commonGPR.begin(), commonGPR.end()); + lay.fpRegisters.assign(commonFPR.begin(), commonFPR.end()); + lay.doubleRegisters.assign(commonDR.begin(), commonDR.end()); + lay.vectorRegisters.assign(commonVR.begin(), commonVR.end()); + lay.stacks.resize(minStacks, nullptr); + + orderFiterableLayout(lay); + } +} + +void Filter::orderFiterableLayout(FilterableLayout& lay) const +{ + orderStacks(lay.stacks, _cc->getStackParamOrder()); + orderRegistersBy(lay.gpRegisters, _cc->getParamRegisters()); + orderRegistersBy(lay.fpRegisters, _cc->getParamFPRegisters()); + orderRegistersBy(lay.doubleRegisters, _cc->getParamDoubleRegisters()); + orderRegistersBy(lay.vectorRegisters, _cc->getParamVectorRegisters()); +} + +void Filter::orderStacks(std::vector& stacks, bool asc) const +{ + if (stacks.empty()) + { + return; + } + + auto config = _abi->getConfig(); + + std::stable_sort( + stacks.begin(), + stacks.end(), + [config, asc](Value* a, Value* b) -> bool + { + auto aOff = config->getStackVariableOffset(a); + auto bOff = config->getStackVariableOffset(b); + + bool ascOrd = aOff < bOff; + + return asc ? ascOrd : !ascOrd; + }); +} + +void Filter::orderRegistersBy( + std::vector& regs, + const std::vector& orderedVector) const +{ + std::stable_sort( + regs.begin(), + regs.end(), + [orderedVector](uint32_t a, uint32_t b) -> bool + { + auto it1 = std::find(orderedVector.begin(), orderedVector.end(), a); + auto it2 = std::find(orderedVector.begin(), orderedVector.end(), b); + + return std::distance(it1, it2) > 0; + }); +} + +FilterableLayout Filter::createArgsFilterableLayout( + const std::vector& group, + const std::vector& knownTypes) const +{ + FilterableLayout layout = separateArgValues(group); + layout.knownTypes = knownTypes; + + orderFiterableLayout(layout); + + return layout; +} + +FilterableLayout Filter::createRetsFilterableLayout( + const std::vector& group, + llvm::Type* knownType) const +{ + std::vector knownTypes = {knownType}; + return createRetsFilterableLayout(group, knownTypes); +} + +FilterableLayout Filter::createRetsFilterableLayout( + const std::vector& group, + const std::vector& knownTypes) const +{ + FilterableLayout layout = separateRetValues(group); + layout.knownTypes = knownTypes; + + orderFiterableLayout(layout); + + return layout; +} + +FilterableLayout Filter::separateArgValues(const std::vector& paramValues) const +{ + auto& regs = _cc->getParamRegisters(); + auto& fpRegs = _cc->getParamFPRegisters(); + auto& doubleRegs = _cc->getParamDoubleRegisters(); + auto& vecRegs = _cc->getParamVectorRegisters(); + + return separateValues(paramValues, regs, fpRegs, doubleRegs, vecRegs); +} + +FilterableLayout Filter::separateRetValues(const std::vector& paramValues) const +{ + auto& regs = _cc->getReturnRegisters(); + auto& fpRegs = _cc->getReturnFPRegisters(); + auto& doubleRegs = _cc->getReturnDoubleRegisters(); + auto& vecRegs = _cc->getReturnVectorRegisters(); + + FilterableLayout lay = separateValues(paramValues, regs, fpRegs, doubleRegs, vecRegs); + lay.stacks.clear(); + + return lay; +} + +FilterableLayout Filter::separateValues( + const std::vector& paramValues, + const std::vector& gpRegs, + const std::vector& fpRegs, + const std::vector& doubleRegs, + const std::vector& vecRegs) const +{ + FilterableLayout layout; + + for (auto pv: paramValues) + { + if (_abi->isStackVariable(pv)) + { + layout.stacks.push_back(pv); + } + else if (!_abi->isRegister(pv)) + { + continue; + } + if (std::find(gpRegs.begin(), gpRegs.end(), + _abi->getRegisterId(pv)) != gpRegs.end()) + { + layout.gpRegisters.push_back(_abi->getRegisterId(pv)); + } + else if (std::find(doubleRegs.begin(), doubleRegs.end(), + _abi->getRegisterId(pv)) != doubleRegs.end()) + { + layout.doubleRegisters.push_back(_abi->getRegisterId(pv)); + } + else if (std::find(fpRegs.begin(), fpRegs.end(), + _abi->getRegisterId(pv)) != fpRegs.end()) + { + layout.fpRegisters.push_back(_abi->getRegisterId(pv)); + } + else if (std::find(vecRegs.begin(), vecRegs.end(), + _abi->getRegisterId(pv)) != vecRegs.end()) + { + layout.vectorRegisters.push_back(_abi->getRegisterId(pv)); + } + } + + return layout; +} + +std::vector Filter::createGroupedArgValues(const FilterableLayout& lay) const +{ + return createGroupedValues(lay); +} + +std::vector Filter::createGroupedRetValues(const FilterableLayout& lay) const +{ + return createGroupedValues(lay); +} + +std::vector Filter::createGroupedValues(const FilterableLayout& lay) const +{ + std::vector paramValues; + + auto ri = lay.gpRegisters.begin(); + auto fi = lay.fpRegisters.begin(); + auto di = lay.doubleRegisters.begin(); + auto vi = lay.vectorRegisters.begin(); + auto si = lay.stacks.begin(); + + if (!lay.knownOrder.empty()) + { + for (auto ord : lay.knownOrder) + { + switch (ord) + { + case OrderID::ORD_GPR: + if (ri != lay.gpRegisters.end()) + { + paramValues.push_back(_abi->getRegister(*ri)); + ri++; + } + break; + + case OrderID::ORD_FPR: + if (fi != lay.fpRegisters.end()) + { + paramValues.push_back(_abi->getRegister(*fi)); + fi++; + } + break; + + case OrderID::ORD_DOUBR: + if (di != lay.doubleRegisters.end()) + { + paramValues.push_back(_abi->getRegister(*di)); + di++; + } + break; + + case OrderID::ORD_VECR: + if (vi != lay.vectorRegisters.end()) + { + paramValues.push_back(_abi->getRegister(*vi)); + vi++; + } + break; + + case OrderID::ORD_STACK: + if (si != lay.stacks.end()) + { + paramValues.push_back(*si); + si++; + } + break; + + case OrderID::ORD_GPR_GROUP: + if (ri != lay.gpRegisters.end()) + { + ri++; + } + break; + + case OrderID::ORD_FPR_GROUP: + if (fi != lay.fpRegisters.end()) + { + fi++; + } + break; + + case OrderID::ORD_DOUBR_GROUP: + if (di != lay.doubleRegisters.end()) + { + di++; + } + break; + + case OrderID::ORD_VECR_GROUP: + + if (vi != lay.vectorRegisters.end()) + { + vi++; + } + break; + + case OrderID::ORD_STACK_GROUP: + if (si != lay.stacks.end()) + { + si++; + } + break; + + + default: + continue; + } + } + + return paramValues; + } + + while (ri != lay.gpRegisters.end()) + { + paramValues.push_back(_abi->getRegister(*ri)); + ri++; + } + + while (fi != lay.fpRegisters.end()) + { + paramValues.push_back(_abi->getRegister(*fi)); + fi++; + } + + while (di != lay.doubleRegisters.end()) + { + paramValues.push_back(_abi->getRegister(*di)); + di++; + } + + while (vi != lay.vectorRegisters.end()) + { + paramValues.push_back(_abi->getRegister(*vi)); + vi++; + } + + paramValues.insert(paramValues.end(), si, lay.stacks.end()); + + return paramValues; +} + +void Filter::leaveOnlyPositiveStacks(FilterableLayout& lay) const +{ + auto* config = _abi->getConfig(); + + lay.stacks.erase( + std::remove_if(lay.stacks.begin(), lay.stacks.end(), + [config](const Value* li) + { + auto aOff = config->getStackVariableOffset(li); + return aOff.isDefined() && aOff < 0; + }), + lay.stacks.end()); +} + +void Filter::leaveOnlyContinuousStack(FilterableLayout& lay) const +{ + retdec::utils::Maybe prevOff; + int gap = _cc->getMaxBytesPerStackParam(); + auto* config = _abi->getConfig(); + + auto it = lay.stacks.begin(); + while (it != lay.stacks.end()) + { + auto off = config->getStackVariableOffset(*it); + + if (prevOff.isUndefined()) + { + prevOff = off; + } + else if (std::abs(prevOff - off) > gap) + { + it = lay.stacks.erase(it); + continue; + } + else + { + prevOff = off; + } + + ++it; + } +} + +void Filter::leaveOnlyContinuousArgRegisters(FilterableLayout& lay) const +{ + leaveOnlyContinuousRegisters(lay.gpRegisters, _cc->getParamRegisters()); + leaveOnlyContinuousRegisters(lay.fpRegisters, _cc->getParamFPRegisters()); + leaveOnlyContinuousRegisters(lay.doubleRegisters, _cc->getParamDoubleRegisters()); + leaveOnlyContinuousRegisters(lay.vectorRegisters, _cc->getParamVectorRegisters()); + + bool usingGPR = !_cc->getParamRegisters().empty(); + bool usingFPR = !_cc->getParamFPRegisters().empty(); + bool usingDR = !_cc->getParamDoubleRegisters().empty(); + bool usingVR = !_cc->getParamVectorRegisters().empty(); + + bool missingGPR = lay.gpRegisters.size() < _cc->getParamRegisters().size(); + bool missingFPR = lay.fpRegisters.size() < _cc->getParamFPRegisters().size(); + bool missingDR = lay.doubleRegisters.size() < _cc->getParamDoubleRegisters().size(); + bool missingVR = lay.vectorRegisters.size() < _cc->getParamVectorRegisters().size(); + + // If calling convention passes large objects on stacks (not by refetence) + // usage of another registers will be omitted. + // + // Stacks can be erased only if all types of registers that a cc + // uses are missing some register usage. + + bool eraseStacks = false; + if (_cc->passesLargeObjectsByReference()) + { + if (usingGPR) + { + eraseStacks = missingGPR; + } + + if (usingFPR) + { + eraseStacks = eraseStacks && missingFPR; + } + + if (usingDR) + { + eraseStacks = eraseStacks && missingDR; + } + + if (usingVR) + { + eraseStacks = eraseStacks && missingVR; + } + } + + if (eraseStacks) + { + lay.stacks.clear(); + } +} + +void Filter::createContinuousArgRegisters(FilterableLayout& lay) const +{ + std::vector gpRegs, fpRegs, dbRegs, veRegs; + + if (!lay.gpRegisters.empty()) + { + uint32_t regId = lay.gpRegisters.back(); + + for (auto ccR : _cc->getParamRegisters()) + { + gpRegs.push_back(ccR); + if (regId == ccR) + { + break; + } + } + } + + if (!lay.fpRegisters.empty()) + { + uint32_t regId = lay.fpRegisters.back(); + + for (auto ccR : _cc->getParamFPRegisters()) + { + fpRegs.push_back(ccR); + if (regId == ccR) + { + break; + } + } + } + + if (!lay.doubleRegisters.empty()) + { + uint32_t regId = lay.doubleRegisters.back(); + + for (auto ccR : _cc->getParamDoubleRegisters()) + { + dbRegs.push_back(ccR); + if (regId == ccR) + { + break; + } + } + } + + if (!lay.vectorRegisters.empty()) + { + uint32_t regId = lay.vectorRegisters.back(); + + for (auto ccR : _cc->getParamRegisters()) + { + veRegs.push_back(ccR); + if (regId == ccR) + { + break; + } + } + } + + lay.gpRegisters = std::move(gpRegs); + lay.fpRegisters = std::move(fpRegs); + lay.doubleRegisters = std::move(dbRegs); + lay.vectorRegisters = std::move(veRegs); +} + +void Filter::leaveOnlyContinuousRetRegisters(FilterableLayout& lay) const +{ + leaveOnlyContinuousRegisters(lay.gpRegisters, _cc->getReturnRegisters()); + leaveOnlyContinuousRegisters(lay.fpRegisters, _cc->getReturnFPRegisters()); + leaveOnlyContinuousRegisters(lay.doubleRegisters, _cc->getReturnDoubleRegisters()); + leaveOnlyContinuousRegisters(lay.vectorRegisters, _cc->getReturnVectorRegisters()); +} + +void Filter::leaveOnlyContinuousRegisters( + std::vector& regs, + const std::vector& templRegs) const +{ + auto itEnd = regs.end(); + auto it = regs.begin(); + for (auto regId : templRegs) + { + if (it == itEnd) + { + break; + } + + if (regId != *it) + { + regs.erase(it, itEnd); + break; + } + + it++; + } +} + +void Filter::leaveSameStacks(FilterableLayout& lay, const FilterableLayout& fig) const +{ + lay.stacks.resize(fig.stacks.size(), nullptr); +} + +// +//============================================================================= +// FilterProvider +//============================================================================= +// + +Filter::Ptr FilterProvider::createFilter(Abi* abi, const CallingConvention::ID& id) +{ + auto* cc = abi->getCallingConvention(id); + if (cc == nullptr) + { + cc = abi->getDefaultCallingConvention(); + } + + assert(cc); + + auto c = abi->getConfig(); + bool isMinGW = c->getConfig().tools.isGcc() + && c->getConfig().fileFormat.isPe(); + + if (abi->isX64() && (isMinGW || c->getConfig().tools.isMsvc())) + { + return std::make_unique(abi, cc); + } + + return std::make_unique(abi, cc); +} + +} +} diff --git a/src/bin2llvmir/optimizations/param_return/filter/ms_x64.cpp b/src/bin2llvmir/optimizations/param_return/filter/ms_x64.cpp new file mode 100644 index 000000000..eb03c46de --- /dev/null +++ b/src/bin2llvmir/optimizations/param_return/filter/ms_x64.cpp @@ -0,0 +1,189 @@ +/** +* @file src/bin2llvmir/optimizations/param_return/filter/ms_x64.cpp +* @brief Microsoft x64 specific filtration of registers. +* @copyright (c) 2019 Avast Software, licensed under the MIT license +*/ + +#include + +#include "retdec/bin2llvmir/optimizations/param_return/filter/ms_x64.h" + +using namespace retdec::utils; +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +MSX64Filter::MSX64Filter( + const Abi* abi, + const CallingConvention* cc) : + Filter(abi, cc) +{ +} + +MSX64Filter::~MSX64Filter() +{ +} + +void MSX64Filter::filterDefinitionArgs(FilterableLayout& args, bool isVoidarg) const +{ + leaveOnlyPositiveStacks(args); + + if (isVoidarg) + { + args.gpRegisters.clear(); + args.fpRegisters.clear(); + args.doubleRegisters.clear(); + args.vectorRegisters.clear(); + args.stacks.clear(); + } + else if (!args.knownTypes.empty()) + { + filterArgsByKnownTypes(args); + } + else + { + leaveOnlyAlternatingArgRegisters(args); + } + + leaveOnlyContinuousStack(args); +} + +void MSX64Filter::filterCallArgs(FilterableLayout& args, bool isVoidarg) const +{ + if (isVoidarg) + { + args.gpRegisters.clear(); + args.fpRegisters.clear(); + args.doubleRegisters.clear(); + args.vectorRegisters.clear(); + args.stacks.clear(); + } + else if (!args.knownTypes.empty()) + { + filterArgsByKnownTypes(args); + } + else + { + leaveOnlyAlternatingArgRegisters(args); + } + + leaveOnlyContinuousStack(args); +} + +void MSX64Filter::filterArgsByKnownTypes(FilterableLayout& lay) const +{ + FilterableLayout newLayout; + newLayout.knownTypes = lay.knownTypes; + + auto& gpRegs = _cc->getParamRegisters(); + + // Indexes of registers to be used next as particular parameter. + auto sIt = lay.stacks.begin(); + + std::size_t regEnd = gpRegs.size(); + + std::vector registers; + + std::vector types = expandTypes(lay.knownTypes); + + for (auto t: types) + { + std::size_t requiredStacks = 0; + OrderID stackOrd = OrderID::ORD_STACK; + + if (t->isFloatingPointTy() || t->isVectorTy()) + { + if (registers.size() < regEnd) + { + newLayout.fpRegisters = registers; + requiredStacks = fetchFPRegsForType(t, newLayout); + registers = newLayout.fpRegisters; + stackOrd = OrderID::ORD_STACK_GROUP; + } + } + else + { + if (registers.size() < regEnd) + { + newLayout.gpRegisters = registers; + requiredStacks = fetchGPRegsForType(t, newLayout); + registers = newLayout.gpRegisters; + stackOrd = OrderID::ORD_STACK_GROUP; + } + } + + if (!requiredStacks && stackOrd == OrderID::ORD_STACK) + { + requiredStacks = getNumberOfStacksForType(t); + } + + for (std::size_t i = 0; i < requiredStacks; i++) + { + if (sIt != lay.stacks.end()) + { + newLayout.stacks.push_back(*sIt); + sIt++; + } + else + { + newLayout.stacks.push_back(nullptr); + } + + newLayout.knownOrder.push_back( + i == 0 ? stackOrd : + OrderID::ORD_STACK_GROUP); + } + } + + std::vector regVals; + for (auto r : registers) + { + regVals.push_back(_abi->getRegister(r)); + } + + lay = separateArgValues(regVals); + lay.stacks = newLayout.stacks; + lay.knownOrder = newLayout.knownOrder; + lay.knownTypes = newLayout.knownTypes; +} + +void MSX64Filter::leaveOnlyAlternatingArgRegisters(FilterableLayout& lay) const +{ + auto& templRegs = _cc->getParamRegisters(); + auto& fpTemplRegs = _cc->getParamFPRegisters(); + + auto it = lay.gpRegisters.begin(); + auto fIt = lay.fpRegisters.begin(); + + std::size_t idx = 0; + while (idx < fpTemplRegs.size() && idx < templRegs.size()) + { + if (it == lay.gpRegisters.end() && fIt == lay.fpRegisters.end()) + { + lay.stacks.clear(); + return; + } + + if (it != lay.gpRegisters.end() && *it == templRegs[idx]) + { + it++; + } + else if (fIt != lay.fpRegisters.end() && *fIt == fpTemplRegs[idx]) + { + fIt++; + } + else + { + lay.gpRegisters.erase(it, lay.gpRegisters.end()); + lay.fpRegisters.erase(fIt, lay.fpRegisters.end()); + lay.stacks.clear(); + return; + } + + idx++; + } +} + +} +} diff --git a/src/bin2llvmir/optimizations/param_return/param_return.cpp b/src/bin2llvmir/optimizations/param_return/param_return.cpp index df4507900..5d763c5c5 100644 --- a/src/bin2llvmir/optimizations/param_return/param_return.cpp +++ b/src/bin2llvmir/optimizations/param_return/param_return.cpp @@ -1,21 +1,7 @@ /** * @file src/bin2llvmir/optimizations/param_return/param_return.cpp * @brief Detect functions' parameters and returns. -* @copyright (c) 2017 Avast Software, licensed under the MIT license -* -* Original implementation: -* name -- position of format string, position of variadic arg start -* printf/scanf -- 0, 1 -* __printf_chk -- 1, 2 -* __fprintf_chk -- 2, 3 -* fprintf/fscanf/wsprintfA/wsprintf/sprintf/sscanf -- 1, 2 -* snprintf -- 2, 3 -* __snprintf_chk -- 4, 5 -* ioctl/open/FmtStr -- not handled -- erase arguments -* wprintf/wscanf -- 0, 1 -* error -- 2, 3 -* error_at_line -- 4, 5 -* other -- not handled -- copy arguments +* @copyright (c) 2019 Avast Software, licensed under the MIT license */ #include @@ -23,6 +9,7 @@ #include #include +#include #include #include #include @@ -30,6 +17,7 @@ #include "retdec/utils/container.h" #include "retdec/utils/string.h" +#include "retdec/bin2llvmir/optimizations/param_return/filter/filter.h" #include "retdec/bin2llvmir/optimizations/param_return/param_return.h" #define debug_enabled false #include "retdec/bin2llvmir/utils/llvm.h" @@ -42,58 +30,6 @@ using namespace llvm; namespace retdec { namespace bin2llvmir { -llvm::Value* getRoot(ReachingDefinitionsAnalysis& RDA, llvm::Value* i, bool first = true) -{ - static std::set seen; - if (first) - { - seen.clear(); - } - if (seen.count(i)) - { - return i; - } - seen.insert(i); - - i = llvm_utils::skipCasts(i); - if (auto* ii = dyn_cast(i)) - { - if (auto* u = RDA.getUse(ii)) - { - if (u->defs.size() == 1) - { - auto* d = (*u->defs.begin())->def; - if (auto* s = dyn_cast(d)) - { - return getRoot(RDA, s->getValueOperand(), false); - } - else - { - return d; - } - } - else if (auto* l = dyn_cast(ii)) - { - return getRoot(RDA, l->getPointerOperand(), false); - } - else - { - return i; - } - } - else if (auto* l = dyn_cast(ii)) - { - return getRoot(RDA, l->getPointerOperand(), false); - } - else - { - return i; - } - } - - return i; -} - // //============================================================================= // ParamReturn @@ -123,11 +59,13 @@ bool ParamReturn::runOnModule(Module& m) _image = FileImageProvider::getFileImage(_module); _dbgf = DebugFormatProvider::getDebugFormat(_module); _lti = LtiProvider::getLti(_module); + _collector = CollectorProvider::createCollector(_abi, _module, &_RDA); + return run(); } bool ParamReturn::runOnModuleCustom( - llvm::Module& m, + Module& m, Config* c, Abi* abi, FileImage* img, @@ -140,6 +78,8 @@ bool ParamReturn::runOnModuleCustom( _image = img; _dbgf = dbgf; _lti = lti; + _collector = CollectorProvider::createCollector(_abi, _module, &_RDA); + return run(); } @@ -151,21 +91,16 @@ bool ParamReturn::run() return false; } - _RDA.runOnModule(*_module, AbiProvider::getAbi(_module)); - -//dumpModuleToFile(_module); + _RDA.runOnModule(*_module, _abi); collectAllCalls(); - dumpInfo(); +// dumpInfo(); filterCalls(); - dumpInfo(); +// dumpInfo(); applyToIr(); _RDA.clear(); -//dumpModuleToFile(_module); -//exit(1); - return false; } @@ -183,18 +118,9 @@ void ParamReturn::collectAllCalls() continue; } - _fnc2calls.emplace( - std::make_pair( - &f, - DataFlowEntry( - _module, - _RDA, - _config, - _abi, - _image, - _dbgf, - _lti, - &f))); + _fnc2calls.emplace(std::make_pair( + &f, + createDataFlowEntry(&f))); } for (auto& f : _module->getFunctionList()) @@ -218,1579 +144,698 @@ void ParamReturn::collectAllCalls() auto fIt = _fnc2calls.find(calledVal); if (fIt == _fnc2calls.end()) { - fIt = _fnc2calls.emplace(std::make_pair( + fIt = _fnc2calls.emplace( + std::make_pair( calledVal, - DataFlowEntry( - _module, - _RDA, - _config, - _abi, - _image, - _dbgf, - _lti, - calledVal))).first; + createDataFlowEntry(calledVal))).first; } - fIt->second.addCall(call); - } -} - -void ParamReturn::filterCalls() -{ - for (auto& p : _fnc2calls) - { - p.second.filter(); - } -} - -void ParamReturn::applyToIr() -{ - for (auto& p : _fnc2calls) - { - p.second.applyToIr(); - } - - for (auto& p : _fnc2calls) - { - p.second.connectWrappers(); + addDataFromCall(&fIt->second, call); } } -/** - * Dump all the info collected and processed so far. - */ -void ParamReturn::dumpInfo() +DataFlowEntry ParamReturn::createDataFlowEntry(Value* calledValue) const { - LOG << std::endl << "_fnc2calls:" << std::endl; - for (auto& p : _fnc2calls) - { - p.second.dump(); - } -} + DataFlowEntry dataflow(calledValue); -// -//============================================================================= -// CallEntry -//============================================================================= -// + _collector->collectDefArgs(&dataflow); + _collector->collectDefRets(&dataflow); -CallEntry::CallEntry(llvm::CallInst* c) : - call(c) -{ + collectExtraData(&dataflow); + return dataflow; } -bool registerCanBeParameterAccordingToAbi(Abi* _abi, Config* _config, llvm::Value* val) +void ParamReturn::collectExtraData(DataFlowEntry* dataflow) const { - if (!_abi->isRegister(val)) + auto* fnc = dataflow->getFunction(); + if (fnc == nullptr) { - return true; + return; } - if (_config->getConfig().architecture.isX86()) - { - return false; - } - else if (_config->getConfig().architecture.isPpc()) - { - static std::set names = {"r3", "r4", "r5", "r6", "r7", "r8", "r9"}; - if (names.find(val->getName()) == names.end()) - { - return false; - } - } - else if (_config->getConfig().architecture.isArmOrThumb()) - { - static std::set names = {"r0", "r1", "r2", "r3"}; - if (names.find(val->getName()) == names.end()) - { - return false; - } - } - else if (_config->getConfig().architecture.isMipsOrPic32()) + // Main + // + if (fnc->getName() == "main") { - static std::set names = {"a0", "a1", "a2", "a3"}; - if (names.find(val->getName()) == names.end()) - { - return false; - } - } - - return true; -} + auto charPointer = PointerType::get( + Type::getInt8Ty(_module->getContext()), 0); -/** - * Remove all registers that are not used to pass argument according to ABI. - */ -void CallEntry::filterRegisters(Abi* _abi, Config* _config) -{ - auto it = possibleArgStores.begin(); - while (it != possibleArgStores.end()) - { - auto* op = (*it)->getPointerOperand(); - if (!registerCanBeParameterAccordingToAbi(_abi, _config, op)) + dataflow->setArgTypes( { - it = possibleArgStores.erase(it); - } - else + _abi->getDefaultType(), + PointerType::get(charPointer, 0) + }, { - ++it; - } + "argc", + "argv" + }); + + dataflow->setRetType(_abi->getDefaultType()); + return; } -} -void DataFlowEntry::filterRegistersArgLoads() -{ - auto it = argLoads.begin(); - while (it != argLoads.end()) + // LTI info. + // + auto* cf = _config->getConfigFunction(fnc); + if (cf && (cf->isDynamicallyLinked() || cf->isStaticallyLinked())) { - auto* op = (*it)->getPointerOperand(); - if (!registerCanBeParameterAccordingToAbi(_abi, _config, op)) - { - it = argLoads.erase(it); - } - else + auto fp = _lti->getPairFunctionFree(cf->getName()); + if (fp.first) { - auto aOff = _config->getStackVariableOffset(op); - if (aOff.isDefined() && aOff < 0) + std::vector argTypes; + std::vector argNames; + for (auto& a : fp.first->args()) + { + if (!a.getType()->isSized()) + { + continue; + } + argTypes.push_back(a.getType()); + argNames.push_back(a.getName()); + } + dataflow->setArgTypes( + std::move(argTypes), + std::move(argNames)); + + if (fp.first->isVarArg()) { - it = argLoads.erase(it); + dataflow->setVariadic(); } - else + dataflow->setRetType(fp.first->getReturnType()); + + std::string declr = fp.second->getDeclaration(); + if (!declr.empty()) { - ++it; + cf->setDeclarationString(declr); } + return; } } -} -/** - * Stack with the lowest (highest negative) offset is the first call argument. - */ -void CallEntry::filterSort(Config* _config) -{ - auto& stores = possibleArgStores; + auto dbgFnc = _dbgf ? _dbgf->getFunction( + _config->getFunctionAddress(fnc)) : nullptr; - std::stable_sort( - stores.begin(), - stores.end(), - [_config](StoreInst* a, StoreInst* b) -> bool + // Debug info. + // + if (dbgFnc) { - auto aOff = _config->getStackVariableOffset(a->getPointerOperand()); - auto bOff = _config->getStackVariableOffset(b->getPointerOperand()); - - if (aOff.isUndefined() && bOff.isUndefined()) - { - return _config->getConfigRegisterNumber(a->getPointerOperand()) < - _config->getConfigRegisterNumber(b->getPointerOperand()); - } - else if (aOff.isUndefined() && bOff.isDefined()) - { - return true; - } - else if (aOff.isDefined() && bOff.isUndefined()) - { - return false; - } - else + std::vector argTypes; + std::vector argNames; + for (auto& a : dbgFnc->parameters) { - return aOff < bOff; + auto* t = llvm_utils::stringToLlvmTypeDefault( + _module, a.type.getLlvmIr()); + if (!t->isSized()) + { + continue; + } + argTypes.push_back(t); + argNames.push_back(a.getName()); } - }); -} - -void DataFlowEntry::filterSortArgLoads() -{ - std::stable_sort( - argLoads.begin(), - argLoads.end(), - [this](LoadInst* a, LoadInst* b) -> bool - { - auto aOff = _config->getStackVariableOffset(a->getPointerOperand()); - auto bOff = _config->getStackVariableOffset(b->getPointerOperand()); + dataflow->setArgTypes( + std::move(argTypes), + std::move(argNames)); - if (aOff.isUndefined() && bOff.isUndefined()) - { - return _config->getConfigRegisterNumber(a->getPointerOperand()) < - _config->getConfigRegisterNumber(b->getPointerOperand()); - } - else if (aOff.isUndefined() && bOff.isDefined()) - { - return true; - } - else if (aOff.isDefined() && bOff.isUndefined()) - { - return false; - } - else + if (dbgFnc->isVariadic()) { - return aOff < bOff; + dataflow->setVariadic(); } - }); -} + dataflow->setRetType( + llvm_utils::stringToLlvmTypeDefault( + _module, + dbgFnc->returnType.getLlvmIr())); -/** - * Arguments are stored into stack variables which go one after another, - * there can be no big stack offset gaps. - */ -void CallEntry::filterLeaveOnlyContinuousStackOffsets(Config* _config) -{ - retdec::utils::Maybe prevOff; - auto it = possibleArgStores.begin(); - while (it != possibleArgStores.end()) - { - auto* s = *it; - auto off = _config->getStackVariableOffset(s->getPointerOperand()); - auto* val = llvm_utils::skipCasts(s->getValueOperand()); + return; + } - int gap = 8; -// int gap = 4; - if (val->getType()->isFloatingPointTy()) + auto configFnc = _config->getConfigFunction(fnc); + if (_config->getConfig().isIda() && configFnc) + { + std::vector argTypes; + std::vector argNames; + for (auto& a : configFnc->parameters) { - gap = 8; + auto* t = llvm_utils::stringToLlvmTypeDefault( + _module, + a.type.getLlvmIr()); + if (!t->isSized()) + { + continue; + } + argTypes.push_back(t); + argNames.push_back(a.getName()); } + dataflow->setArgTypes( + std::move(argTypes), + std::move(argNames)); - if (off.isUndefined()) - { - ++it; - continue; - } - if (prevOff.isUndefined()) - { - prevOff = off; - } - else if (std::abs(prevOff - off) > gap) - { - it = possibleArgStores.erase(it); - continue; - } - else + if (configFnc->isVariadic()) { - prevOff = off; + dataflow->setVariadic(); } - - ++it; + dataflow->setRetType( + llvm_utils::stringToLlvmTypeDefault( + _module, + configFnc->returnType.getLlvmIr())); + return; } -} -void CallEntry::filterLeaveOnlyNeededStackOffsets(Abi* _abi, Config* _config) -{ - int regNum = 0; - auto it = possibleArgStores.begin(); - while (it != possibleArgStores.end()) + // Calling convention. + if (configFnc) { - auto* s = *it; - auto* op = s->getPointerOperand(); - auto off = _config->getStackVariableOffset(op); + dataflow->setCallingConvention(configFnc->callingConvention.getID()); + } - if (_abi->isRegister(op)) - { - ++regNum; - ++it; - continue; - } - else if (off.isDefined()) + // Wrappers. + // + if (CallInst* wrappedCall = getWrapper(fnc)) + { + auto* wf = wrappedCall->getCalledFunction(); + auto* ltiFnc = _lti->getLlvmFunctionFree(wf->getName()); + if (ltiFnc) { - if (_config->getConfig().architecture.isX86()) - { - // nothing - } - else if (_config->getConfig().architecture.isPpc()) - { - if (regNum == 7) - { - // nothing - } - else - { - it = possibleArgStores.erase(it); - continue; - } - } - else if (_config->getConfig().architecture.isArmOrThumb() - || _config->getConfig().architecture.isMipsOrPic32()) + std::vector argTypes; + std::vector argNames; + for (auto& a : ltiFnc->args()) { - if (regNum == 4) + if (!a.getType()->isSized()) { - // nothing - } - else - { - it = possibleArgStores.erase(it); continue; } + argTypes.push_back(a.getType()); + argNames.push_back(a.getName()); } - else + dataflow->setArgTypes( + std::move(argTypes), + std::move(argNames)); + + if (ltiFnc->isVarArg()) { - // nothing + dataflow->setVariadic(); } - } + dataflow->setRetType(ltiFnc->getReturnType()); + dataflow->setWrappedCall(wrappedCall); - ++it; + return; + } } } -void CallEntry::extractFormatString(ReachingDefinitionsAnalysis& _RDA) +CallInst* ParamReturn::getWrapper(Function* fnc) const { - for (auto* s : possibleArgStores) + auto ai = AsmInstruction(fnc); + if (ai.isInvalid()) { - auto* v = getRoot(_RDA, s->getValueOperand()); - auto* gv = dyn_cast_or_null(v); + return nullptr; + } - if (gv == nullptr || !gv->hasInitializer()) + bool single = true; + auto next = ai.getNext(); + while (next.isValid()) + { + if (!next.empty() && !isa(next.front())) { - continue; + single = false; + break; } + next = next.getNext(); + } - auto* init = dyn_cast_or_null(gv->getInitializer()); - if (init == nullptr) + // Pattern + // .text:00008A38 LDR R0, =aCCc ; "C::cc()" + // .text:00008A3C B puts + // .text:00008A40 off_8A40 DCD aCCc + // TODO: make better wrapper detection. In wrapper, wrapped function params + // should not be set like in this example. + // + if (ai && next) + { + if (_image->getConstantDefault(next.getEndAddress())) { - if (auto* i = dyn_cast(gv->getInitializer())) + auto* l = ai.getInstructionFirst(); + auto* s = ai.getInstructionFirst(); + auto* c = next.getInstructionFirst(); + if (l && s && c && isa(l->getPointerOperand()) + && s->getPointerOperand()->getName() == "r0") { - if (auto* igv = dyn_cast(i->getOperand(0))) + auto gvA = _config->getGlobalAddress(cast(l->getPointerOperand())); + if (gvA == next.getEndAddress()) { - init = dyn_cast_or_null(igv->getInitializer()); + return nullptr; } } } + } - if (init == nullptr || !init->isString()) + if (single) + { + for (auto& i : ai) { - continue; + if (auto* c = dyn_cast(&i)) + { + auto* cf = c->getCalledFunction(); + if (cf && !cf->isIntrinsic()) // && cf->isDeclaration()) + { + return c; + } + } } - - formatStr = init->getAsString(); - break; } -} - -// -//============================================================================= -// ReturnEntry -//============================================================================= -// - -ReturnEntry::ReturnEntry(llvm::ReturnInst* r) : - ret(r) -{ - -} - -// -//============================================================================= -// DataFlowEntry -//============================================================================= -// -DataFlowEntry::DataFlowEntry( - llvm::Module* m, - ReachingDefinitionsAnalysis& rda, - Config* c, - Abi* abi, - FileImage* img, - DebugFormat* dbg, - Lti* lti, - llvm::Value* v) - : - _module(m), - _RDA(rda), - _config(c), - _abi(abi), - _image(img), - _lti(lti), - called(v) -{ - if (auto* f = getFunction()) + unsigned aiNum = 0; + bool isSmall = true; + next = ai; + while (next.isValid()) { - configFnc = c->getConfigFunction(f); - if (dbg) + ++aiNum; + next = next.getNext(); + if (aiNum > 4) { - dbgFnc = dbg->getFunction(c->getFunctionAddress(f)); + isSmall = false; + break; } - - if (!f->empty()) + } + auto* s = _image->getImage()->getSegmentFromAddress(ai.getAddress()); + if ((s && s->getName() == ".plt") || isSmall) + { + for (inst_iterator it = inst_begin(fnc), rIt = inst_end(fnc); + it != rIt; ++it) { - addArgLoads(); - addRetStores(); + if (auto* l = dyn_cast(&*it)) + { + std::string n = l->getPointerOperand()->getName(); + if (n == "lr" || n == "sp") + { + return nullptr; + } + } + else if (auto* s = dyn_cast(&*it)) + { + std::string n = s->getPointerOperand()->getName(); + if (n == "lr" || n == "sp") + { + return nullptr; + } + } + else if (auto* c = dyn_cast(&*it)) + { + auto* cf = c->getCalledFunction(); + if (cf && !cf->isIntrinsic() && cf->isDeclaration()) + { + return c; + } + } } } - setTypeFromExtraInfo(); + return nullptr; } -bool DataFlowEntry::isFunctionEntry() const +void ParamReturn::addDataFromCall(DataFlowEntry* dataflow, CallInst* call) const { - return getFunction() != nullptr; -} + CallEntry* ce = dataflow->createCallEntry(call); -bool DataFlowEntry::isValueEntry() const -{ - return called && !isFunctionEntry(); + _collector->collectCallArgs(ce); + + // TODO: Use info from collecting return loads. + // + // At this moment info return loads is not used + // as it is not reliable source of info + // about return value. To enable this + // collector must have redesigned and reimplemented + // collection algorithm. + // + //_collector->collectCallRets(ce); + + collectExtraData(ce); } -llvm::Value* DataFlowEntry::getValue() const +void ParamReturn::collectExtraData(CallEntry* ce) const { - return called; } -llvm::Function* DataFlowEntry::getFunction() const +void ParamReturn::dumpInfo() const { - return dyn_cast_or_null(called); + LOG << std::endl << "_fnc2calls:" << std::endl; + + for (auto& p : _fnc2calls) + { + dumpInfo(p.second); + } } -void DataFlowEntry::dump() const +void ParamReturn::dumpInfo(const DataFlowEntry& de) const { + auto called = de.getValue(); + auto fnc = de.getFunction(); + auto configFnc = _config->getConfigFunction(fnc); + auto dbgFnc = _dbgf ? _dbgf->getFunction( + _config->getFunctionAddress(fnc)) : nullptr; + auto wrappedCall = de.getWrappedCall(); + LOG << "\n\t>|" << called->getName().str() << std::endl; - LOG << "\t>|fnc call : " << isFunctionEntry() << std::endl; - LOG << "\t>|val call : " << isValueEntry() << std::endl; - LOG << "\t>|variadic : " << isVarArg << std::endl; + LOG << "\t>|fnc call : " << de.isFunction() << std::endl; + LOG << "\t>|val call : " << de.isValue() << std::endl; + LOG << "\t>|variadic : " << de.isVariadic() << std::endl; + LOG << "\t>|voidarg : " << de.isVoidarg() << std::endl; + LOG << "\t>|call conv: " << de.getCallingConvention() << std::endl; LOG << "\t>|config f : " << (configFnc != nullptr) << std::endl; LOG << "\t>|debug f : " << (dbgFnc != nullptr) << std::endl; LOG << "\t>|wrapp c : " << llvmObjToString(wrappedCall) << std::endl; - LOG << "\t>|type set : " << typeSet << std::endl; - LOG << "\t>|ret type : " << llvmObjToString(retType) << std::endl; + LOG << "\t>|type set : " << !de.argTypes().empty() << std::endl; + LOG << "\t>|ret type : " << llvmObjToString(de.getRetType()) << std::endl; + LOG << "\t>|ret value: " << llvmObjToString(de.getRetValue()) << std::endl; LOG << "\t>|arg types:" << std::endl; - for (auto* t : argTypes) + for (auto* t : de.argTypes()) { LOG << "\t\t>|" << llvmObjToString(t) << std::endl; } LOG << "\t>|arg names:" << std::endl; - for (auto& n : argNames) + for (auto& n : de.argNames()) { LOG << "\t\t>|" << n << std::endl; } LOG << "\t>|calls:" << std::endl; - for (auto& e : calls) + for (auto& e : de.callEntries()) { - LOG << "\t\t>|" << llvmObjToString(e.call) << std::endl; - LOG << "\t\t\targ stores:" << std::endl; - for (auto* s : e.possibleArgStores) - { - LOG << "\t\t\t>|" << llvmObjToString(s) << std::endl; - } - LOG << "\t\t\tret loads:" << std::endl; - for (auto* l : e.possibleRetLoads) - { - LOG << "\t\t\t>|" << llvmObjToString(l) << std::endl; - } - if (!e.formatStr.empty()) - { - LOG << "\t\t\t>|format str: " << e.formatStr << std::endl; - } + dumpInfo(e); } LOG << "\t>|arg loads:" << std::endl; - for (auto* l : argLoads) + for (auto* l : de.args()) { LOG << "\t\t\t>|" << llvmObjToString(l) << std::endl; } LOG << "\t>|return stores:" << std::endl; - for (auto& e : retStores) + for (auto& e : de.retEntries()) { - LOG << "\t\t>|" << llvmObjToString(e.ret) << std::endl; - for (auto* s : e.possibleRetStores) - { - LOG << "\t\t\t>|" << llvmObjToString(s) << std::endl; - } + dumpInfo(e); } } -void DataFlowEntry::addArgLoads() +void ParamReturn::dumpInfo(const CallEntry& ce) const { - auto* f = getFunction(); - if (f == nullptr) + LOG << "\t\t>|" << llvmObjToString(ce.getCallInstruction()) + << std::endl; + LOG << "\t\t\tvoidarg :" << ce.isVoidarg() << std::endl; + LOG << "\t\t\targ values:" << std::endl; + for (auto* s : ce.args()) { - return; + LOG << "\t\t\t>|" << llvmObjToString(s) << std::endl; } - - std::set added; - for (auto it = inst_begin(f), end = inst_end(f); it != end; ++it) + LOG << "\t\t\targ stores:" << std::endl; + for (auto* s : ce.argStores()) { - if (auto* l = dyn_cast(&*it)) - { - auto* ptr = l->getPointerOperand(); - if (!_config->isStackVariable(ptr) && !_abi->isRegister(ptr)) - { - continue; - } - if (_abi->isFlagRegister(ptr)) - { - continue; - } - - auto* use = _RDA.getUse(l); - if (use == nullptr) - { - continue; - } - - if ((use->defs.empty() || use->isUndef()) - && added.find(ptr) == added.end()) - { - argLoads.push_back(l); - added.insert(ptr); - } - } + LOG << "\t\t\t>|" << llvmObjToString(s) << std::endl; + } + LOG << "\t\t\tret values:" << std::endl; + for (auto* l : ce.retValues()) + { + LOG << "\t\t\t>|" << llvmObjToString(l) << std::endl; + } + LOG << "\t\t\tret loads:" << std::endl; + for (auto* l : ce.retLoads()) + { + LOG << "\t\t\t>|" << llvmObjToString(l) << std::endl; + } + LOG << "\t\t\targ types:" << std::endl; + for (auto* t : ce.getBaseFunction()->argTypes()) + { + LOG << "\t\t\t>|" << llvmObjToString(t); + LOG << " (size : " << _abi->getTypeByteSize(t) << "B)" << std::endl; + } + for (auto* t : ce.argTypes()) + { + LOG << "\t\t\t>|" << llvmObjToString(t); + LOG << " (size : " << _abi->getTypeByteSize(t) << "B)" << std::endl; } + LOG << "\t\t\tformat string: " << ce.getFormatString() << std::endl; } -void DataFlowEntry::addRetStores() +void ParamReturn::dumpInfo(const ReturnEntry& re) const { - auto* f = getFunction(); - if (f == nullptr) + LOG << "\t\t>|" << llvmObjToString(re.getRetInstruction()) + << std::endl; + + LOG << "\t\t\tret stores:" << std::endl; + for (auto* s : re.retStores()) { - return; + LOG << "\t\t\t>|" << llvmObjToString(s) << std::endl; } - for (auto it = inst_begin(f), end = inst_end(f); it != end; ++it) + LOG << "\t\t\tret values:" << std::endl; + for (auto* s : re.retValues()) { - if (auto* r = dyn_cast(&*it)) - { - ReturnEntry re(r); - - NonIterableSet seenBbs; - NonIterableSet disqualifiedValues; - auto* b = r->getParent(); - seenBbs.insert(b); - Instruction* prev = r; - while (true) - { - if (prev == &b->front()) - { - auto* spb = b->getSinglePredecessor(); - if (spb && !spb->empty() && seenBbs.hasNot(spb)) - { - b = spb; - prev = &b->back(); - seenBbs.insert(b); - } - else - { - break; - } - } - else - { - prev = prev->getPrevNode(); - } - if (prev == nullptr) - { - break; - } - - if (isa(prev) || isa(prev)) - { - break; - } - else if (auto* store = dyn_cast(prev)) - { - auto* ptr = store->getPointerOperand(); - - if (disqualifiedValues.hasNot(ptr) - && !_abi->isFlagRegister(ptr) - && (_config->isStackVariable(ptr) || _abi->isRegister(ptr))) - { - re.possibleRetStores.push_back(store); - disqualifiedValues.insert(ptr); - } - } - else if (auto* load = dyn_cast(prev)) - { - auto* ptr = load->getPointerOperand(); - disqualifiedValues.insert(ptr); - } - } - - retStores.push_back(re); - } + LOG << "\t\t\t>|" << llvmObjToString(s) << std::endl; } } -void DataFlowEntry::addCall(llvm::CallInst* call) +void ParamReturn::filterCalls() { - // Pattern: - // bc pc // call prantf() - // align 4 - // prantf(): - // ... - // Call has no args because it is stub, if we let it, it will destroy - // all arg from all other calls. - // - // TODO: - // => ignore - this is an ugly hack, solve somehow better. - // - if (_config->getConfig().architecture.isArmOrThumb()) + std::map filters; + + for (auto& p : _fnc2calls) { - if (auto ai = AsmInstruction(call)) + DataFlowEntry& de = p.second; + auto cc = de.getCallingConvention(); + if (filters.find(cc) == filters.end()) { - auto* cs = ai.getCapstoneInsn(); - if ((cs->id == ARM_INS_B || cs->id == ARM_INS_BX) - && cs->detail->arm.op_count == 1 - && cs->detail->arm.operands[0].type == ARM_OP_REG - && cs->detail->arm.operands[0].reg == ARM_REG_PC) - { - return; - } + filters[cc] = FilterProvider::createFilter(_abi, cc); } - } - - CallEntry ce(call); - - addCallArgs(call, ce); - addCallReturns(call, ce); - calls.push_back(ce); -} - -void DataFlowEntry::addCallArgs(llvm::CallInst* call, CallEntry& ce) -{ - NonIterableSet disqualifiedValues; - unsigned maxUsedRegNum = 0; - auto* b = call->getParent(); - Instruction* prev = call; - std::set seen; - seen.insert(b); - while (true) - { - if (prev == &b->front()) + if (de.hasDefinition()) { - auto* spb = b->getSinglePredecessor(); - if (spb && !spb->empty() && spb != b && seen.count(spb) == 0) - { - b = spb; - prev = &b->back(); - seen.insert(b); - } - else - { - break; - } + filters[cc]->filterDefinition(&de); } - else + + if (de.isVariadic()) { - prev = prev->getPrevNode(); + filters[cc]->filterCallsVariadic(&de, _collector.get()); } - if (prev == nullptr) + else { - break; + filters[cc]->filterCalls(&de); } - if (auto* call = dyn_cast(prev)) + filters[cc]->estimateRetValue(&de); + + modifyType(de); + } +} + +Type* ParamReturn::extractType(Value* from) const +{ + from = llvm_utils::skipCasts(from); + + if (from == nullptr) + { + return _abi->getDefaultType(); + } + + if (auto* p = dyn_cast(from->getType())) + { + return p->getElementType(); + } + + if (auto* p = dyn_cast(from->getType())) + { + if (auto* a = dyn_cast(p->getElementType())) { - auto* calledFnc = call->getCalledFunction(); - if (calledFnc == nullptr || !calledFnc->isIntrinsic()) - { - break; - } + return PointerType::get(a->getElementType(), 0); } - else if (auto* store = dyn_cast(prev)) - { - auto* val = store->getValueOperand(); - auto* ptr = store->getPointerOperand(); + } - if (!_config->isStackVariable(ptr) && !_abi->isRegister(ptr)) - { - disqualifiedValues.insert(ptr); - } + return from->getType(); +} + +void ParamReturn::modifyType(DataFlowEntry& de) const +{ + // TODO + // Based on large type we should do: + // + // If large type is encountered + // and if cc passes large type by reference + // just cast the reference + // + // else separate as much values as possible + // and call function that will create new structure + // and put this values in the elements of + // the structure set this structure as parameter - if (auto* l = dyn_cast(val)) + if (de.argTypes().empty()) + { + for (auto& call : de.callEntries()) + { + std::vector types; + for (auto& arg : call.args()) { - if (l->getPointerOperand()->getName() == "ebp" - || l->getPointerOperand()->getName() == "rbp") - { - disqualifiedValues.insert(ptr); - } - if (_abi->isRegister(ptr) - && _abi->isRegister(l->getPointerOperand()) - && ptr != l->getPointerOperand()) + if (arg == nullptr) { - disqualifiedValues.insert(l->getPointerOperand()); + types.push_back(_abi->getDefaultType()); + continue; } - } - if (disqualifiedValues.hasNot(ptr) - && !_abi->isFlagRegister(ptr) - && (isa(ptr) || _abi->isRegister(ptr))) - { - ce.possibleArgStores.push_back(store); - disqualifiedValues.insert(ptr); - disqualifiedValues.insert(store); - - if (_abi->isGeneralPurposeRegister(ptr) - || (_config->getConfig().architecture.isMipsOrPic32() - && _abi->isRegister(ptr) - && ptr->getType()->isFloatingPointTy())) + auto usage = std::find_if( + call.argStores().begin(), + call.argStores().end(), + [arg](StoreInst* s) + { + return s->getPointerOperand() + == arg; + }); + + if (usage == call.argStores().end()) { - auto rn = _config->getConfigRegisterNumber(ptr); - if (rn.isDefined() && rn > maxUsedRegNum) + + if (auto* p = dyn_cast(arg->getType())) + { + types.push_back(p->getElementType()); + } + else { - maxUsedRegNum = rn; + types.push_back(arg->getType()); } } + else + { + types.push_back(extractType((*usage)->getValueOperand())); + } } + + de.setArgTypes(std::move(types)); + break; } } -} -void DataFlowEntry::addCallReturns(llvm::CallInst* call, CallEntry& ce) -{ - NonIterableSet disqualifiedValues; - auto* b = call->getParent(); - Instruction* next = call; - std::set seen; - seen.insert(b); - while (true) + if (de.argTypes().empty()) { - if (next == &b->back()) + std::vector types; + std::vector args; + + for (auto i : de.args()) { - auto* ssb = b->getSingleSuccessor(); - if (ssb && !ssb->empty() && ssb != b && seen.count(ssb) == 0) + if (i == nullptr) { - b = ssb; - next = &b->front(); - seen.insert(b); + types.push_back(_abi->getDefaultType()); } - else + else if (auto* p = dyn_cast(i->getType())) { - break; + types.push_back(p->getElementType()); } - } - else - { - next = next->getNextNode(); - } - if (next == nullptr) - { - break; - } - - if (auto* call = dyn_cast(next)) - { - auto* calledFnc = call->getCalledFunction(); - if (calledFnc == nullptr || !calledFnc->isIntrinsic()) + else { - break; + types.push_back(i->getType()); } } - else if (auto* store = dyn_cast(next)) - { - auto* ptr = store->getPointerOperand(); - disqualifiedValues.insert(ptr); - } - else if (auto* load = dyn_cast(next)) - { - auto* ptr = load->getPointerOperand(); - if (disqualifiedValues.hasNot(ptr) - && !_abi->isFlagRegister(ptr) - && (_config->isStackVariable(ptr) || _abi->isRegister(ptr))) - { - ce.possibleRetLoads.push_back(load); - disqualifiedValues.insert(ptr); - } - } + de.setArgTypes(std::move(types)); } + + auto args = de.args(); + args.erase( + std::remove_if( + args.begin(), + args.end(), + [](Value* v){return v == nullptr;}), + args.end()); + de.setArgs(std::move(args)); } -void DataFlowEntry::filter() +void ParamReturn::applyToIr() { - if (!isVarArg) + for (auto& p : _fnc2calls) + { + applyToIr(p.second); + } + + for (auto& p : _fnc2calls) { - callsFilterCommonRegisters(); + + connectWrappers(p.second); } +} - filterRegistersArgLoads(); - filterSortArgLoads(); +void ParamReturn::applyToIr(DataFlowEntry& de) +{ + Function* fnc = de.getFunction(); - for (CallEntry& e : calls) + if (fnc == nullptr) { - e.filterRegisters(_abi, _config); - e.filterSort(_config); - e.filterLeaveOnlyContinuousStackOffsets(_config); - e.filterLeaveOnlyNeededStackOffsets(_abi, _config); + auto loadsOfCalls = fetchLoadsOfCalls(de.callEntries()); - if (isVarArg) + for (auto l : loadsOfCalls) { - e.extractFormatString(_RDA); + IrModifier::modifyCallInst(l.first, de.getRetType(), l.second); } - } - if (!isVarArg) - { - callsFilterSameNumberOfStacks(); + return; } - if (typeSet) + if (fnc->arg_size() > 0) { - for (CallEntry& e : calls) - { - auto tIt = argTypes.begin(); - auto sIt = e.possibleArgStores.begin(); + return; + } + + auto loadsOfCalls = fetchLoadsOfCalls(de.callEntries()); - while (tIt != argTypes.end() && sIt != e.possibleArgStores.end()) - { - Type* t = *tIt; - auto nextIt = sIt; - ++nextIt; - if (t->isDoubleTy() - && nextIt != e.possibleArgStores.end() - && _abi->isRegister((*nextIt)->getPointerOperand())) - { - e.possibleArgStores.erase(nextIt); - } + std::map rets2vals; - ++tIt; - ++sIt; - } - } - } - else - { - if (_config->getConfig().architecture.isArmOrThumb()) - { - static std::vector armNames = - {"r0", "r1", "r2", "r3"}; - - for (CallEntry& e : calls) - { - std::size_t idx = 0; - auto sIt = e.possibleArgStores.begin(); - while (sIt != e.possibleArgStores.end() && idx < armNames.size()) - { - StoreInst* s = *sIt; - if (s->getPointerOperand()->getName() != armNames[idx]) - { - e.possibleArgStores.erase(sIt, e.possibleArgStores.end()); - break; - } - - ++sIt; - ++idx; - } - } - } - } - - setTypeFromUseContext(); -} - -void DataFlowEntry::callsFilterCommonRegisters() -{ - if (calls.empty()) - { - return; - } - - std::set commonRegs; - - for (auto* s : calls.front().possibleArgStores) - { - Value* r = s->getPointerOperand(); - if (_abi->isRegister(r)) - { - commonRegs.insert(r); - } - } - - for (auto& e : calls) - { - // TODO: sometimes, we do not find all arg stores. - // this is a hack, we should manufacture loads even if we do not have - // stores but know there are some arguments (debug, ...). - if (e.possibleArgStores.empty()) - { - continue; - } - - std::set regs; - for (auto* s : e.possibleArgStores) - { - Value* r = s->getPointerOperand(); - if (_abi->isRegister(r)) - { - regs.insert(r); - } - } - - std::set intersect; - std::set_intersection( - commonRegs.begin(), - commonRegs.end(), - regs.begin(), - regs.end(), - std::inserter(intersect, intersect.begin())); - commonRegs = std::move(intersect); - } - - // If common contains r3, then it should also contain r2, r1, and r0. - // Example for MIPS: if contains a2, it should a1 and a0. - // - static std::vector regNames; - if (regNames.empty()) - { - if (_config->getConfig().architecture.isMipsOrPic32()) - { - if (_config->getConfig().tools.isPspGcc()) - { - regNames = {"a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3"}; - } - else - { - regNames = {"a0", "a1", "a2", "a3"}; - } - } - else if (_config->getConfig().architecture.isArmOrThumb()) - { - regNames = {"r0", "r1", "r2", "r3"}; - } - else if (_config->getConfig().architecture.isPpc()) - { - regNames = {"r3", "r4", "r5", "r6", "r7", "r8", "r9"}; - } - } - for (auto it = regNames.rbegin(); it != regNames.rend(); ++it) - { - auto* r = _config->getLlvmRegister(*it); - if (commonRegs.count(r)) - { - ++it; - while (it != regNames.rend()) - { - r = _config->getLlvmRegister(*it); - commonRegs.insert(r); - ++it; - } - break; - } - } - - for (auto& e : calls) - { - auto it = e.possibleArgStores.begin(); - for ( ; it != e.possibleArgStores.end(); ) - { - Value* r = (*it)->getPointerOperand(); - if (_abi->isRegister(r) - && commonRegs.find(r) == commonRegs.end()) - { - it = e.possibleArgStores.erase(it); - } - else - { - ++it; - } - } - } -} - -void DataFlowEntry::callsFilterSameNumberOfStacks() -{ - if (calls.empty()) - { - return; - } - - std::size_t loads = 0; - for (auto* l : argLoads) - { - if (_config->isStackVariable(l->getPointerOperand())) - { - ++loads; - } - } - - std::size_t stacks = std::numeric_limits::max(); - for (auto& ce : calls) - { - std::size_t ss = 0; - for (auto* s : ce.possibleArgStores) - { - if (_config->isStackVariable(s->getPointerOperand())) - { - ++ss; - } - } - - // TODO: all but one have 2 params, receiving have 2 params, one has zero. - // - if (ss < stacks && ss != 0 && ss >= loads) - { - stacks = ss; - } - } - if (typeSet && stacks < argTypes.size()) - { - stacks = argTypes.size(); - } - - for (auto& ce : calls) - { - std::size_t cntr = 0; - auto it = ce.possibleArgStores.begin(); - while (it != ce.possibleArgStores.end()) - { - auto* s = *it; - if (!_config->isStackVariable(s->getPointerOperand())) - { - ++it; - continue; - } - - ++cntr; - if (cntr > stacks) - { - it = ce.possibleArgStores.erase(it); - } - else - { - ++it; - } - } - } -} - -void DataFlowEntry::applyToIr() -{ - if (isVarArg) - { - applyToIrVariadic(); - } - else - { - applyToIrOrdinary(); - } -} - -void DataFlowEntry::applyToIrOrdinary() -{ - if (Function* fnc = getFunction()) - { - if (fnc->arg_size() > 0) - { - return; - } - - std::map> calls2vals; - for (auto& e : calls) - { - std::vector loads; - auto* call = e.call; - for (auto* s : e.possibleArgStores) - { - auto fIt = specialArgStorage.find(loads.size()); - while (fIt != specialArgStorage.end()) - { - auto* sl = new LoadInst(fIt->second, "", call); - loads.push_back(sl); - fIt = specialArgStorage.find(loads.size()); - } - - auto* l = new LoadInst(s->getPointerOperand(), "", call); - loads.push_back(l); - } - - calls2vals[call] = loads; - } - - llvm::Value* retVal = nullptr; - std::map rets2vals; - if (_config->getConfig().architecture.isX86()) - { - if (retType->isFloatingPointTy()) - { - retVal = _config->getLlvmRegister("st7"); - } - else if (_config->getConfig().architecture.isX86_32()) - { - retVal = _config->getLlvmRegister("eax"); - } - else if (_config->getConfig().architecture.isX86_64()) - { - retVal = _config->getLlvmRegister("rax"); - } - } - else if (_config->getConfig().architecture.isMipsOrPic32()) - { - retVal = _config->getLlvmRegister("v0"); - } - else if (_config->getConfig().architecture.isArmOrThumb()) - { - retVal = _config->getLlvmRegister("r0"); - } - else if (_config->getConfig().architecture.isPpc()) - { - retVal = _config->getLlvmRegister("r3"); - } - if (retVal) - { - for (auto& e : retStores) - { - auto* l = new LoadInst(retVal, "", e.ret); - rets2vals[e.ret] = l; - } - } - - std::vector argStores; - for (LoadInst* l : argLoads) - { - auto fIt = specialArgStorage.find(argStores.size()); - while (fIt != specialArgStorage.end()) - { - argStores.push_back(fIt->second); - fIt = specialArgStorage.find(argStores.size()); - } - - argStores.push_back(l->getPointerOperand()); - } - - static std::vector ppcNames = - {"r3", "r4", "r5", "r6", "r7", "r8", "r9"}; - static std::vector armNames = - {"r0", "r1", "r2", "r3"}; - static std::vector mipsNames = - {"a0", "a1", "a2", "a3"}; - if (_config->getConfig().tools.isPspGcc()) - { - mipsNames = {"a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3"}; - } - for (auto& p : calls2vals) - { - retdec::utils::Maybe stackOff; - if (_config->getConfig().architecture.isX86()) - { - if (!p.second.empty()) - { - auto* l = cast(p.second.back()); - if (_config->isStackVariable(l->getPointerOperand())) - { - stackOff = _config->getStackVariableOffset(l->getPointerOperand()); - stackOff = stackOff + 4; - } - } - - if (stackOff.isUndefined()) - { - AsmInstruction ai(p.first); - while (ai.isValid()) - { - for (auto& i : ai) - { - if (auto* s = dyn_cast(&i)) - { - if (_config->isStackVariable(s->getPointerOperand())) - { - stackOff = _config->getStackVariableOffset(s->getPointerOperand()); - break; - } - } - } - if (stackOff.isDefined()) - { - break; - } - ai = ai.getPrev(); - } - } - } - - std::size_t idx = 0; - for (auto* t : argTypes) - { - (void) t; - if (p.second.size() <= idx) - { - if (_config->getConfig().architecture.isArmOrThumb()) - { - if (idx < armNames.size()) - { - auto* r = _config->getLlvmRegister(armNames[idx]); - auto* l = new LoadInst(r, "", p.first); - p.second.push_back(l); - } - } - else if (_config->getConfig().architecture.isMipsOrPic32()) - { - if (idx < mipsNames.size()) - { - auto* r = _config->getLlvmRegister(mipsNames[idx]); - auto* l = new LoadInst(r, "", p.first); - p.second.push_back(l); - } - } - else if (_config->getConfig().architecture.isPpc()) - { - if (idx < ppcNames.size()) - { - auto* r = _config->getLlvmRegister(ppcNames[idx]); - auto* l = new LoadInst(r, "", p.first); - p.second.push_back(l); - } - } - else if (_config->getConfig().architecture.isX86() - && stackOff.isDefined()) - { - auto* s = _config->getLlvmStackVariable(p.first->getFunction(), stackOff); - if (s) - { - auto* l = new LoadInst(s, "", p.first); - p.second.push_back(l); - stackOff = stackOff + 4; - } - else - { - stackOff.setUndefined(); - } - } - } - ++idx; - } - } - - auto* oldType = fnc->getType(); - IrModifier irm(_module, _config); - auto* newFnc = irm.modifyFunction( - fnc, - retType, - argTypes, - isVarArg, - rets2vals, - calls2vals, - retVal, - argStores, - argNames).first; - - LOG << "modify fnc: " << newFnc->getName().str() << " = " - << llvmObjToString(oldType) << " -> " - << llvmObjToString(newFnc->getType()) << std::endl; - - called = newFnc; - } - else - { - for (auto& e : calls) - { - auto* call = e.call; - LOG << "\tmodify call: " << llvmObjToString(call) << std::endl; - - std::vector loads; - for (auto* s : e.possibleArgStores) - { - auto* l = new LoadInst(s->getPointerOperand(), "", call); - loads.push_back(l); - LOG << "\t\t" << llvmObjToString(l) << std::endl; - } - - auto* ret = fnc ? fnc->getReturnType() : call->getType(); - IrModifier::modifyCallInst(call, ret, loads); - } - } -} - -void DataFlowEntry::applyToIrVariadic() -{ - std::vector ppcNames = - {"r3", "r4", "r5", "r6", "r7", "r8", "r9"}; - std::vector armNames = - {"r0", "r1", "r2", "r3"}; - std::vector mipsNames = - {"a0", "a1", "a2", "a3"}; - if (_config->getConfig().tools.isPspGcc()) - { - mipsNames = {"a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3"}; - } - - std::vector mipsFpNames = - {"fd12", "fd13", "fd14", "fd15", "fd16", "fd17", "fd18", "fd19", "fd20"}; - - llvm::Value* retVal = nullptr; - std::map rets2vals; - std::vector argStores; - std::map> calls2vals; - - for (CallEntry& ce : calls) - { - auto* fnc = ce.call->getFunction(); - auto* calledFnc = ce.call->getCalledFunction(); - - LOG << llvmObjToString(ce.call) << std::endl; - LOG << "\tformat : " << ce.formatStr << std::endl; - - // get lowest stack offset - // - int stackOff = std::numeric_limits::max(); - for (StoreInst* s : ce.possibleArgStores) - { - if (_config->isStackVariable(s->getPointerOperand())) - { - auto so = _config->getStackVariableOffset(s->getPointerOperand()); - if (so < stackOff) - { - stackOff = so; - } - } - } - LOG << "\tlowest : " << std::dec << stackOff << std::endl; - - // - // - auto* wrapCall = isSimpleWrapper(calledFnc); - auto* wrapFnc = wrapCall ? wrapCall->getCalledFunction() : calledFnc; - std::vector ttypes = llvm_utils::parseFormatString( - _module, - ce.formatStr, - wrapFnc); - - if (_config->getConfig().architecture.isPic32()) - { - for (size_t i = 0; i < ttypes.size(); ++i) - { - if (ttypes[i]->isDoubleTy()) - { - ttypes[i] = Type::getFloatTy(_module->getContext()); - } - } - } - - // - // - int off = stackOff; - std::vector args; - - size_t faIdx = 0; - size_t aIdx = 0; - - std::vector types = argTypes; - types.insert(types.end(), ttypes.begin(), ttypes.end()); - - for (Type* t : types) - { - LOG << "\ttype : " << llvmObjToString(t) << std::endl; - int sz = static_cast(_abi->getTypeByteSize(t)); - sz = sz > 4 ? 8 : 4; - - if (_config->getConfig().architecture.isX86()) - { - auto* st = _config->getLlvmStackVariable(fnc, off); - if (st) - { - args.push_back(st); - } - - off += sz; - } - else if (_config->getConfig().architecture.isPpc()) - { - if (aIdx < ppcNames.size()) - { - auto* r = _module->getNamedGlobal(ppcNames[aIdx]); - if (r) - { - args.push_back(r); - } - } - else - { - auto* st = _config->getLlvmStackVariable(fnc, off); - if (st) - { - args.push_back(st); - } - - off += sz; - } - } - else if (_config->getConfig().architecture.isArmOrThumb()) - { - if (aIdx < armNames.size()) - { - auto* r = _module->getNamedGlobal(armNames[aIdx]); - if (r) - { - args.push_back(r); - } - - if (sz > 4) - { - ++aIdx; - } - } - else - { - auto* st = _config->getLlvmStackVariable(fnc, off); - if (st) - { - args.push_back(st); - } - - off += sz; - } - } - else if (_config->getConfig().architecture.isPic32()) - { - if (aIdx < mipsNames.size()) - { - auto* r = _module->getNamedGlobal(mipsNames[aIdx]); - if (r) - { - args.push_back(r); - } - } - else - { - auto* st = _config->getLlvmStackVariable(fnc, off); - if (st) - { - args.push_back(st); - } - - off += sz; - } - } - else if (_config->getConfig().architecture.isMips()) - { - bool useStack = false; - if (t->isFloatingPointTy()) - { - --aIdx; - - if (faIdx < mipsFpNames.size()) - { - auto* r = _module->getNamedGlobal(mipsFpNames[faIdx]); - if (r) - { - args.push_back(r); - } - - if (sz > 4) - { - ++faIdx; - } - } - else - { - useStack = true; - } - - ++faIdx; - } - else - { - if (aIdx < mipsNames.size()) - { - auto* r = _module->getNamedGlobal(mipsNames[aIdx]); - if (r) - { - args.push_back(r); - } - } - else - { - useStack = true; - } - } - - if (useStack) - { - auto* st = _config->getLlvmStackVariable(fnc, off); - if (st) - { - args.push_back(st); - } - - off += sz; - } - } - - ++aIdx; - } - - // - // - unsigned idx = 0; - std::vector loads; - for (auto* a : args) - { - Value* l = new LoadInst(a, "", ce.call); - LOG << "\t\t" << llvmObjToString(l) << std::endl; - - if (types.size() > idx) - { - l = IrModifier::convertValueToType(l, types[idx], ce.call); - } - - loads.push_back(l); - ++idx; - } - - if (!loads.empty()) - { - calls2vals[ce.call] = loads; - } - } - - if (_config->getConfig().architecture.isX86()) - { - if (retType->isFloatingPointTy()) - { - retVal = _config->getLlvmRegister("st7"); - } - else if (_config->getConfig().architecture.isX86_32()) + if (de.getRetValue()) + { + if (de.getRetType() == nullptr) { - retVal = _config->getLlvmRegister("eax"); + if (auto* p = dyn_cast(de.getRetValue()->getType())) + { + de.setRetType(p->getElementType()); + } + else + { + de.setRetType(de.getRetValue()->getType()); + } } - else if (_config->getConfig().architecture.isX86_64()) + + for (auto& e : de.retEntries()) { - retVal = _config->getLlvmRegister("rax"); + auto* l = new LoadInst(de.getRetValue(), "", e.getRetInstruction()); + rets2vals[e.getRetInstruction()] = l; } } - else if (_config->getConfig().architecture.isMipsOrPic32()) - { - retVal = _config->getLlvmRegister("v0"); - } - else if (_config->getConfig().architecture.isArmOrThumb()) - { - retVal = _config->getLlvmRegister("r0"); - } - else if (_config->getConfig().architecture.isPpc()) + else { - retVal = _config->getLlvmRegister("r3"); + de.setRetType(Type::getVoidTy(_module->getContext())); } - if (retVal) + + std::vector definitionArgs; + for (auto& a : de.args()) { - for (auto& e : retStores) + if (a != nullptr) { - auto* l = new LoadInst(retVal, "", e.ret); - rets2vals[e.ret] = l; + definitionArgs.push_back(a); } } - auto* fnc = getFunction(); - auto* oldType = fnc->getType(); - IrModifier irm(_module, _config); auto* newFnc = irm.modifyFunction( fnc, - retType, - argTypes, - isVarArg, + de.getRetType(), + de.argTypes(), + de.isVariadic(), rets2vals, - calls2vals, - retVal, - argStores, - argNames).first; + loadsOfCalls, + de.getRetValue(), + definitionArgs, + de.argNames()).first; - LOG << "modify fnc: " << newFnc->getName().str() << " = " - << llvmObjToString(oldType) << " -> " - << llvmObjToString(newFnc->getType()) << std::endl; - - called = newFnc; + de.setCalledValue(newFnc); } -void DataFlowEntry::connectWrappers() +void ParamReturn::connectWrappers(const DataFlowEntry& de) { - auto* fnc = getFunction(); + auto* fnc = de.getFunction(); + auto* wrappedCall = de.getWrappedCall(); if (fnc == nullptr || wrappedCall == nullptr) { return; @@ -1878,359 +923,54 @@ void DataFlowEntry::connectWrappers() } } -llvm::CallInst* DataFlowEntry::isSimpleWrapper(llvm::Function* fnc) +std::map> ParamReturn::fetchLoadsOfCalls( + const std::vector& calls) const { - auto ai = AsmInstruction(fnc); - if (ai.isInvalid()) - { - return nullptr; - } + std::map> loadsOfCalls; - bool single = true; - auto next = ai.getNext(); - while (next.isValid()) + for (auto& e : calls) { - if (!next.empty() && !isa(next.front())) - { - single = false; - break; - } - next = next.getNext(); - } + std::vector loads; + auto* call = e.getCallInstruction(); - // Pattern - // .text:00008A38 LDR R0, =aCCc ; "C::cc()" - // .text:00008A3C B puts - // .text:00008A40 off_8A40 DCD aCCc - // TODO: make better wrapper detection. In wrapper, wrapped function params - // should not be set like in this example. - // - if (ai && next) - { - if (_image->getConstantDefault(next.getEndAddress())) - { - auto* l = ai.getInstructionFirst(); - auto* s = ai.getInstructionFirst(); - auto* c = next.getInstructionFirst(); - if (l && s && c && isa(l->getPointerOperand()) - && s->getPointerOperand()->getName() == "r0") - { - auto gvA = _config->getGlobalAddress(cast(l->getPointerOperand())); - if (gvA == next.getEndAddress()) - { - return nullptr; - } - } - } - } + auto types = e.getBaseFunction()->argTypes(); + types.insert( + types.end(), + e.argTypes().begin(), + e.argTypes().end()); - if (single) - { - for (auto& i : ai) - { - if (auto* c = dyn_cast(&i)) - { - auto* cf = c->getCalledFunction(); - if (cf && !cf->isIntrinsic()) // && cf->isDeclaration()) - { - return c; - } - } - } - } + auto tIt = types.begin(); + auto aIt = e.args().begin(); - unsigned aiNum = 0; - bool isSmall = true; - next = ai; - while (next.isValid()) - { - ++aiNum; - next = next.getNext(); - if (aiNum > 4) - { - isSmall = false; - break; - } - } - auto* s = _image->getImage()->getSegmentFromAddress(ai.getAddress()); - if ((s && s->getName() == ".plt") || isSmall) - { - for (inst_iterator it = inst_begin(fnc), rIt = inst_end(fnc); - it != rIt; ++it) + while (aIt != e.args().end()) { - if (auto* l = dyn_cast(&*it)) - { - std::string n = l->getPointerOperand()->getName(); - if (n == "lr" || n == "sp") - { - return nullptr; - } - } - else if (auto* s = dyn_cast(&*it)) - { - std::string n = s->getPointerOperand()->getName(); - if (n == "lr" || n == "sp") - { - return nullptr; - } - } - else if (auto* c = dyn_cast(&*it)) + if (*aIt == nullptr) { - auto* cf = c->getCalledFunction(); - if (cf && !cf->isIntrinsic() && cf->isDeclaration()) - { - return c; - } + aIt++; + continue; } - } - } - - return nullptr; -} - -void DataFlowEntry::setTypeFromExtraInfo() -{ - auto* fnc = getFunction(); - if (fnc == nullptr) - { - return; - } - // Main - // - if (fnc->getName() == "main") - { - argTypes.push_back(Type::getInt32Ty(_module->getContext())); - argTypes.push_back(PointerType::get( - PointerType::get( - Type::getInt8Ty(_module->getContext()), - 0), - 0)); - argNames.push_back("argc"); - argNames.push_back("argv"); - retType = Type::getInt32Ty(_module->getContext()); - typeSet = true; - return; - } + Value* l = new LoadInst(*aIt, "", call); - // LTI info. - // - auto* cf = _config->getConfigFunction(fnc); - if (cf && (cf->isDynamicallyLinked() || cf->isStaticallyLinked())) - { - auto fp = _lti->getPairFunctionFree(cf->getName()); - if (fp.first) - { - for (auto& a : fp.first->args()) - { - argTypes.push_back(a.getType()); - argNames.push_back(a.getName()); - } - if (fp.first->isVarArg()) + if (tIt != types.end()) { - isVarArg = true; + l = IrModifier::convertValueToType(l, *tIt, call); + tIt++; } - retType = fp.first->getReturnType(); - typeSet = true; - - std::string declr = fp.second->getDeclaration(); - if (!declr.empty()) + else { - cf->setDeclarationString(declr); + l = IrModifier::convertValueToType(l, _abi->getDefaultType(), call); } - // TODO: we could rename function if LTI name differs. - // e.g. scanf vs _scanf. - // -// if (fp.first->getName() != fnc->getName()) -// { -// IrModifier irmodif(_module, _config); -// irmodif.renameFunction(fnc, fp.first->getName()); -// } - - return; - } - } - - // Debug info. - // - if (dbgFnc) - { - for (auto& a : dbgFnc->parameters) - { - auto* t = llvm_utils::stringToLlvmTypeDefault(_module, a.type.getLlvmIr()); - argTypes.push_back(t); - argNames.push_back(a.getName()); - } - if (dbgFnc->isVariadic()) - { - isVarArg = true; - } - retType = llvm_utils::stringToLlvmTypeDefault( - _module, - dbgFnc->returnType.getLlvmIr()); - typeSet = true; - return; - } - - if (_config->getConfig().isIda() && configFnc) - { - for (auto& a : configFnc->parameters) - { - auto* t = llvm_utils::stringToLlvmTypeDefault(_module, a.type.getLlvmIr()); - argTypes.push_back(t); - argNames.push_back(a.getName()); - - if (_config->getConfig().architecture.isX86()) - { - std::string regName; - if (a.getStorage().isRegister(regName)) - { - if (auto* reg = _config->getLlvmRegister(regName)) - { - specialArgStorage[argTypes.size()-1] = reg; - } - } - } - } - if (configFnc->isVariadic()) - { - isVarArg = true; - } - retType = llvm_utils::stringToLlvmTypeDefault( - _module, - configFnc->returnType.getLlvmIr()); - if (!argTypes.empty()) - { - typeSet = true; + loads.push_back(l); + aIt++; } - return; - } - // Wrappers. - // - if ((wrappedCall = isSimpleWrapper(fnc))) - { - auto* wf = wrappedCall->getCalledFunction(); - auto* ltiFnc = _lti->getLlvmFunctionFree(wf->getName()); - if (ltiFnc) - { - for (auto& a : ltiFnc->args()) - { - argTypes.push_back(a.getType()); - argNames.push_back(a.getName()); - } - if (ltiFnc->isVarArg()) - { - isVarArg = true; - } - retType = ltiFnc->getReturnType(); - typeSet = true; - return; - } - else - { - wrappedCall = nullptr; - } + loadsOfCalls[call] = std::move(loads); } -} -void DataFlowEntry::setTypeFromUseContext() -{ - if (!typeSet) - { - setReturnType(); - setArgumentTypes(); - typeSet = true; - } + return loadsOfCalls; } -void DataFlowEntry::setReturnType() -{ - llvm::Value* retVal = nullptr; - if (_config->getConfig().architecture.isX86()) - { - bool hasEax = false; - bool hasRax = false; - bool hasSt0 = false; - for (auto& re : retStores) - { - for (StoreInst* s : re.possibleRetStores) - { - if (s->getPointerOperand()->getName() == "eax") - { - hasEax = true; - break; - } - else if (s->getPointerOperand()->getName() == "rax") - { - hasRax = true; - break; - } - else if (s->getPointerOperand()->getName() == "st7") - { - hasSt0 = true; - } - } - } - if (!hasEax && !hasRax && hasSt0) - { - retVal = _config->getLlvmRegister("st7"); - } - else if (_config->getLlvmRegister("rax")) - { - retVal = _config->getLlvmRegister("rax"); - } - else - { - retVal = _config->getLlvmRegister("eax"); - } - } - else if (_config->getConfig().architecture.isMipsOrPic32()) - { - retVal = _config->getLlvmRegister("v0"); - } - else if (_config->getConfig().architecture.isArmOrThumb()) - { - retVal = _config->getLlvmRegister("r0"); - } - else if (_config->getConfig().architecture.isPpc()) - { - retVal = _config->getLlvmRegister("r3"); - } - - retType = retVal ? - retVal->getType()->getPointerElementType() : - Type::getVoidTy(_module->getContext()); } - -void DataFlowEntry::setArgumentTypes() -{ - if (calls.empty()) - { - argTypes.insert( - argTypes.end(), - argLoads.size(), - Abi::getDefaultType(_module)); - } - else - { - CallEntry* ce = &calls.front(); - for (auto& c : calls) - { - if (!c.possibleArgStores.empty()) - { - ce = &c; - break; - } - } - - argTypes.insert( - argTypes.end(), - ce->possibleArgStores.size(), - Abi::getDefaultType(_module)); - } } - -} // namespace bin2llvmir -} // namespace retdec diff --git a/src/bin2llvmir/providers/abi/abi.cpp b/src/bin2llvmir/providers/abi/abi.cpp index e27d69fe7..84f84e93f 100644 --- a/src/bin2llvmir/providers/abi/abi.cpp +++ b/src/bin2llvmir/providers/abi/abi.cpp @@ -7,8 +7,11 @@ #include "retdec/bin2llvmir/providers/abi/abi.h" #include "retdec/bin2llvmir/providers/abi/arm.h" #include "retdec/bin2llvmir/providers/abi/mips.h" +#include "retdec/bin2llvmir/providers/abi/ms_x64.h" #include "retdec/bin2llvmir/providers/abi/powerpc.h" #include "retdec/bin2llvmir/providers/abi/x86.h" +#include "retdec/bin2llvmir/providers/abi/x64.h" +#include "retdec/bin2llvmir/providers/abi/pic32.h" using namespace llvm; @@ -36,12 +39,12 @@ Abi::~Abi() } -bool Abi::isRegister(const llvm::Value* val) +bool Abi::isRegister(const llvm::Value* val) const { return _regs2id.count(val); } -bool Abi::isRegister(const llvm::Value* val, uint32_t r) +bool Abi::isRegister(const llvm::Value* val, uint32_t r) const { return getRegister(r) == val; } @@ -52,7 +55,7 @@ bool Abi::isFlagRegister(const llvm::Value* val) && val->getType()->getPointerElementType()->isIntegerTy(1); } -bool Abi::isStackPointerRegister(const llvm::Value* val) +bool Abi::isStackPointerRegister(const llvm::Value* val) const { return getStackPointerRegister() == val; } @@ -70,7 +73,7 @@ bool Abi::isZeroRegister(const llvm::Value* val) * This solves the problem with overlapping IDs when used like this: * Abi::getRegister(MIPS_REG_GP, Abi::isMips()) */ -llvm::GlobalVariable* Abi::getRegister(uint32_t r, bool use) +llvm::GlobalVariable* Abi::getRegister(uint32_t r, bool use) const { if (!use) { @@ -80,7 +83,7 @@ llvm::GlobalVariable* Abi::getRegister(uint32_t r, bool use) return _id2regs[r]; } -uint32_t Abi::getRegisterId(const llvm::Value* r) +uint32_t Abi::getRegisterId(const llvm::Value* r) const { auto it = _regs2id.find(r); return it != _regs2id.end() ? it->second : Abi::REG_INVALID; @@ -91,16 +94,29 @@ const std::vector& Abi::getRegisters() const return _regs; } -llvm::GlobalVariable* Abi::getStackPointerRegister() +llvm::GlobalVariable* Abi::getStackPointerRegister() const { return getRegister(_regStackPointerId); } -llvm::GlobalVariable* Abi::getZeroRegister() +llvm::GlobalVariable* Abi::getZeroRegister() const { return getRegister(_regZeroReg); } +std::size_t Abi::getRegisterByteSize(uint32_t reg) const +{ + auto r = getRegister(reg); + assert(r); + + if (auto* p = dyn_cast(r->getType())) + { + return getTypeByteSize(p->getElementType()); + } + + return getTypeByteSize(r->getType()); +} + void Abi::addRegister(uint32_t id, llvm::GlobalVariable* reg) { if (id >= _id2regs.size()) @@ -127,6 +143,11 @@ llvm::GlobalVariable* Abi::getSyscallArgumentRegister(unsigned n) return n < _syscallRegs.size() ? getRegister(_syscallRegs[n]) : nullptr; } +bool Abi::isStackVariable(const Value* val) const +{ + return _config->isStackVariable(val); +} + bool Abi::isNopInstruction(AsmInstruction ai) { return isNopInstruction(ai.getCapstoneInsn()); @@ -152,15 +173,24 @@ llvm::PointerType* Abi::getDefaultPointerType() const return Abi::getDefaultPointerType(_module); } +std::size_t Abi::getWordSize() const +{ + return _config->getConfig().architecture.getBitSize() / 8; +} + std::size_t Abi::getTypeByteSize(llvm::Module* m, llvm::Type* t) { assert(m); + assert(t->isSized()); + return m->getDataLayout().getTypeStoreSize(t); } std::size_t Abi::getTypeBitSize(llvm::Module* m, llvm::Type* t) { assert(m); + assert(t->isSized()); + return m->getDataLayout().getTypeSizeInBits(t); } @@ -171,32 +201,90 @@ llvm::IntegerType* Abi::getDefaultType(llvm::Module* m) return Type::getIntNTy(m->getContext(), s); } +llvm::Type* Abi::getDefaultFPType(llvm::Module* m) +{ + assert(m); + return Type::getFloatTy(m->getContext()); +} + llvm::PointerType* Abi::getDefaultPointerType(llvm::Module* m) { assert(m); return PointerType::get(Abi::getDefaultType(m), 0); } +std::size_t Abi::getWordSize(llvm::Module* m) +{ + return m->getDataLayout().getPointerSize(0); +} + bool Abi::isMips() const { return _config->getConfig().architecture.isMipsOrPic32(); } +bool Abi::isMips64() const +{ + return _config->getConfig().architecture.isMips64(); +} + bool Abi::isArm() const { return _config->getConfig().architecture.isArmOrThumb(); } +bool Abi::isArm64() const +{ + return _config->getConfig().architecture.isArm64(); +} + bool Abi::isX86() const { return _config->getConfig().architecture.isX86(); } +bool Abi::isX64() const +{ + return _config->getConfig().architecture.isX86_64(); +} + bool Abi::isPowerPC() const { return _config->getConfig().architecture.isPpc(); } +bool Abi::isPowerPC64() const +{ + return _config->getConfig().architecture.isPpc64(); +} + +bool Abi::isPic32() const +{ + return _config->getConfig().architecture.isPic32(); +} + +CallingConvention* Abi::getDefaultCallingConvention() +{ + return getCallingConvention(_defcc); +} + +CallingConvention* Abi::getCallingConvention( + const CallingConvention::ID& cc) +{ + if (_id2cc.find(cc) == _id2cc.end()) + { + auto provider = CallingConventionProvider::getProvider(); + _id2cc[cc] = provider->createCallingConvention(cc, this); + } + + return _id2cc[cc].get(); +} + +Config* Abi::getConfig() const +{ + return _config; +} + // //============================================================================== // AbiProvider @@ -219,16 +307,35 @@ Abi* AbiProvider::addAbi( auto p = _module2abi.emplace(m, std::make_unique(m, c)); return p.first->second.get(); } - else if (c->getConfig().architecture.isMipsOrPic32()) + else if (c->getConfig().architecture.isMips()) { auto p = _module2abi.emplace(m, std::make_unique(m, c)); return p.first->second.get(); } + else if (c->getConfig().architecture.isPic32()) + { + auto p = _module2abi.emplace(m, std::make_unique(m, c)); + return p.first->second.get(); + } else if (c->getConfig().architecture.isPpc()) { auto p = _module2abi.emplace(m, std::make_unique(m, c)); return p.first->second.get(); } + else if (c->getConfig().architecture.isX86_64()) + { + bool isMinGW = c->getConfig().tools.isGcc() + && c->getConfig().fileFormat.isPe(); + + if (isMinGW || c->getConfig().tools.isMsvc()) + { + auto p = _module2abi.emplace(m, std::make_unique(m, c)); + return p.first->second.get(); + } + + auto p = _module2abi.emplace(m, std::make_unique(m, c)); + return p.first->second.get(); + } else if (c->getConfig().architecture.isX86()) { auto p = _module2abi.emplace(m, std::make_unique(m, c)); diff --git a/src/bin2llvmir/providers/abi/arm.cpp b/src/bin2llvmir/providers/abi/arm.cpp index 70d3284a3..eee6a7a5a 100644 --- a/src/bin2llvmir/providers/abi/arm.cpp +++ b/src/bin2llvmir/providers/abi/arm.cpp @@ -28,6 +28,8 @@ AbiArm::AbiArm(llvm::Module* m, Config* c) : ARM_REG_R3, ARM_REG_R4, ARM_REG_R5}; + + _defcc = CallingConvention::ID::CC_ARM; } AbiArm::~AbiArm() @@ -35,7 +37,7 @@ AbiArm::~AbiArm() } -bool AbiArm::isGeneralPurposeRegister(const llvm::Value* val) +bool AbiArm::isGeneralPurposeRegister(const llvm::Value* val) const { uint32_t rid = getRegisterId(val); return ARM_REG_R0 <= rid && rid <= ARM_REG_R12; diff --git a/src/bin2llvmir/providers/abi/arm64.cpp b/src/bin2llvmir/providers/abi/arm64.cpp new file mode 100644 index 000000000..d9be3ba84 --- /dev/null +++ b/src/bin2llvmir/providers/abi/arm64.cpp @@ -0,0 +1,59 @@ +/** + * @file src/bin2llvmir/providers/abi/arm64.cpp + * @brief ABI information for ARM64. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/arm64.h" + +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +AbiArm64::AbiArm64(llvm::Module* m, Config* c) : + Abi(m, c) +{ + _regs.reserve(ARM64_REG_ENDING); + _id2regs.resize(ARM64_REG_ENDING, nullptr); + _regStackPointerId = ARM64_REG_SP; + + // system calls + _regSyscallId = ARM64_REG_X7; + _regSyscallReturn = ARM64_REG_X0; + _syscallRegs = { + ARM64_REG_X0, + ARM64_REG_X1, + ARM64_REG_X2, + ARM64_REG_X3, + ARM64_REG_X4, + ARM64_REG_X5}; + + _defcc = CallingConvention::ID::CC_ARM64; +} + +AbiArm64::~AbiArm64() +{ + +} + +bool AbiArm64::isGeneralPurposeRegister(const llvm::Value* val) const +{ + uint32_t rid = getRegisterId(val); + return ARM64_REG_X0 <= rid && rid <= ARM64_REG_X30; +} + +bool AbiArm64::isNopInstruction(cs_insn* insn) +{ + // True NOP variants. + // + if (insn->id == ARM64_INS_NOP) + { + return true; + } + + return false; +} + +} // namespace bin2llvmir +} // namespace retdec diff --git a/src/bin2llvmir/providers/abi/mips.cpp b/src/bin2llvmir/providers/abi/mips.cpp index bd2abc5b0..c29fc3184 100644 --- a/src/bin2llvmir/providers/abi/mips.cpp +++ b/src/bin2llvmir/providers/abi/mips.cpp @@ -27,6 +27,8 @@ AbiMips::AbiMips(llvm::Module* m, Config* c) : MIPS_REG_A1, MIPS_REG_A2, MIPS_REG_A3}; + + _defcc = CallingConvention::ID::CC_MIPS; } AbiMips::~AbiMips() @@ -34,7 +36,7 @@ AbiMips::~AbiMips() } -bool AbiMips::isGeneralPurposeRegister(const llvm::Value* val) +bool AbiMips::isGeneralPurposeRegister(const llvm::Value* val) const { uint32_t rid = getRegisterId(val); return MIPS_REG_0 <= rid && rid <= MIPS_REG_31; diff --git a/src/bin2llvmir/providers/abi/mips64.cpp b/src/bin2llvmir/providers/abi/mips64.cpp new file mode 100644 index 000000000..cc2edb2a7 --- /dev/null +++ b/src/bin2llvmir/providers/abi/mips64.cpp @@ -0,0 +1,63 @@ +/** + * @file src/bin2llvmir/providers/abi/mips64.cpp + * @brief ABI information for MIPS. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/mips64.h" + +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +AbiMips64::AbiMips64(llvm::Module* m, Config* c) : + Abi(m, c) +{ + _regs.reserve(MIPS_REG_ENDING); + _id2regs.resize(MIPS_REG_ENDING, nullptr); + _regStackPointerId = MIPS_REG_SP; + _regZeroReg = MIPS_REG_ZERO; + + // system calls + _regSyscallId = MIPS_REG_V0; + _regSyscallReturn = MIPS_REG_V0; + _syscallRegs = { + MIPS_REG_A0, + MIPS_REG_A1, + MIPS_REG_A2, + MIPS_REG_A3, + MIPS_REG_T0, + MIPS_REG_T1, + MIPS_REG_T2, + MIPS_REG_T3}; + + _defcc = CallingConvention::ID::CC_MIPS64; +} + +AbiMips64::~AbiMips64() +{ + +} + +bool AbiMips64::isGeneralPurposeRegister(const llvm::Value* val) const +{ + uint32_t rid = getRegisterId(val); + return MIPS_REG_0 <= rid && rid <= MIPS_REG_31; +} + +bool AbiMips64::isNopInstruction(cs_insn* insn) +{ + // True NOP variants. + // + if (insn->id == MIPS_INS_NOP + || insn->id == MIPS_INS_SSNOP) + { + return true; + } + + return false; +} + +} // namespace bin2llvmir +} // namespace retdec diff --git a/src/bin2llvmir/providers/abi/ms_x64.cpp b/src/bin2llvmir/providers/abi/ms_x64.cpp new file mode 100644 index 000000000..f943bc5da --- /dev/null +++ b/src/bin2llvmir/providers/abi/ms_x64.cpp @@ -0,0 +1,105 @@ +/** + * @file src/bin2llvmir/providers/abi/ms_x64.cpp + * @brief ABI information for x86_64. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/ms_x64.h" + +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +AbiMS_X64::AbiMS_X64(llvm::Module* m, Config* c) : + Abi(m, c) +{ + _regs.reserve(X86_REG_ENDING); + _id2regs.resize(X86_REG_ENDING, nullptr); + _regStackPointerId = X86_REG_RSP; + + // system calls + _regSyscallId = X86_REG_EAX; + _regSyscallReturn = X86_REG_EAX; + _syscallRegs = { + X86_REG_RDI, + X86_REG_RSI, + X86_REG_RDX, + X86_REG_R10, + X86_REG_R8, + X86_REG_R9}; + + _defcc = CallingConvention::ID::CC_X64; +} + +AbiMS_X64::~AbiMS_X64() +{ +} + +bool AbiMS_X64::isGeneralPurposeRegister(const llvm::Value* val) const +{ + uint32_t rid = getRegisterId(val); + return rid == X86_REG_RAX + || rid == X86_REG_RBX + || rid == X86_REG_RCX + || rid == X86_REG_RDX + || rid == X86_REG_RSP + || rid == X86_REG_RBP + || rid == X86_REG_RSI + || rid == X86_REG_RDI + || rid == X86_REG_R8 + || rid == X86_REG_R9 + || rid == X86_REG_R10 + || rid == X86_REG_R11 + || rid == X86_REG_R12 + || rid == X86_REG_R13 + || rid == X86_REG_R14 + || rid == X86_REG_R15; +} + +bool AbiMS_X64::isNopInstruction(cs_insn* insn) +{ + cs_x86& insn86 = insn->detail->x86; + + // True NOP variants. + // + if (insn->id == X86_INS_NOP + || insn->id == X86_INS_FNOP + || insn->id == X86_INS_FDISI8087_NOP + || insn->id == X86_INS_FENI8087_NOP + || insn->id == X86_INS_INT3) + { + return true; + } + // e.g. lea esi, [esi] + // + else if (insn->id == X86_INS_LEA + && insn86.disp == 0 + && insn86.op_count == 2 + && insn86.operands[0].type == X86_OP_REG + && insn86.operands[1].type == X86_OP_MEM + && insn86.operands[1].mem.segment == X86_REG_INVALID + && insn86.operands[1].mem.index == X86_REG_INVALID + && insn86.operands[1].mem.scale == 1 + && insn86.operands[1].mem.disp == 0 + && insn86.operands[1].mem.base == insn86.operands[0].reg) + { + return true; + } + // e.g. mov esi. esi + // + else if (insn->id == X86_INS_MOV + && insn86.disp == 0 + && insn86.op_count == 2 + && insn86.operands[0].type == X86_OP_REG + && insn86.operands[1].type == X86_OP_REG + && insn86.operands[0].reg == insn86.operands[1].reg) + { + return true; + } + + return false; +} + +} // namespace bin2llvmir +} // namespace retdec diff --git a/src/bin2llvmir/providers/abi/pic32.cpp b/src/bin2llvmir/providers/abi/pic32.cpp new file mode 100644 index 000000000..ff629e090 --- /dev/null +++ b/src/bin2llvmir/providers/abi/pic32.cpp @@ -0,0 +1,79 @@ +/** + * @file src/bin2llvmir/providers/abi/pic32.cpp + * @brief ABI information for MIPS. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/pic32.h" + +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +AbiPic32::AbiPic32(llvm::Module* m, Config* c) : + Abi(m, c) +{ + _regs.reserve(MIPS_REG_ENDING); + _id2regs.resize(MIPS_REG_ENDING, nullptr); + _regStackPointerId = MIPS_REG_SP; + _regZeroReg = MIPS_REG_ZERO; + + // system calls + _regSyscallId = MIPS_REG_V0; + _regSyscallReturn = MIPS_REG_V0; + _syscallRegs = { + MIPS_REG_A0, + MIPS_REG_A1, + MIPS_REG_A2, + MIPS_REG_A3}; + + _defcc = CallingConvention::ID::CC_PIC32; +} + +AbiPic32::~AbiPic32() +{ + +} + +bool AbiPic32::isGeneralPurposeRegister(const llvm::Value* val) const +{ + uint32_t rid = getRegisterId(val); + return MIPS_REG_0 <= rid && rid <= MIPS_REG_31; +} + +bool AbiPic32::isNopInstruction(cs_insn* insn) +{ + // True NOP variants. + // + if (insn->id == MIPS_INS_NOP + || insn->id == MIPS_INS_SSNOP) + { + return true; + } + + return false; +} + +std::size_t AbiPic32::getTypeByteSize(llvm::Type* t) const +{ + if (t->isDoubleTy()) + { + t = Type::getFloatTy(_module->getContext()); + } + + return Abi::getTypeByteSize(t); +} + +std::size_t AbiPic32::getTypeBitSize(llvm::Type* t) const +{ + if (t->isDoubleTy()) + { + t = Type::getFloatTy(_module->getContext()); + } + + return Abi::getTypeByteSize(t); +} + +} // namespace bin2llvmir +} // namespace retdec diff --git a/src/bin2llvmir/providers/abi/powerpc.cpp b/src/bin2llvmir/providers/abi/powerpc.cpp index 92cbae6d8..e6983d27d 100644 --- a/src/bin2llvmir/providers/abi/powerpc.cpp +++ b/src/bin2llvmir/providers/abi/powerpc.cpp @@ -17,6 +17,8 @@ AbiPowerpc::AbiPowerpc(llvm::Module* m, Config* c) : _regs.reserve(PPC_REG_ENDING); _id2regs.resize(PPC_REG_ENDING, nullptr); _regStackPointerId = PPC_REG_R1; + + _defcc = CallingConvention::ID::CC_POWERPC; } AbiPowerpc::~AbiPowerpc() @@ -24,7 +26,7 @@ AbiPowerpc::~AbiPowerpc() } -bool AbiPowerpc::isGeneralPurposeRegister(const llvm::Value* val) +bool AbiPowerpc::isGeneralPurposeRegister(const llvm::Value* val) const { uint32_t rid = getRegisterId(val); return PPC_REG_R0 <= rid && rid <= PPC_REG_R31; diff --git a/src/bin2llvmir/providers/abi/powerpc64.cpp b/src/bin2llvmir/providers/abi/powerpc64.cpp new file mode 100644 index 000000000..662114db6 --- /dev/null +++ b/src/bin2llvmir/providers/abi/powerpc64.cpp @@ -0,0 +1,49 @@ +/** + * @file src/bin2llvmir/providers/abi/powerpc64.cpp + * @brief ABI information for PowerPC 64. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/powerpc64.h" + +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +AbiPowerpc64::AbiPowerpc64(llvm::Module* m, Config* c) : + Abi(m, c) +{ + _regs.reserve(PPC_REG_ENDING); + _id2regs.resize(PPC_REG_ENDING, nullptr); + _regStackPointerId = PPC_REG_R1; + + _defcc = CallingConvention::ID::CC_POWERPC64; +} + +AbiPowerpc64::~AbiPowerpc64() +{ + +} + +bool AbiPowerpc64::isGeneralPurposeRegister(const llvm::Value* val) const +{ + uint32_t rid = getRegisterId(val); + return PPC_REG_R0 <= rid && rid <= PPC_REG_R31; +} + +bool AbiPowerpc64::isNopInstruction(cs_insn* insn) +{ + // True NOP variants. + // + if (insn->id == PPC_INS_NOP + || insn->id == PPC_INS_XNOP) + { + return true; + } + + return false; +} + +} // namespace bin2llvmir +} // namespace retdec diff --git a/src/bin2llvmir/providers/abi/x64.cpp b/src/bin2llvmir/providers/abi/x64.cpp new file mode 100644 index 000000000..304c20aa2 --- /dev/null +++ b/src/bin2llvmir/providers/abi/x64.cpp @@ -0,0 +1,105 @@ +/** + * @file src/bin2llvmir/providers/abi/x64.cpp + * @brief ABI information for x86_64. + * @copyright (c) 2017 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/x64.h" + +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +AbiX64::AbiX64(llvm::Module* m, Config* c) : + Abi(m, c) +{ + _regs.reserve(X86_REG_ENDING); + _id2regs.resize(X86_REG_ENDING, nullptr); + _regStackPointerId = X86_REG_RSP; + + // system calls + _regSyscallId = X86_REG_EAX; + _regSyscallReturn = X86_REG_EAX; + _syscallRegs = { + X86_REG_RDI, + X86_REG_RSI, + X86_REG_RDX, + X86_REG_R10, + X86_REG_R8, + X86_REG_R9}; + + _defcc = CallingConvention::ID::CC_X64; +} + +AbiX64::~AbiX64() +{ +} + +bool AbiX64::isGeneralPurposeRegister(const llvm::Value* val) const +{ + uint32_t rid = getRegisterId(val); + return rid == X86_REG_RAX + || rid == X86_REG_RBX + || rid == X86_REG_RCX + || rid == X86_REG_RDX + || rid == X86_REG_RSP + || rid == X86_REG_RBP + || rid == X86_REG_RSI + || rid == X86_REG_RDI + || rid == X86_REG_R8 + || rid == X86_REG_R9 + || rid == X86_REG_R10 + || rid == X86_REG_R11 + || rid == X86_REG_R12 + || rid == X86_REG_R13 + || rid == X86_REG_R14 + || rid == X86_REG_R15; +} + +bool AbiX64::isNopInstruction(cs_insn* insn) +{ + cs_x86& insn86 = insn->detail->x86; + + // True NOP variants. + // + if (insn->id == X86_INS_NOP + || insn->id == X86_INS_FNOP + || insn->id == X86_INS_FDISI8087_NOP + || insn->id == X86_INS_FENI8087_NOP + || insn->id == X86_INS_INT3) + { + return true; + } + // e.g. lea esi, [esi] + // + else if (insn->id == X86_INS_LEA + && insn86.disp == 0 + && insn86.op_count == 2 + && insn86.operands[0].type == X86_OP_REG + && insn86.operands[1].type == X86_OP_MEM + && insn86.operands[1].mem.segment == X86_REG_INVALID + && insn86.operands[1].mem.index == X86_REG_INVALID + && insn86.operands[1].mem.scale == 1 + && insn86.operands[1].mem.disp == 0 + && insn86.operands[1].mem.base == insn86.operands[0].reg) + { + return true; + } + // e.g. mov esi. esi + // + else if (insn->id == X86_INS_MOV + && insn86.disp == 0 + && insn86.op_count == 2 + && insn86.operands[0].type == X86_OP_REG + && insn86.operands[1].type == X86_OP_REG + && insn86.operands[0].reg == insn86.operands[1].reg) + { + return true; + } + + return false; +} + +} // namespace bin2llvmir +} // namespace retdec diff --git a/src/bin2llvmir/providers/abi/x86.cpp b/src/bin2llvmir/providers/abi/x86.cpp index d1dde441a..361137b63 100644 --- a/src/bin2llvmir/providers/abi/x86.cpp +++ b/src/bin2llvmir/providers/abi/x86.cpp @@ -28,6 +28,8 @@ AbiX86::AbiX86(llvm::Module* m, Config* c) : X86_REG_ESI, X86_REG_EDI, X86_REG_EBP}; + + _defcc = fetchDefaultCC(); } AbiX86::~AbiX86() @@ -35,7 +37,7 @@ AbiX86::~AbiX86() } -bool AbiX86::isGeneralPurposeRegister(const llvm::Value* val) +bool AbiX86::isGeneralPurposeRegister(const llvm::Value* val) const { uint32_t rid = getRegisterId(val); return rid == X86_REG_EAX @@ -92,5 +94,19 @@ bool AbiX86::isNopInstruction(cs_insn* insn) return false; } +CallingConvention::ID AbiX86::fetchDefaultCC() const +{ + if (_config->getConfig().tools.isWatcom()) + { + return CallingConvention::ID::CC_WATCOM; + } + if (_config->getConfig().tools.isBorland()) + { + return CallingConvention::ID::CC_PASCAL; + } + + return CallingConvention::ID::CC_CDECL; +} + } // namespace bin2llvmir } // namespace retdec diff --git a/src/bin2llvmir/providers/calling_convention/arm/arm_conv.cpp b/src/bin2llvmir/providers/calling_convention/arm/arm_conv.cpp new file mode 100644 index 000000000..4e1ce53dc --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/arm/arm_conv.cpp @@ -0,0 +1,49 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/arm/arm_conv.cpp + * @brief Calling convention of ARM architecture. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/arm/arm_conv.h" +#include "retdec/capstone2llvmir/arm/arm.h" + +namespace retdec { +namespace bin2llvmir { + +ArmCallingConvention::ArmCallingConvention(const Abi* a) : + CallingConvention(a) +{ + _paramRegs = { + ARM_REG_R0, + ARM_REG_R1, + ARM_REG_R2, + ARM_REG_R3}; + + _returnRegs = { + ARM_REG_R0, + ARM_REG_R1}; + + _largeObjectsPassedByReference = true; +// _respectsRegCouples = true; + _numOfRegsPerParam = 2; + _numOfFPRegsPerParam = 2; + _numOfVectorRegsPerParam = 4; +} + +ArmCallingConvention::~ArmCallingConvention() +{ +} + +CallingConvention::Ptr ArmCallingConvention::create(const Abi* a) +{ + if (!a->isArm()) + { + return nullptr; + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/arm64/arm64_conv.cpp b/src/bin2llvmir/providers/calling_convention/arm64/arm64_conv.cpp new file mode 100644 index 000000000..79a5af180 --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/arm64/arm64_conv.cpp @@ -0,0 +1,78 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/arm64/arm64_conv.cpp + * @brief Calling conventions of ARM64 architecture. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/arm64/arm64_conv.h" + +namespace retdec { +namespace bin2llvmir { + +Arm64CallingConvention::Arm64CallingConvention(const Abi* a) : + CallingConvention(a) +{ + _paramRegs = { + ARM64_REG_X0, + ARM64_REG_X1, + ARM64_REG_X2, + ARM64_REG_X3, + ARM64_REG_X4, + ARM64_REG_X5, + ARM64_REG_X6, + ARM64_REG_X7 + }; + _paramFPRegs = { + ARM64_REG_V0, + ARM64_REG_V1, + ARM64_REG_V2, + ARM64_REG_V3, + ARM64_REG_V4, + ARM64_REG_V5, + ARM64_REG_V6, + ARM64_REG_V7 + }; + + _paramVectorRegs = { + ARM64_REG_V0, + ARM64_REG_V1, + ARM64_REG_V2, + ARM64_REG_V3, + ARM64_REG_V4, + ARM64_REG_V5, + ARM64_REG_V6, + ARM64_REG_V7 + }; + + _returnRegs = { + ARM64_REG_X0 + }; + + _returnFPRegs = { + ARM64_REG_V0 + }; + + _largeObjectsPassedByReference = true; + _respectsRegCouples = true; + _numOfRegsPerParam = 2; +} + +Arm64CallingConvention::~Arm64CallingConvention() +{ +} + +CallingConvention::Ptr Arm64CallingConvention::create(const Abi* a) +{ + if (!a->isArm64()) + { + return nullptr; + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/calling_convention.cpp b/src/bin2llvmir/providers/calling_convention/calling_convention.cpp new file mode 100644 index 000000000..cd3e882ee --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/calling_convention.cpp @@ -0,0 +1,234 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/calling_convention.cpp + * @brief Calling convention information. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/calling_convention.h" +#include "retdec/bin2llvmir/providers/calling_convention/arm/arm_conv.h" +#include "retdec/bin2llvmir/providers/calling_convention/arm64/arm64_conv.h" +#include "retdec/bin2llvmir/providers/calling_convention/mips/mips_conv.h" +#include "retdec/bin2llvmir/providers/calling_convention/mips64/mips64_conv.h" +#include "retdec/bin2llvmir/providers/calling_convention/pic32/pic32_conv.h" +#include "retdec/bin2llvmir/providers/calling_convention/powerpc/powerpc_conv.h" +#include "retdec/bin2llvmir/providers/calling_convention/powerpc64/powerpc64_conv.h" +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_cdecl.h" +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_fastcall.h" +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_pascal.h" +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_thiscall.h" +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_watcom.h" +#include "retdec/bin2llvmir/providers/calling_convention/x64/x64_conv.h" + +using namespace llvm; + +namespace retdec { +namespace bin2llvmir { + +// +//============================================================================== +// CallingConvention +//============================================================================== +// + +const bool CallingConvention::RTL = true; +const bool CallingConvention::LTR = false; + +CallingConvention::CallingConvention( + const Abi* abi) : + _abi(abi) +{ +} + +CallingConvention::~CallingConvention() +{ +} + +const std::vector& CallingConvention::getParamRegisters() const +{ + return _paramRegs; +} + +const std::vector& CallingConvention::getParamFPRegisters() const +{ + return _paramFPRegs; +} + +const std::vector& CallingConvention::getParamDoubleRegisters() const +{ + return _paramDoubleRegs; +} + +const std::vector& CallingConvention::getParamVectorRegisters() const +{ + return _paramVectorRegs; +} + +const std::vector& CallingConvention::getReturnRegisters() const +{ + return _returnRegs; +} + +const std::vector& CallingConvention::getReturnFPRegisters() const +{ + return _returnFPRegs; +} + +const std::vector& CallingConvention::getReturnDoubleRegisters() const +{ + return _returnDoubleRegs; +} + +const std::vector& CallingConvention::getReturnVectorRegisters() const +{ + return _returnVectorRegs; +} + +bool CallingConvention::usesFPRegistersForParameters() const +{ + return !(_paramFPRegs.empty() && _paramDoubleRegs.empty()); +} + +std::size_t CallingConvention::getMaxNumOfRegsPerParam() const +{ + return _numOfRegsPerParam; +} + +std::size_t CallingConvention::getMaxNumOfFPRegsPerParam() const +{ + return _numOfFPRegsPerParam; +} + +std::size_t CallingConvention::getMaxNumOfDoubleRegsPerParam() const +{ + return _numOfDoubleRegsPerParam; +} + +std::size_t CallingConvention::getMaxNumOfVectorRegsPerParam() const +{ + return _numOfVectorRegsPerParam; +} + +bool CallingConvention::getStackParamOrder() const +{ + return _stackParamOrder; +} + +bool CallingConvention::passesLargeObjectsByReference() const +{ + return _largeObjectsPassedByReference; +} + +bool CallingConvention::respectsRegisterCouples() const +{ + return _respectsRegCouples; +} + +std::size_t CallingConvention::getMaxBytesPerStackParam() const +{ + return _abi->getWordSize(); +} + +bool CallingConvention::valueCanBeParameter(const Value *val) const +{ + if (_abi->isStackVariable(val)) + { + return true; + } + + auto rId = _abi->getRegisterId(val); + + if (rId == Abi::REG_INVALID) + { + return false; + } + + return std::count(_paramRegs.begin(), _paramRegs.end(), rId) + || std::count(_paramFPRegs.begin(), _paramFPRegs.end(), rId) + || std::count(_paramDoubleRegs.begin(), _paramDoubleRegs.end(), rId) + || std::count(_paramVectorRegs.begin(), _paramVectorRegs.end(), rId); +} + +bool CallingConvention::canHoldReturnValue(const Value* val) const +{ + auto rId = _abi->getRegisterId(val); + + if (rId == Abi::REG_INVALID) + { + return false; + } + + return std::count(_returnRegs.begin(), _returnRegs.end(), rId) + || std::count(_returnFPRegs.begin(), _returnFPRegs.end(), rId) + || std::count(_returnDoubleRegs.begin(), _returnDoubleRegs.end(), rId) + || std::count(_returnVectorRegs.begin(), _returnVectorRegs.end(), rId); +} + +// +//============================================================================== +// CallingConventionProvider +//============================================================================== +// + +CallingConventionProvider::CallingConventionProvider() +{ + registerCC(CallingConvention::ID::CC_CDECL, &CdeclCallingConvention::create); + registerCC(CallingConvention::ID::CC_ELLIPSIS, &CdeclCallingConvention::create); + registerCC(CallingConvention::ID::CC_STDCALL, &CdeclCallingConvention::create); + registerCC(CallingConvention::ID::CC_THISCALL, &ThiscallCallingConvention::create); + registerCC(CallingConvention::ID::CC_PASCAL, &PascalCallingConvention::create); + registerCC(CallingConvention::ID::CC_FASTCALL, &FastcallCallingConvention::create); + registerCC(CallingConvention::ID::CC_WATCOM, &WatcomCallingConvention::create); + registerCC(CallingConvention::ID::CC_X64, &X64CallingConvention::create); + registerCC(CallingConvention::ID::CC_ARM, &ArmCallingConvention::create); + registerCC(CallingConvention::ID::CC_ARM64, &Arm64CallingConvention::create); + registerCC(CallingConvention::ID::CC_POWERPC, &PowerPCCallingConvention::create); + registerCC(CallingConvention::ID::CC_POWERPC64, &PowerPC64CallingConvention::create); + registerCC(CallingConvention::ID::CC_MIPS, &MipsCallingConvention::create); + registerCC(CallingConvention::ID::CC_MIPS64, &Mips64CallingConvention::create); + registerCC(CallingConvention::ID::CC_PIC32, &Pic32CallingConvention::create); +} + +CallingConventionProvider::~CallingConventionProvider() +{ + _id2cc.clear(); +} + +CallingConventionProvider* CallingConventionProvider::getProvider() +{ + static CallingConventionProvider instance; + + return &instance; +} + +void CallingConventionProvider::registerCC( + const CallingConvention::ID& cc, + const CallingConvention::ConstructorMethod& con) +{ + auto ccId = static_cast(cc); + + if (ccId >= _id2cc.size()) + { + _id2cc.resize(ccId+1, nullptr); + } + + _id2cc[ccId] = con; +} + +CallingConvention::Ptr CallingConventionProvider::createCallingConvention( + const CallingConvention::ID& cc, + const Abi* a) const +{ + auto ccId = static_cast(cc); + + if (ccId >= _id2cc.size() || _id2cc[ccId] == nullptr) + { + return nullptr; + } + + return _id2cc[ccId](a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/mips/mips_conv.cpp b/src/bin2llvmir/providers/calling_convention/mips/mips_conv.cpp new file mode 100644 index 000000000..015b667c0 --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/mips/mips_conv.cpp @@ -0,0 +1,65 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/mips/mips_conv.cpp + * @brief Calling conventions of MIPS architecture. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/mips/mips_conv.h" +#include "retdec/bin2llvmir/providers/calling_convention/mips/mips_psp.h" +#include "retdec/capstone2llvmir/mips/mips.h" + +namespace retdec { +namespace bin2llvmir { + +MipsCallingConvention::MipsCallingConvention(const Abi* a) : + CallingConvention(a) +{ + _paramRegs = { + MIPS_REG_A0, + MIPS_REG_A1, + MIPS_REG_A2, + MIPS_REG_A3 + }; + _paramFPRegs = { + MIPS_REG_F12, + MIPS_REG_F14, + MIPS_REG_F16, + MIPS_REG_F18 + }; + _paramDoubleRegs = { + MIPS_REG_FD12, + MIPS_REG_FD14, + MIPS_REG_FD16, + }; + + _returnRegs = { + MIPS_REG_V0 + }; + + _numOfRegsPerParam = 2; + _largeObjectsPassedByReference = true; + _respectsRegCouples = true; +} + +MipsCallingConvention::~MipsCallingConvention() +{ +} + +CallingConvention::Ptr MipsCallingConvention::create(const Abi* a) +{ + if (!a->isMips()) + { + return nullptr; + } + + if (a->getConfig()->getConfig().tools.isPspGcc()) + { + return std::make_unique(a); + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/mips/mips_psp.cpp b/src/bin2llvmir/providers/calling_convention/mips/mips_psp.cpp new file mode 100644 index 000000000..28dc6a970 --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/mips/mips_psp.cpp @@ -0,0 +1,58 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/mips/mips_psp.cpp + * @brief Calling conventions of MIPS architecture. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/calling_convention/mips/mips_psp.h" +#include "retdec/capstone2llvmir/mips/mips.h" + +namespace retdec { +namespace bin2llvmir { + +MipsPSPCallingConvention::MipsPSPCallingConvention(const Abi* a) : + CallingConvention(a) +{ + _paramRegs = { + MIPS_REG_A0, + MIPS_REG_A1, + MIPS_REG_A2, + MIPS_REG_A3, + MIPS_REG_T0, + MIPS_REG_T1, + MIPS_REG_T2, + MIPS_REG_T3 + }; + _paramFPRegs = { + MIPS_REG_F12, + MIPS_REG_F14, + MIPS_REG_F16, + MIPS_REG_F18 + }; + _paramDoubleRegs = { + MIPS_REG_FD12, + MIPS_REG_FD14, + MIPS_REG_FD16, + }; + + _returnRegs = { + MIPS_REG_V0 + }; + _returnFPRegs = { + MIPS_REG_V0 + }; + _returnDoubleRegs = { + MIPS_REG_V0 + }; + + _numOfRegsPerParam = 2; + _largeObjectsPassedByReference = true; + _respectsRegCouples = true; +} + +MipsPSPCallingConvention::~MipsPSPCallingConvention() +{ +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/mips64/mips64_conv.cpp b/src/bin2llvmir/providers/calling_convention/mips64/mips64_conv.cpp new file mode 100644 index 000000000..c29bcdf9e --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/mips64/mips64_conv.cpp @@ -0,0 +1,64 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/mips64/mips64_conv.cpp + * @brief Calling convention of Mips64 architecture. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/mips64/mips64_conv.h" +#include "retdec/capstone2llvmir/mips/mips.h" + +namespace retdec { +namespace bin2llvmir { + +Mips64CallingConvention::Mips64CallingConvention(const Abi* a) : + CallingConvention(a) +{ + _paramRegs = { + MIPS_REG_A0, + MIPS_REG_A1, + MIPS_REG_A2, + MIPS_REG_A3, + MIPS_REG_T0, + MIPS_REG_T1, + MIPS_REG_T2, + MIPS_REG_T3 + }; + _paramFPRegs = { + MIPS_REG_F12, + MIPS_REG_F13, + MIPS_REG_F14, + MIPS_REG_F15, + MIPS_REG_F16, + MIPS_REG_F17, + MIPS_REG_F18 + }; + + _returnRegs = { + MIPS_REG_V0 + }; + _returnFPRegs = { + MIPS_REG_F1 + }; + + _numOfRegsPerParam = 2; + _largeObjectsPassedByReference = true; + _respectsRegCouples = true; +} + +Mips64CallingConvention::~Mips64CallingConvention() +{ +} + +CallingConvention::Ptr Mips64CallingConvention::create(const Abi* a) +{ + if (!a->isMips64()) + { + return nullptr; + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/pic32/pic32_conv.cpp b/src/bin2llvmir/providers/calling_convention/pic32/pic32_conv.cpp new file mode 100644 index 000000000..46f0bc03a --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/pic32/pic32_conv.cpp @@ -0,0 +1,48 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/pic32/pic32_conv.cpp + * @brief Calling conventions of PIC32 architecture. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/pic32/pic32_conv.h" +#include "retdec/capstone2llvmir/mips/mips.h" + +namespace retdec { +namespace bin2llvmir { + +Pic32CallingConvention::Pic32CallingConvention(const Abi* a) : + CallingConvention(a) +{ + _paramRegs = { + MIPS_REG_A0, + MIPS_REG_A1, + MIPS_REG_A2, + MIPS_REG_A3 + }; + + _returnRegs = { + MIPS_REG_V0 + }; + + _numOfRegsPerParam = 1; + _largeObjectsPassedByReference = true; + _respectsRegCouples = true; +} + +Pic32CallingConvention::~Pic32CallingConvention() +{ +} + +CallingConvention::Ptr Pic32CallingConvention::create(const Abi* a) +{ + if (!a->isPic32()) + { + return nullptr; + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/powerpc/powerpc_conv.cpp b/src/bin2llvmir/providers/calling_convention/powerpc/powerpc_conv.cpp new file mode 100644 index 000000000..fcd9589c7 --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/powerpc/powerpc_conv.cpp @@ -0,0 +1,66 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/powerpc/powerpc_conv.cpp + * @brief Calling conventions of PowerPC architecture. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/powerpc/powerpc_conv.h" +#include "retdec/capstone2llvmir/powerpc/powerpc.h" + +namespace retdec { +namespace bin2llvmir { + +PowerPCCallingConvention::PowerPCCallingConvention(const Abi* a) : + CallingConvention(a) +{ + _paramRegs = { + PPC_REG_R3, + PPC_REG_R4, + PPC_REG_R5, + PPC_REG_R6, + PPC_REG_R7, + PPC_REG_R8, + PPC_REG_R9, + PPC_REG_R10 + }; + _paramFPRegs = { + PPC_REG_F1, + PPC_REG_F2, + PPC_REG_F3, + PPC_REG_F4, + PPC_REG_F5, + PPC_REG_F6, + PPC_REG_F7, + PPC_REG_F8 + }; + + _returnRegs = { + PPC_REG_R3, + PPC_REG_R4 + }; + _returnFPRegs = { + PPC_REG_F1 + }; + + _numOfRegsPerParam = 2; + _largeObjectsPassedByReference = true; + _respectsRegCouples = true; +} + +PowerPCCallingConvention::~PowerPCCallingConvention() +{ +} + +CallingConvention::Ptr PowerPCCallingConvention::create(const Abi* a) +{ + if (!a->isPowerPC()) + { + return nullptr; + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/powerpc64/powerpc64_conv.cpp b/src/bin2llvmir/providers/calling_convention/powerpc64/powerpc64_conv.cpp new file mode 100644 index 000000000..c15373711 --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/powerpc64/powerpc64_conv.cpp @@ -0,0 +1,68 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/powerpc64/powerpc64_conv.cpp + * @brief Calling conventions of PowerPC64 architecture. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/powerpc64/powerpc64_conv.h" +#include "retdec/capstone2llvmir/powerpc/powerpc.h" + +namespace retdec { +namespace bin2llvmir { + +PowerPC64CallingConvention::PowerPC64CallingConvention(const Abi* a) : + CallingConvention(a) +{ + _paramRegs = { + PPC_REG_R3, + PPC_REG_R4, + PPC_REG_R5, + PPC_REG_R6, + PPC_REG_R7, + PPC_REG_R8, + PPC_REG_R9, + PPC_REG_R10 + }; + _paramFPRegs = { + PPC_REG_F1, + PPC_REG_F2, + PPC_REG_F3, + PPC_REG_F4, + PPC_REG_F5, + PPC_REG_F6, + PPC_REG_F7, + PPC_REG_F8, + PPC_REG_F10, + PPC_REG_F11 + }; + + _returnRegs = { + PPC_REG_R3, + PPC_REG_R4 + }; + _returnFPRegs = { + PPC_REG_F1 + }; + + _numOfRegsPerParam = 2; + _largeObjectsPassedByReference = true; + _respectsRegCouples = true; +} + +PowerPC64CallingConvention::~PowerPC64CallingConvention() +{ +} + +CallingConvention::Ptr PowerPC64CallingConvention::create(const Abi* a) +{ + if (!a->isPowerPC64()) + { + return nullptr; + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/x64/x64_conv.cpp b/src/bin2llvmir/providers/calling_convention/x64/x64_conv.cpp new file mode 100644 index 000000000..fe99afab6 --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/x64/x64_conv.cpp @@ -0,0 +1,36 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/x64/x64_conv.cpp + * @brief Calling conventions of X64 architecture. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/x64/x64_conv.h" +#include "retdec/bin2llvmir/providers/calling_convention/x64/x64_microsoft.h" +#include "retdec/bin2llvmir/providers/calling_convention/x64/x64_systemv.h" +#include "retdec/capstone2llvmir/x86/x86.h" + +namespace retdec { +namespace bin2llvmir { + +CallingConvention::Ptr X64CallingConvention::create(const Abi* a) +{ + if (!a->isX64()) + { + return nullptr; + } + + auto c = a->getConfig(); + bool isMinGW = c->getConfig().tools.isGcc() + && c->getConfig().fileFormat.isPe(); + + if (isMinGW || c->getConfig().tools.isMsvc()) + { + return std::make_unique(a); + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/x64/x64_microsoft.cpp b/src/bin2llvmir/providers/calling_convention/x64/x64_microsoft.cpp new file mode 100644 index 000000000..5e824052d --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/x64/x64_microsoft.cpp @@ -0,0 +1,44 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/x64/x64_microsoft.cpp + * @brief Microsoft calling convention of X64 architecture. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/calling_convention/x64/x64_microsoft.h" +#include "retdec/capstone2llvmir/x86/x86.h" + +namespace retdec { +namespace bin2llvmir { + +MicrosoftX64CallingConvention::MicrosoftX64CallingConvention(const Abi* a) : + CallingConvention(a) +{ + _paramRegs = { + X86_REG_RCX, + X86_REG_RDX, + X86_REG_R8, + X86_REG_R9 + }; + _paramFPRegs = { + X86_REG_XMM0, + X86_REG_XMM1, + X86_REG_XMM2, + X86_REG_XMM3 + }; + + _returnRegs = { + X86_REG_RAX + }; + _returnFPRegs = { + X86_REG_XMM0 + }; + + _largeObjectsPassedByReference = true; +} + +MicrosoftX64CallingConvention::~MicrosoftX64CallingConvention() +{ +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/x64/x64_systemv.cpp b/src/bin2llvmir/providers/calling_convention/x64/x64_systemv.cpp new file mode 100644 index 000000000..56dd105cc --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/x64/x64_systemv.cpp @@ -0,0 +1,69 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/x64/x64_systemv.cpp + * @brief System V Calling convention of X64 architecture. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/calling_convention/x64/x64_systemv.h" +#include "retdec/capstone2llvmir/x86/x86.h" + +namespace retdec { +namespace bin2llvmir { + +SystemVX64CallingConvention::SystemVX64CallingConvention(const Abi* a) : + CallingConvention(a) +{ + _paramRegs = { + X86_REG_RDI, + X86_REG_RSI, + X86_REG_RDX, + X86_REG_RCX, + X86_REG_R8, + X86_REG_R9 + }; + _paramFPRegs = { + X86_REG_XMM0, + X86_REG_XMM1, + X86_REG_XMM2, + X86_REG_XMM3, + X86_REG_XMM4, + X86_REG_XMM5, + X86_REG_XMM6, + X86_REG_XMM7 + }; + _paramVectorRegs = { + X86_REG_XMM0, + X86_REG_XMM1, + X86_REG_XMM2, + X86_REG_XMM3, + X86_REG_XMM4, + X86_REG_XMM5, + X86_REG_XMM6, + X86_REG_XMM7 + }; + + _returnRegs = { + X86_REG_RAX, + X86_REG_RDX + }; + _returnFPRegs = { + X86_REG_XMM0, + X86_REG_XMM1 + }; + _returnVectorRegs = { + X86_REG_XMM0, + X86_REG_XMM1 + }; + + _largeObjectsPassedByReference = true; + _numOfRegsPerParam = 2; + _numOfFPRegsPerParam = 2; + _numOfVectorRegsPerParam = 2; +} + +SystemVX64CallingConvention::~SystemVX64CallingConvention() +{ +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/x86/x86_cdecl.cpp b/src/bin2llvmir/providers/calling_convention/x86/x86_cdecl.cpp new file mode 100644 index 000000000..87ef9a78e --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/x86/x86_cdecl.cpp @@ -0,0 +1,43 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/x86/x86_cdecl.cpp + * @brief Cdecl calling convention of architecture x86. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_cdecl.h" +#include "retdec/capstone2llvmir/x86/x86.h" + +namespace retdec { +namespace bin2llvmir { + +CdeclCallingConvention::CdeclCallingConvention(const Abi* a) : + X86CallingConvention(a) +{ + _returnRegs = { + X86_REG_EAX, + X86_REG_EDX + }; + + _returnFPRegs = { + X86_REG_ST7, + X86_REG_ST0 + }; +} + +CdeclCallingConvention::~CdeclCallingConvention() +{ +} + +CallingConvention::Ptr CdeclCallingConvention::create(const Abi* a) +{ + if (!a->isX86()) + { + return nullptr; + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/x86/x86_conv.cpp b/src/bin2llvmir/providers/calling_convention/x86/x86_conv.cpp new file mode 100644 index 000000000..3c50f2983 --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/x86/x86_conv.cpp @@ -0,0 +1,26 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/x86/x86_conv.cpp + * @brief Calling convention of architecture x86. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_conv.h" +#include "retdec/capstone2llvmir/x86/x86.h" + +namespace retdec { +namespace bin2llvmir { + +X86CallingConvention::X86CallingConvention(const Abi* a) : + CallingConvention(a) + +{ +} + +std::size_t X86CallingConvention::getMaxBytesPerStackParam() const +{ + return _abi->getWordSize()*2; +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/x86/x86_fastcall.cpp b/src/bin2llvmir/providers/calling_convention/x86/x86_fastcall.cpp new file mode 100644 index 000000000..995620a79 --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/x86/x86_fastcall.cpp @@ -0,0 +1,93 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/x86/x86_fastcall.cpp + * @brief Fastcall calling convention of architecture x86. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_fastcall.h" +#include "retdec/capstone2llvmir/x86/x86.h" + +namespace retdec { +namespace bin2llvmir { + +// +//============================================================================== +// PascalFastcallCallingConvention +//============================================================================== +// + +PascalFastcallCallingConvention::PascalFastcallCallingConvention(const Abi* a) : + X86CallingConvention(a) + +{ + _paramRegs = { + X86_REG_EAX, + X86_REG_EDX, + X86_REG_ECX + }; + + _returnRegs = { + X86_REG_EAX, + X86_REG_EDX + }; + + _returnFPRegs = { + X86_REG_ST7, + X86_REG_ST0 + }; + + _stackParamOrder = LTR; +} + +PascalFastcallCallingConvention::~PascalFastcallCallingConvention() +{ +} + +// +//============================================================================== +// FastcallCallingConvention +//============================================================================== +// + +FastcallCallingConvention::FastcallCallingConvention(const Abi* a) : + X86CallingConvention(a) + +{ + _paramRegs = { + X86_REG_ECX, + X86_REG_EDX, + }; + + _returnRegs = { + X86_REG_EAX, + X86_REG_EDX + }; + + _returnFPRegs = { + X86_REG_ST7, + X86_REG_ST0 + }; +} + +FastcallCallingConvention::~FastcallCallingConvention() +{ +} + +CallingConvention::Ptr FastcallCallingConvention::create(const Abi* a) +{ + if (!a->isX86()) + { + return nullptr; + } + + if (a->getConfig()->getConfig().tools.isBorland()) + { + return std::make_unique(a); + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/x86/x86_pascal.cpp b/src/bin2llvmir/providers/calling_convention/x86/x86_pascal.cpp new file mode 100644 index 000000000..66fa1b155 --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/x86/x86_pascal.cpp @@ -0,0 +1,46 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/x86/x86_pascal.cpp + * @brief Pascal calling convention of architecture x86. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_pascal.h" +#include "retdec/capstone2llvmir/x86/x86.h" + +namespace retdec { +namespace bin2llvmir { + +PascalCallingConvention::PascalCallingConvention(const Abi* a) : + X86CallingConvention(a) + +{ + _returnRegs = { + X86_REG_EAX, + X86_REG_EDX + }; + + _returnFPRegs = { + X86_REG_ST7, + X86_REG_ST0 + }; + + _stackParamOrder = LTR; +} + +PascalCallingConvention::~PascalCallingConvention() +{ +} + +CallingConvention::Ptr PascalCallingConvention::create(const Abi* a) +{ + if (!a->isX86()) + { + return nullptr; + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/x86/x86_thiscall.cpp b/src/bin2llvmir/providers/calling_convention/x86/x86_thiscall.cpp new file mode 100644 index 000000000..b5f9dd247 --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/x86/x86_thiscall.cpp @@ -0,0 +1,48 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/x86/x86_thiscall.cpp + * @brief Thiscall calling convention of architecture x86. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_thiscall.h" +#include "retdec/capstone2llvmir/x86/x86.h" + +namespace retdec { +namespace bin2llvmir { + +ThiscallCallingConvention::ThiscallCallingConvention(const Abi* a) : + X86CallingConvention(a) + +{ + _paramRegs = { + X86_REG_ECX + }; + + _returnRegs = { + X86_REG_EAX, + X86_REG_EDX + }; + + _returnFPRegs = { + X86_REG_ST7, + X86_REG_ST0 + }; +} + +ThiscallCallingConvention::~ThiscallCallingConvention() +{ +} + +CallingConvention::Ptr ThiscallCallingConvention::create(const Abi* a) +{ + if (!a->isX86()) + { + return nullptr; + } + + return std::make_unique(a); +} + +} +} diff --git a/src/bin2llvmir/providers/calling_convention/x86/x86_watcom.cpp b/src/bin2llvmir/providers/calling_convention/x86/x86_watcom.cpp new file mode 100644 index 000000000..34abcb773 --- /dev/null +++ b/src/bin2llvmir/providers/calling_convention/x86/x86_watcom.cpp @@ -0,0 +1,51 @@ +/** + * @file src/bin2llvmir/providers/calling_convention/x86/x86_watcom.cpp + * @brief Watcom calling convention of architecture x86. + * @copyright (c) 2019 Avast Software, licensed under the MIT license + */ + +#include "retdec/bin2llvmir/providers/abi/abi.h" +#include "retdec/bin2llvmir/providers/calling_convention/x86/x86_watcom.h" +#include "retdec/capstone2llvmir/x86/x86.h" + +namespace retdec { +namespace bin2llvmir { + +WatcomCallingConvention::WatcomCallingConvention(const Abi* a) : + X86CallingConvention(a) + +{ + _paramRegs = { + X86_REG_EAX, + X86_REG_EDX, + X86_REG_EBX, + X86_REG_ECX + }; + + _returnRegs = { + X86_REG_EAX, + X86_REG_EDX + }; + + _returnFPRegs = { + X86_REG_ST7, + X86_REG_ST0 + }; +} + +WatcomCallingConvention::~WatcomCallingConvention() +{ +} + +CallingConvention::Ptr WatcomCallingConvention::create(const Abi* a) +{ + if (!a->isX86()) + { + return nullptr; + } + + return std::make_unique(a); +} + +} +} diff --git a/src/capstone2llvmir/arm/arm.cpp b/src/capstone2llvmir/arm/arm.cpp index 2ce2c57a9..c10395eb4 100644 --- a/src/capstone2llvmir/arm/arm.cpp +++ b/src/capstone2llvmir/arm/arm.cpp @@ -1659,11 +1659,6 @@ void Capstone2LlvmIrTranslatorArm_impl::translateStr(cs_insn* i, cs_arm* ai, llv op0 = irb.CreateZExtOrTrunc(op0, irb.getInt16Ty()); break; } - // TODO: The new commented code is better, but without special handling - // in bin2llvmirl, it screws up some tests: - // e.g. "many-params.c -a arm -f elf -c gcc -C -O0 -g" - // Therefore, at the moment, we generate the same code as original sem. - // case ARM_INS_STRD: case ARM_INS_STREXD: { @@ -1687,12 +1682,15 @@ void Capstone2LlvmIrTranslatorArm_impl::translateStr(cs_insn* i, cs_arm* ai, llv bool subtract = false; if (i->id == ARM_INS_STRD || i->id == ARM_INS_STREXD) { - // TODO: op1 is not stored at all at the moment. See comment above. - if (ai->op_count == 3 && ai->operands[2].type == ARM_OP_MEM) { storeOp(ai->operands[2], op0, irb); + + auto op3 = ai->operands[2]; + op3.mem.disp += 4; + storeOp(op3, op1, irb); + baseR = ai->operands[2].mem.base; if (auto disp = ai->operands[2].mem.disp) { @@ -1702,11 +1700,19 @@ void Capstone2LlvmIrTranslatorArm_impl::translateStr(cs_insn* i, cs_arm* ai, llv { idx = loadRegister(ai->operands[2].mem.index, irb); } + // Maybe we should add +4 to idx? } else if (ai->op_count == 4 && ai->operands[2].type == ARM_OP_MEM) { storeOp(ai->operands[2], op0, irb); + + // We don't use op4 here, post-index offset is applied to source + // address only after the transfers at writeback. + auto op3 = ai->operands[2]; + op3.mem.disp += 4; + storeOp(op3, op1, irb); + baseR = ai->operands[2].mem.base; idx = loadOp(ai->operands[3], irb); subtract = ai->operands[3].subtracted; diff --git a/src/capstone2llvmir/x86/x86.cpp b/src/capstone2llvmir/x86/x86.cpp index 70bfdb589..24688bd41 100644 --- a/src/capstone2llvmir/x86/x86.cpp +++ b/src/capstone2llvmir/x86/x86.cpp @@ -224,7 +224,7 @@ void Capstone2LlvmIrTranslatorX86_impl::generateDataLayout() } case CS_MODE_64: { - _module->setDataLayout("e-i64:64-f80:128-n8:16:32:64-S128"); // clang + _module->setDataLayout("e-m:e-p:64:64-i64:64-f80:128-n8:16:32:64-S128"); // clang break; } default: diff --git a/src/config/architecture.cpp b/src/config/architecture.cpp index 4a21b177a..fffee32c0 100644 --- a/src/config/architecture.cpp +++ b/src/config/architecture.cpp @@ -13,11 +13,14 @@ namespace { const std::string ARCH_UNKNOWN = "unknown"; const std::string ARCH_MIPS = "mips"; +const std::string ARCH_MIPS64 = "mips64"; const std::string ARCH_PIC32 = "pic32"; const std::string ARCH_ARM = "arm"; +const std::string ARCH_ARM64 = "arm64"; const std::string ARCH_THUMB = "thumb"; const std::string ARCH_x86 = "x86"; const std::string ARCH_PPC = "powerpc"; +const std::string ARCH_PPC64 = "powerpc64"; const std::string JSON_name = "name"; const std::string JSON_endian = "endian"; @@ -34,15 +37,18 @@ namespace config { bool Architecture::isArmOrThumb() const { return isArm() || isThumb(); } bool Architecture::isPic32() const { return isArch(eArch::PIC32); } bool Architecture::isArm() const { return isArch(eArch::ARM); } +bool Architecture::isArm64() const { return isArm() && getBitSize() == 64; } bool Architecture::isThumb() const { return isArch(eArch::THUMB); } bool Architecture::isX86() const { return isArch(eArch::X86); } bool Architecture::isX86_16() const { return isX86() && getBitSize() == 16; } bool Architecture::isX86_32() const { return isX86() && getBitSize() == 32; } bool Architecture::isX86_64() const { return isX86() && getBitSize() == 64; } bool Architecture::isPpc() const { return isArch(eArch::PPC); } +bool Architecture::isPpc64() const { return isPpc() && getBitSize() == 64; } bool Architecture::isKnown() const { return !isUnknown(); } bool Architecture::isUnknown() const { return isArch(eArch::UNKNOWN); } bool Architecture::isMips() const { return isArch(eArch::MIPS); } +bool Architecture::isMips64() const { return isMips() && getBitSize() == 64; } bool Architecture::isMipsOrPic32() const{ return isMips() || isPic32(); } /** diff --git a/src/config/calling_convention.cpp b/src/config/calling_convention.cpp index 0f7de479a..60f98926c 100644 --- a/src/config/calling_convention.cpp +++ b/src/config/calling_convention.cpp @@ -46,6 +46,11 @@ CallingConvention::CallingConvention(eCallingConvention cc) : } +CallingConventionID CallingConvention::getID() const +{ + return _callingConvention; +} + CallingConvention CallingConvention::initVoidarg() { return CallingConvention(eCallingConvention::CC_VOIDARG); } CallingConvention CallingConvention::initCdecl() { return CallingConvention(eCallingConvention::CC_CDECL); } CallingConvention CallingConvention::initEllipsis() { return CallingConvention(eCallingConvention::CC_ELLIPSIS); } @@ -132,9 +137,9 @@ bool CallingConvention::operator<(const CallingConvention& cc) const return _callingConvention < cc._callingConvention; } -std::ostream& operator<<(std::ostream &out, const CallingConvention& cc) +std::ostream& operator<<(std::ostream &out, const CallingConventionID& cc) { - switch(cc._callingConvention) + switch(cc) { case CallingConvention::eCallingConvention::CC_UNKNOWN: out << "CC_UNKNOWN"; break; case CallingConvention::eCallingConvention::CC_VOIDARG: out << "CC_VOIDARG"; break; @@ -154,5 +159,12 @@ std::ostream& operator<<(std::ostream &out, const CallingConvention& cc) return out; } +std::ostream& operator<<(std::ostream &out, const CallingConvention& cc) +{ + out << cc._callingConvention; + + return out; +} + } // namespace config } // namespace retdec diff --git a/src/stacofin/stacofin.cpp b/src/stacofin/stacofin.cpp index ec630338a..d96c74df6 100644 --- a/src/stacofin/stacofin.cpp +++ b/src/stacofin/stacofin.cpp @@ -108,10 +108,14 @@ std::set selectSignaturePaths( selectSignaturesWithName(allSigs, sigs, "ucrt"); std::string arch; - if (c.architecture.isX86()) + if (c.architecture.isX86_32()) { arch = "x86"; } + else if (c.architecture.isX86_64()) + { + arch = "x64"; + } else if (c.architecture.isArmOrThumb()) { arch = "arm"; @@ -855,7 +859,7 @@ void Finder::solveReferences() utils::Address Finder::getAddressFromRef(utils::Address ref) { - if (_config->architecture.isX86_32()) + if (_config->architecture.isX86()) { return getAddressFromRef_x86(ref); } diff --git a/tests/bin2llvmir/optimizations/param_return/param_return_tests.cpp b/tests/bin2llvmir/optimizations/param_return/param_return_tests.cpp index 1baef1ba9..28651f303 100644 --- a/tests/bin2llvmir/optimizations/param_return/param_return_tests.cpp +++ b/tests/bin2llvmir/optimizations/param_return/param_return_tests.cpp @@ -4,6 +4,10 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ +#include "retdec/bin2llvmir/providers/abi/arm64.h" +#include "retdec/bin2llvmir/providers/abi/mips64.h" +#include "retdec/bin2llvmir/providers/abi/powerpc64.h" + #include "retdec/bin2llvmir/optimizations/param_return/param_return.h" #include "bin2llvmir/utils/llvmir_tests.h" @@ -23,11 +27,547 @@ class ParamReturnTests: public LlvmIrTests ParamReturn pass; }; -//// -//// x86 -//// // -//TEST_F(ParamReturnTests, x86PtrCallBasicFunctionality) +// x86 +// + +TEST_F(ParamReturnTests, x86PtrCallBasicFunctionality) +{ + parseInput(R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + %a = bitcast i32* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + %a = bitcast i32* @r to void()* + %1 = load i32, i32* %stack_-8 + %2 = load i32, i32* %stack_-4 + %3 = bitcast void ()* %a to void (i32, i32)* + call void %3(i32 %1, i32 %2) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86PtrCallPrevBbIsUsedOnlyIfItIsASinglePredecessor) +{ + parseInput(R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + br label %lab1 + lab1: + store i32 123, i32* %stack_-4 + br label %lab2 + lab2: + store i32 456, i32* %stack_-8 + %a = bitcast i32* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + br label %lab1 + lab1: + store i32 123, i32* %stack_-4 + br label %lab2 + lab2: + store i32 456, i32* %stack_-8 + %a = bitcast i32* @r to void()* + %1 = load i32, i32* %stack_-8 + %2 = load i32, i32* %stack_-4 + %3 = bitcast void ()* %a to void (i32, i32)* + call void %3(i32 %1, i32 %2) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86PtrCallPrevBbIsNotUsedIfItIsNotASinglePredecessor) +{ + parseInput(R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + br label %lab1 + lab1: + store i32 123, i32* %stack_-4 + br label %lab2 + lab2: + store i32 456, i32* %stack_-8 + %a = bitcast i32* @r to void()* + call void %a() + br label %lab2 + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + br label %lab1 + lab1: + store i32 123, i32* %stack_-4 + br label %lab2 + lab2: + store i32 456, i32* %stack_-8 + %a = bitcast i32* @r to void()* + %1 = load i32, i32* %stack_-8 + %2 = bitcast void ()* %a to void (i32)* + call void %2(i32 %1) + br label %lab2 + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86PtrCallOnlyStackStoresAreUsed) +{ + parseInput(R"( + @eax = global i32 0 + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %local = alloca i32 + store i32 123, i32* %stack_-4 + store i32 456, i32* %local + store i32 789, i32* @eax + %a = bitcast i32* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + } + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_EAX, getGlobalByName("eax")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @eax = global i32 0 + @r = global i32 0 + define i32 @fnc() { + %stack_-4 = alloca i32 + %local = alloca i32 + store i32 123, i32* %stack_-4 + store i32 456, i32* %local + store i32 789, i32* @eax + %a = bitcast i32* @r to void()* + %1 = load i32, i32* %stack_-4 + %2 = bitcast void ()* %a to void (i32)* + call void %2(i32 %1) + %3 = load i32, i32* @eax + ret i32 %3 + } + declare void @0() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86PtrCallStackAreUsedAsArgumentsInCorrectOrder) +{ + parseInput(R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 456, i32* %stack_-8 + store i32 123, i32* %stack_-4 + %a = bitcast i32* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 456, i32* %stack_-8 + store i32 123, i32* %stack_-4 + %a = bitcast i32* @r to void()* + %1 = load i32, i32* %stack_-8 + %2 = load i32, i32* %stack_-4 + %3 = bitcast void ()* %a to void (i32, i32)* + call void %3(i32 %1, i32 %2) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86PtrCallOnlyContinuousStackOffsetsAreUsed) +{ + parseInput(R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-16 = alloca i32 + %stack_-20 = alloca i32 + %stack_-24 = alloca i32 + store i32 1, i32* %stack_-16 + store i32 2, i32* %stack_-20 + store i32 3, i32* %stack_-24 + store i32 4, i32* %stack_-4 + %a = bitcast i32* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-16", + "storage" : { "type" : "stack", "value" : -16 } + }, + { + "name" : "stack_-20", + "storage" : { "type" : "stack", "value" : -20 } + }, + { + "name" : "stack_-24", + "storage" : { "type" : "stack", "value" : -24 } + } + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-16 = alloca i32 + %stack_-20 = alloca i32 + %stack_-24 = alloca i32 + store i32 1, i32* %stack_-16 + store i32 2, i32* %stack_-20 + store i32 3, i32* %stack_-24 + store i32 4, i32* %stack_-4 + %a = bitcast i32* @r to void()* + %1 = load i32, i32* %stack_-24 + %2 = load i32, i32* %stack_-20 + %3 = load i32, i32* %stack_-16 + %4 = bitcast void ()* %a to void (i32, i32, i32)* + call void %4(i32 %1, i32 %2, i32 %3) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86ExternalCallBasicFunctionality) +{ + parseInput(R"( + declare void @print() + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + declare void @print(i32, i32) + declare void @0() + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + %1 = load i32, i32* %stack_-8 + %2 = load i32, i32* %stack_-4 + call void @print(i32 %1, i32 %2) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86ExternalCallFixOnMultiplePlaces) +{ + parseInput(R"( + declare void @print() + define void @fnc1() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + call void @print() + ret void + } + define void @fnc2() { + %stack_-16 = alloca i32 + %stack_-20 = alloca i32 + %stack_-24 = alloca i32 + store i32 456, i32* %stack_-20 + store i32 123, i32* %stack_-16 + store i32 123, i32* %stack_-24 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "functions" : [ + { + "name" : "fnc1", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + }, + { + "name" : "fnc2", + "locals" : [ + { + "name" : "stack_-16", + "storage" : { "type" : "stack", "value" : -16 } + }, + { + "name" : "stack_-20", + "storage" : { "type" : "stack", "value" : -20 } + }, + { + "name" : "stack_-24", + "storage" : { "type" : "stack", "value" : -24 } + } + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + declare void @print(i32, i32) + declare void @0() + define void @fnc1() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + %1 = load i32, i32* %stack_-8 + %2 = load i32, i32* %stack_-4 + call void @print(i32 %1, i32 %2) + ret void + } + define void @fnc2() { + %stack_-16 = alloca i32 + %stack_-20 = alloca i32 + %stack_-24 = alloca i32 + store i32 456, i32* %stack_-20 + store i32 123, i32* %stack_-16 + store i32 123, i32* %stack_-24 + %1 = load i32, i32* %stack_-24 + %2 = load i32, i32* %stack_-20 + call void @print(i32 %1, i32 %2) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +//TEST_F(ParamReturnTests, x86ExternalCallSomeFunctionCallsAreNotModified) //{ // parseInput(R"( // @r = global i32 0 @@ -339,6 +879,939 @@ class ParamReturnTests: public LlvmIrTests // )"; // checkModuleAgainstExpectedIr(exp); //} + +TEST_F(ParamReturnTests, x86_64PtrCallBasicFunctionality) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @r = global i64 0 + @rdi = global i64 0 + @rsi = global i64 0 + @rax = global i64 0 + define void @fnc() { + store i64 123, i64* @rdi + store i64 456, i64* @rsi + %a = bitcast i64* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RDI, getGlobalByName("rdi")); + abi->addRegister(X86_REG_RSI, getGlobalByName("rsi")); + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @r = global i64 0 + @rdi = global i64 0 + @rsi = global i64 0 + @rax = global i64 0 + + define i64 @fnc() { + store i64 123, i64* @rdi + store i64 456, i64* @rsi + %a = bitcast i64* @r to void()* + %1 = load i64, i64* @rdi + %2 = load i64, i64* @rsi + %3 = bitcast void ()* %a to void (i64, i64)* + call void %3(i64 %1, i64 %2) + %4 = load i64, i64* @rax + ret i64 %4 + } + + declare void @0() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86_64PtrCallPrevBbIsUsedOnlyIfItIsASinglePredecessor) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @r = global i64 0 + @rdi = global i64 0 + @rsi = global i64 0 + @rax = global i64 0 + + define void @fnc() { + br label %lab1 + lab1: + store i64 123, i64* @rdi + br label %lab2 + lab2: + store i64 456, i64* @rsi + %a = bitcast i64* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + } + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RDI, getGlobalByName("rdi")); + abi->addRegister(X86_REG_RSI, getGlobalByName("rsi")); + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @r = global i64 0 + @rdi = global i64 0 + @rsi = global i64 0 + @rax = global i64 0 + + define i64 @fnc() { + br label %lab1 + + lab1: + store i64 123, i64* @rdi + br label %lab2 + + lab2: + store i64 456, i64* @rsi + %a = bitcast i64* @r to void ()* + %1 = load i64, i64* @rdi + %2 = load i64, i64* @rsi + %3 = bitcast void ()* %a to void (i64, i64)* + call void %3(i64 %1, i64 %2) + %4 = load i64, i64* @rax + ret i64 %4 + } + + declare void @0() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86_64ExternalCallUseStacksIf6RegistersUsed) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rdi = global i64 0 + @rsi = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + @r8 = global i64 0 + @r9 = global i64 0 + @r10 = global i64 0 + @rax = global i64 0 + declare void @print() + define void @fnc() { + store i64 1, i64* @rdi + %stack_-8 = alloca i64 + %stack_-16 = alloca i64 + store i64 1, i64* @r9 + store i64 2, i64* @r10 + store i64 1, i64* @r8 + store i64 1, i64* @rsi + store i64 2, i64* %stack_-8 + store i64 1, i64* @rdx + store i64 2, i64* %stack_-16 + store i64 1, i64* @rcx + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + }, + { + "name" : "stack_-16", + "storage" : { "type" : "stack", "value" : -16 } + } + + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + abi->addRegister(X86_REG_RDI, getGlobalByName("rdi")); + abi->addRegister(X86_REG_RSI, getGlobalByName("rsi")); + abi->addRegister(X86_REG_RCX, getGlobalByName("rcx")); + abi->addRegister(X86_REG_RDX, getGlobalByName("rdx")); + abi->addRegister(X86_REG_R8, getGlobalByName("r8")); + abi->addRegister(X86_REG_R9, getGlobalByName("r9")); + abi->addRegister(X86_REG_R10, getGlobalByName("r10")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rdi = global i64 0 + @rsi = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + @r8 = global i64 0 + @r9 = global i64 0 + @r10 = global i64 0 + @rax = global i64 0 + + declare i64 @print(i64, i64, i64, i64, i64, i64, i64, i64) + + declare void @0() + + define i64 @fnc() { + store i64 1, i64* @rdi + %stack_-8 = alloca i64 + %stack_-16 = alloca i64 + store i64 1, i64* @r9 + store i64 2, i64* @r10 + store i64 1, i64* @r8 + store i64 1, i64* @rsi + store i64 2, i64* %stack_-8 + store i64 1, i64* @rdx + store i64 2, i64* %stack_-16 + store i64 1, i64* @rcx + %1 = load i64, i64* @rdi + %2 = load i64, i64* @rsi + %3 = load i64, i64* @rdx + %4 = load i64, i64* @rcx + %5 = load i64, i64* @r8 + %6 = load i64, i64* @r9 + %7 = load i64, i64* %stack_-16 + %8 = load i64, i64* %stack_-8 + %9 = call i64 @print(i64 %1, i64 %2, i64 %3, i64 %4, i64 %5, i64 %6, i64 %7, i64 %8) + store i64 %9, i64* @rax + %10 = load i64, i64* @rax + ret i64 %10 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86_64ExternalCallUsesFPRegistersBasic) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rax = global i64 0 + @xmm0 = global double 0.0 + @xmm1 = global double 0.0 + + declare void @print() + define void @fnc() { + store double 2.0, double* @xmm1 + store double 2.0, double* @xmm0 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + } + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + abi->addRegister(X86_REG_XMM0, getGlobalByName("xmm0")); + abi->addRegister(X86_REG_XMM1, getGlobalByName("xmm1")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rax = global i64 0 + @xmm0 = global double 0.000000e+00 + @xmm1 = global double 0.000000e+00 + + declare i64 @print(double, double) + + declare void @0() + + define i64 @fnc() { + store double 2.000000e+00, double* @xmm1 + store double 2.000000e+00, double* @xmm0 + %1 = load double, double* @xmm0 + %2 = load double, double* @xmm1 + %3 = call i64 @print(double %1, double %2) + store i64 %3, i64* @rax + %4 = load i64, i64* @rax + ret i64 %4 + } + + declare void @1() + + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86_64ExternalCallUsesFPRegisters) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rdi = global i64 0 + @rsi = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + @r8 = global i64 0 + @r9 = global i64 0 + @r10 = global i64 0 + @rax = global i64 0 + @xmm0 = global double 0.0 + @xmm1 = global double 0.0 + + declare void @print() + define void @fnc() { + store i64 1, i64* @rdi + store i64 1, i64* @r9 + store i64 2, i64* @r10 + store i64 1, i64* @r8 + store i64 1, i64* @rsi + store double 2.0, double* @xmm1 + store i64 1, i64* @rdx + store double 2.0, double* @xmm0 + store i64 1, i64* @rcx + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + } + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + abi->addRegister(X86_REG_RDI, getGlobalByName("rdi")); + abi->addRegister(X86_REG_RSI, getGlobalByName("rsi")); + abi->addRegister(X86_REG_RCX, getGlobalByName("rcx")); + abi->addRegister(X86_REG_RDX, getGlobalByName("rdx")); + abi->addRegister(X86_REG_R8, getGlobalByName("r8")); + abi->addRegister(X86_REG_R9, getGlobalByName("r9")); + abi->addRegister(X86_REG_R10, getGlobalByName("r10")); + abi->addRegister(X86_REG_XMM0, getGlobalByName("xmm0")); + abi->addRegister(X86_REG_XMM1, getGlobalByName("xmm1")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rdi = global i64 0 + @rsi = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + @r8 = global i64 0 + @r9 = global i64 0 + @r10 = global i64 0 + @rax = global i64 0 + @xmm0 = global double 0.000000e+00 + @xmm1 = global double 0.000000e+00 + + declare i64 @print(i64, i64, i64, i64, i64, i64, double, double) + + declare void @0() + + define i64 @fnc() { + store i64 1, i64* @rdi + store i64 1, i64* @r9 + store i64 2, i64* @r10 + store i64 1, i64* @r8 + store i64 1, i64* @rsi + store double 2.000000e+00, double* @xmm1 + store i64 1, i64* @rdx + store double 2.000000e+00, double* @xmm0 + store i64 1, i64* @rcx + %1 = load i64, i64* @rdi + %2 = load i64, i64* @rsi + %3 = load i64, i64* @rdx + %4 = load i64, i64* @rcx + %5 = load i64, i64* @r8 + %6 = load i64, i64* @r9 + %7 = load double, double* @xmm0 + %8 = load double, double* @xmm1 + %9 = call i64 @print(i64 %1, i64 %2, i64 %3, i64 %4, i64 %5, i64 %6, double %7, double %8) + store i64 %9, i64* @rax + %10 = load i64, i64* @rax + ret i64 %10 + } + + declare void @1() + + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86_64UsesJustContinuousSequenceOfRegisters) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rax = global i64 0 + @rdi = global i64 0 + @rsi = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + + declare void @print() + define void @fnc() { + store i64 1, i64* @rdi + store i64 1, i64* @rdx + store i64 1, i64* @rcx + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + abi->addRegister(X86_REG_RDI, getGlobalByName("rdi")); + abi->addRegister(X86_REG_RSI, getGlobalByName("rsi")); + abi->addRegister(X86_REG_RCX, getGlobalByName("rcx")); + abi->addRegister(X86_REG_RDX, getGlobalByName("rdx")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rax = global i64 0 + @rdi = global i64 0 + @rsi = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + + declare i64 @print(i64) + + declare void @0() + + define i64 @fnc() { + store i64 1, i64* @rdi + store i64 1, i64* @rdx + store i64 1, i64* @rcx + %1 = load i64, i64* @rdi + %2 = call i64 @print(i64 %1) + store i64 %2, i64* @rax + %3 = load i64, i64* @rax + ret i64 %3 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ms_x64PtrCallBasicFunctionality) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @r = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + @rax = global i64 0 + define void @fnc() { + store i64 123, i64* @rcx + store i64 456, i64* @rdx + %a = bitcast i64* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + }, + "fileFormat" : "pe64", + "tools" : + [ + {"name" : "gcc"} + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RCX, getGlobalByName("rcx")); + abi->addRegister(X86_REG_RDX, getGlobalByName("rdx")); + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @r = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + @rax = global i64 0 + + define i64 @fnc() { + store i64 123, i64* @rcx + store i64 456, i64* @rdx + %a = bitcast i64* @r to void()* + %1 = load i64, i64* @rcx + %2 = load i64, i64* @rdx + %3 = bitcast void ()* %a to void (i64, i64)* + call void %3(i64 %1, i64 %2) + %4 = load i64, i64* @rax + ret i64 %4 + } + + declare void @0() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ms_x64PtrCallPrevBbIsUsedOnlyIfItIsASinglePredecessor) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @r = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + @rax = global i64 0 + + define void @fnc() { + br label %lab1 + lab1: + store i64 123, i64* @rcx + br label %lab2 + lab2: + store i64 456, i64* @rdx + %a = bitcast i64* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + }, + "fileFormat" : "pe64", + "tools" : + [ + {"name" : "gcc"} + ] + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RCX, getGlobalByName("rcx")); + abi->addRegister(X86_REG_RDX, getGlobalByName("rdx")); + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @r = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + @rax = global i64 0 + + define i64 @fnc() { + br label %lab1 + + lab1: + store i64 123, i64* @rcx + br label %lab2 + + lab2: + store i64 456, i64* @rdx + %a = bitcast i64* @r to void ()* + %1 = load i64, i64* @rcx + %2 = load i64, i64* @rdx + %3 = bitcast void ()* %a to void (i64, i64)* + call void %3(i64 %1, i64 %2) + %4 = load i64, i64* @rax + ret i64 %4 + } + + declare void @0() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ms_x64ExternalCallUseStacksIf4RegistersUsed) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rsi = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + @r8 = global i64 0 + @r9 = global i64 0 + @rax = global i64 0 + declare void @print() + define void @fnc() { + %stack_-8 = alloca i64 + %stack_-16 = alloca i64 + store i64 1, i64* @r9 + store i64 1, i64* @r8 + store i64 1, i64* @rsi + store i64 2, i64* %stack_-8 + store i64 1, i64* @rdx + store i64 2, i64* %stack_-16 + store i64 1, i64* @rcx + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + }, + { + "name" : "stack_-16", + "storage" : { "type" : "stack", "value" : -16 } + } + + ] + } + ], + "fileFormat" : "pe64", + "tools" : + [ + {"name" : "gcc"} + ] + + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + abi->addRegister(X86_REG_RSI, getGlobalByName("rsi")); + abi->addRegister(X86_REG_RCX, getGlobalByName("rcx")); + abi->addRegister(X86_REG_RDX, getGlobalByName("rdx")); + abi->addRegister(X86_REG_R8, getGlobalByName("r8")); + abi->addRegister(X86_REG_R9, getGlobalByName("r9")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rsi = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + @r8 = global i64 0 + @r9 = global i64 0 + @rax = global i64 0 + + declare i64 @print(i64, i64, i64, i64, i64, i64) + + declare void @0() + + define i64 @fnc() { + %stack_-8 = alloca i64 + %stack_-16 = alloca i64 + store i64 1, i64* @r9 + store i64 1, i64* @r8 + store i64 1, i64* @rsi + store i64 2, i64* %stack_-8 + store i64 1, i64* @rdx + store i64 2, i64* %stack_-16 + store i64 1, i64* @rcx + %1 = load i64, i64* @rcx + %2 = load i64, i64* @rdx + %3 = load i64, i64* @r8 + %4 = load i64, i64* @r9 + %5 = load i64, i64* %stack_-16 + %6 = load i64, i64* %stack_-8 + %7 = call i64 @print(i64 %1, i64 %2, i64 %3, i64 %4, i64 %5, i64 %6) + store i64 %7, i64* @rax + %8 = load i64, i64* @rax + ret i64 %8 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ms_x64ExternalCallUsesFPRegisters) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @r8 = global i64 0 + @r9 = global i64 0 + @rax = global i64 0 + @xmm0 = global double 0.0 + @xmm1 = global double 0.0 + + declare void @print() + define void @fnc() { + store double 2.0, double* @xmm1 + store double 2.0, double* @xmm0 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + }, + "fileFormat" : "pe64", + "tools" : + [ + {"name" : "gcc"} + ] + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + abi->addRegister(X86_REG_R8, getGlobalByName("r8")); + abi->addRegister(X86_REG_R9, getGlobalByName("r9")); + abi->addRegister(X86_REG_XMM0, getGlobalByName("xmm0")); + abi->addRegister(X86_REG_XMM1, getGlobalByName("xmm1")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @r8 = global i64 0 + @r9 = global i64 0 + @rax = global i64 0 + @xmm0 = global double 0.000000e+00 + @xmm1 = global double 0.000000e+00 + + declare i64 @print(double, double) + + declare void @0() + + define i64 @fnc() { + store double 2.000000e+00, double* @xmm1 + store double 2.000000e+00, double* @xmm0 + %1 = load double, double* @xmm0 + %2 = load double, double* @xmm1 + %3 = call i64 @print(double %1, double %2) + store i64 %3, i64* @rax + %4 = load i64, i64* @rax + ret i64 %4 + } + + declare void @1() + + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ms_x64UsesJustContinuousSequenceOfRegisters) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rax = global i64 0 + @r8 = global i64 0 + @r9 = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + + declare void @print() + define void @fnc() { + store i64 1, i64* @r9 + store i64 1, i64* @r8 + store i64 1, i64* @rcx + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + }, + "fileFormat" : "pe64", + "tools" : + [ + {"name" : "gcc"} + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + abi->addRegister(X86_REG_R8, getGlobalByName("r8")); + abi->addRegister(X86_REG_R9, getGlobalByName("r9")); + abi->addRegister(X86_REG_RCX, getGlobalByName("rcx")); + abi->addRegister(X86_REG_RDX, getGlobalByName("rdx")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rax = global i64 0 + @r8 = global i64 0 + @r9 = global i64 0 + @rcx = global i64 0 + @rdx = global i64 0 + + declare i64 @print(i64) + + declare void @0() + + define i64 @fnc() { + store i64 1, i64* @r9 + store i64 1, i64* @r8 + store i64 1, i64* @rcx + %1 = load i64, i64* @rcx + %2 = call i64 @print(i64 %1) + store i64 %2, i64* @rax + %3 = load i64, i64* @rax + ret i64 %3 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ms_x64ExternalCallUsesFPRegistersAdvanced) +{ + parseInput(R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rcx = global i64 0 + @rdx = global i64 0 + @rax = global i64 0 + @xmm2 = global double 0.0 + @xmm3 = global double 0.0 + + declare void @print() + define void @fnc() { + store i64 1, i64* @rcx + store i64 1, i64* @rdx + store double 2.0, double* @xmm2 + store double 2.0, double* @xmm3 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "x86" + }, + "fileFormat" : "pe64", + "tools" : + [ + {"name" : "gcc"} + ] + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(X86_REG_RAX, getGlobalByName("rax")); + abi->addRegister(X86_REG_RDX, getGlobalByName("rdx")); + abi->addRegister(X86_REG_RCX, getGlobalByName("rcx")); + abi->addRegister(X86_REG_XMM2, getGlobalByName("xmm2")); + abi->addRegister(X86_REG_XMM3, getGlobalByName("xmm3")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + @rcx = global i64 0 + @rdx = global i64 0 + @rax = global i64 0 + @xmm2 = global double 0.000000e+00 + @xmm3 = global double 0.000000e+00 + + declare i64 @print(i64, i64, double, double) + + declare void @0() + + define i64 @fnc() { + store i64 1, i64* @rcx + store i64 1, i64* @rdx + store double 2.000000e+00, double* @xmm2 + store double 2.000000e+00, double* @xmm3 + %1 = load i64, i64* @rcx + %2 = load i64, i64* @rdx + %3 = load double, double* @xmm2 + %4 = load double, double* @xmm3 + %5 = call i64 @print(i64 %1, i64 %2, double %3, double %4) + store i64 %5, i64* @rax + %6 = load i64, i64* @rax + ret i64 %6 + } + + declare void @1() + + )"; + checkModuleAgainstExpectedIr(exp); +} + // //TEST_F(ParamReturnTests, x86PtrCallOnlyContinuousStackOffsetsAreUsed) //{ @@ -390,1139 +1863,1031 @@ class ParamReturnTests: public LlvmIrTests // })"); // auto abi = AbiProvider::addAbi(module.get(), &config); // -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r = global i32 0 -// define void @fnc() { -// %stack_-4 = alloca i32 -// %stack_-16 = alloca i32 -// %stack_-20 = alloca i32 -// %stack_-24 = alloca i32 -// store i32 1, i32* %stack_-16 -// store i32 2, i32* %stack_-20 -// store i32 3, i32* %stack_-24 -// store i32 4, i32* %stack_-4 -// %a = bitcast i32* @r to void()* -// %1 = load i32, i32* %stack_-24 -// %2 = load i32, i32* %stack_-20 -// %3 = load i32, i32* %stack_-16 -// %4 = bitcast void ()* %a to void (i32, i32, i32)* -// call void %4(i32 %1, i32 %2, i32 %3) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, x86ExternalCallBasicFunctionality) -//{ -// parseInput(R"( -// declare void @print() -// define void @fnc() { -// %stack_-4 = alloca i32 -// %stack_-8 = alloca i32 -// store i32 123, i32* %stack_-4 -// store i32 456, i32* %stack_-8 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "little", -// "name" : "x86" -// }, -// "functions" : [ -// { -// "name" : "fnc", -// "locals" : [ -// { -// "name" : "stack_-4", -// "storage" : { "type" : "stack", "value" : -4 } -// }, -// { -// "name" : "stack_-8", -// "storage" : { "type" : "stack", "value" : -8 } -// } -// ] -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// declare void @print(i32, i32) -// declare void @0() -// define void @fnc() { -// %stack_-4 = alloca i32 -// %stack_-8 = alloca i32 -// store i32 123, i32* %stack_-4 -// store i32 456, i32* %stack_-8 -// %1 = load i32, i32* %stack_-8 -// %2 = load i32, i32* %stack_-4 -// call void @print(i32 %1, i32 %2) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, x86ExternalCallFixOnMultiplePlaces) -//{ -// parseInput(R"( -// declare void @print() -// define void @fnc1() { -// %stack_-4 = alloca i32 -// %stack_-8 = alloca i32 -// store i32 123, i32* %stack_-4 -// store i32 456, i32* %stack_-8 -// call void @print() -// ret void -// } -// define void @fnc2() { -// %stack_-16 = alloca i32 -// %stack_-20 = alloca i32 -// %stack_-24 = alloca i32 -// store i32 456, i32* %stack_-20 -// store i32 123, i32* %stack_-16 -// store i32 123, i32* %stack_-24 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "little", -// "name" : "x86" -// }, -// "functions" : [ -// { -// "name" : "fnc1", -// "locals" : [ -// { -// "name" : "stack_-4", -// "storage" : { "type" : "stack", "value" : -4 } -// }, -// { -// "name" : "stack_-8", -// "storage" : { "type" : "stack", "value" : -8 } -// } -// ] -// }, -// { -// "name" : "fnc2", -// "locals" : [ -// { -// "name" : "stack_-16", -// "storage" : { "type" : "stack", "value" : -16 } -// }, -// { -// "name" : "stack_-20", -// "storage" : { "type" : "stack", "value" : -20 } -// }, -// { -// "name" : "stack_-24", -// "storage" : { "type" : "stack", "value" : -24 } -// } -// ] -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// declare void @print(i32, i32) -// declare void @0() -// define void @fnc1() { -// %stack_-4 = alloca i32 -// %stack_-8 = alloca i32 -// store i32 123, i32* %stack_-4 -// store i32 456, i32* %stack_-8 -// %1 = load i32, i32* %stack_-8 -// %2 = load i32, i32* %stack_-4 -// call void @print(i32 %1, i32 %2) -// ret void -// } -// define void @fnc2() { -// %stack_-16 = alloca i32 -// %stack_-20 = alloca i32 -// %stack_-24 = alloca i32 -// store i32 456, i32* %stack_-20 -// store i32 123, i32* %stack_-16 -// store i32 123, i32* %stack_-24 -// %1 = load i32, i32* %stack_-24 -// %2 = load i32, i32* %stack_-20 -// call void @print(i32 %1, i32 %2) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -////TEST_F(ParamReturnTests, x86ExternalCallSomeFunctionCallsAreNotModified) -////{ -//// auto module = parseInput(R"( -//// declare void @print1() -//// declare void @print2() -//// declare void @print3(i32) -//// define void @fnc() { -//// %stack_-4 = alloca i32 -//// store i32 123, i32* %stack_-4 -//// call void @print1() -//// store i32 123, i32* %stack_-4 -//// call void @print2() -//// store i32 123, i32* %stack_-4 -//// call void @print3(i32 123) -//// ret void -//// } -//// )"); -//// auto config = Config::fromJsonString(module.get(), R"({ -//// "architecture" : { -//// "bitSize" : 32, -//// "endian" : "little", -//// "name" : "x86" -//// }, -//// "functions" : [ -//// { -//// "name" : "fnc", -//// "locals" : [ -//// { -//// "name" : "stack_-4", -//// "storage" : { "type" : "stack", "value" : -4 } -//// } -//// ] -//// }, -//// { -//// "name" : "print1", -//// "fncType" : "dynamicallyLinked", -//// "declarationStr" : "whatever" -//// }, -//// { -//// "name" : "print2", -//// "fncType" : "dynamicallyLinked", -//// "isFromDebug" : true -//// }, -//// { -//// "name" : "print3", -//// "fncType" : "dynamicallyLinked" -//// } -//// ] -//// })"); -//// -//// pass.runOnModuleCustom(*module, &config, abi); -//// -//// std::string exp = R"( -//// declare void @print1() -//// declare void @print2() -//// declare void @print3(i32) -//// define void @fnc() { -//// %stack_-4 = alloca i32 -//// store i32 123, i32* %stack_-4 -//// call void @print1() -//// store i32 123, i32* %stack_-4 -//// call void @print2() -//// store i32 123, i32* %stack_-4 -//// call void @print3(i32 123) -//// ret void -//// } -//// )"; -//// checkModuleAgainstExpectedIr(exp, module.get()); -////} -// -//// -//// PowerPC -//// -// -//TEST_F(ParamReturnTests, ppcPtrCallBasicFunctionality) -//{ -// parseInput(R"( -// @r = global i32 0 -// @r3 = global i32 0 -// @r4 = global i32 0 -// define void @fnc() { -// store i32 123, i32* @r3 -// store i32 456, i32* @r4 -// %a = bitcast i32* @r to void()* -// call void %a() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "big", -// "name" : "powerpc" -// }, -// "registers" : [ -// { -// "name" : "r3", -// "storage" : { "type" : "register", "value" : "r3", -// "registerClass" : "gpregs", "registerNumber" : 3 } -// }, -// { -// "name" : "r4", -// "storage" : { "type" : "register", "value" : "r4", -// "registerClass" : "gpregs", "registerNumber" : 4 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r = global i32 0 -// @r3 = global i32 0 -// @r4 = global i32 0 -// define void @fnc() { -// store i32 123, i32* @r3 -// store i32 456, i32* @r4 -// %a = bitcast i32* @r to void()* -// %1 = load i32, i32* @r3 -// %2 = load i32, i32* @r4 -// %3 = bitcast void ()* %a to void (i32, i32)* -// call void %3(i32 %1, i32 %2) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, ppcExternalCallBasicFunctionality) -//{ -// parseInput(R"( -// @r3 = global i32 0 -// @r4 = global i32 0 -// declare void @print() -// define void @fnc() { -// store i32 123, i32* @r3 -// store i32 456, i32* @r4 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "big", -// "name" : "powerpc" -// }, -// "registers" : [ -// { -// "name" : "r3", -// "storage" : { "type" : "register", "value" : "r3", -// "registerClass" : "gpregs", "registerNumber" : 3 } -// }, -// { -// "name" : "r4", -// "storage" : { "type" : "register", "value" : "r4", -// "registerClass" : "gpregs", "registerNumber" : 4 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r3 = global i32 0 -// @r4 = global i32 0 -// declare void @print(i32, i32) -// declare void @0() -// define void @fnc() { -// store i32 123, i32* @r3 -// store i32 456, i32* @r4 -// %1 = load i32, i32* @r3 -// %2 = load i32, i32* @r4 -// call void @print(i32 %1, i32 %2) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, ppcExternalCallDoNotUseObjectsIfTheyAreNotRegisters) -//{ -// parseInput(R"( -// @r3 = global i32 0 -// declare void @print() -// define void @fnc() { -// store i32 123, i32* @r3 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "big", -// "name" : "powerpc" -// } -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r3 = global i32 0 -// declare void @print() -// define void @fnc() { -// store i32 123, i32* @r3 -// call void @print() -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, ppcExternalCallFilterRegistersOnMultiplePlaces) -//{ -// parseInput(R"( -// @r3 = global i32 0 -// @r4 = global i32 0 -// @r5 = global i32 0 -// declare void @print() -// define void @fnc1() { -// store i32 123, i32* @r3 -// store i32 456, i32* @r4 -// call void @print() -// ret void -// } -// define void @fnc2() { -// store i32 123, i32* @r3 -// store i32 456, i32* @r5 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "big", -// "name" : "powerpc" -// }, -// "registers" : [ -// { -// "name" : "r3", -// "storage" : { "type" : "register", "value" : "r3", -// "registerClass" : "gpregs", "registerNumber" : 3 } -// }, -// { -// "name" : "r4", -// "storage" : { "type" : "register", "value" : "r4", -// "registerClass" : "gpregs", "registerNumber" : 4 } -// }, -// { -// "name" : "r5", -// "storage" : { "type" : "register", "value" : "r5", -// "registerClass" : "gpregs", "registerNumber" : 5 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r3 = global i32 0 -// @r4 = global i32 0 -// @r5 = global i32 0 -// declare void @print(i32) -// declare void @0() -// define void @fnc1() { -// store i32 123, i32* @r3 -// store i32 456, i32* @r4 -// %1 = load i32, i32* @r3 -// call void @print(i32 %1) -// ret void -// } -// define void @fnc2() { -// store i32 123, i32* @r3 -// store i32 456, i32* @r5 -// %1 = load i32, i32* @r3 -// call void @print(i32 %1) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, ppcExternalCallDoNotUseAllRegisters) -//{ -// parseInput(R"( -// @r1 = global i32 0 -// @r2 = global i32 0 -// @r3 = global i32 0 -// declare void @print() -// define void @fnc() { -// store i32 123, i32* @r1 -// store i32 456, i32* @r3 -// store i32 789, i32* @r2 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "big", -// "name" : "powerpc" -// }, -// "registers" : [ -// { -// "name" : "r1", -// "storage" : { "type" : "register", "value" : "r1", -// "registerClass" : "gpregs", "registerNumber" : 1 } -// }, -// { -// "name" : "r2", -// "storage" : { "type" : "register", "value" : "r2", -// "registerClass" : "gpregs", "registerNumber" : 2 } -// }, -// { -// "name" : "r3", -// "storage" : { "type" : "register", "value" : "r3", -// "registerClass" : "gpregs", "registerNumber" : 3 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r1 = global i32 0 -// @r2 = global i32 0 -// @r3 = global i32 0 -// declare void @print(i32) -// declare void @0() -// define void @fnc() { -// store i32 123, i32* @r1 -// store i32 456, i32* @r3 -// store i32 789, i32* @r2 -// %1 = load i32, i32* @r3 -// call void @print(i32 %1) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, ppcExternalCallSortRegistersIntoCorrectOrder) -//{ -// parseInput(R"( -// @r3 = global i32 0 -// @r4 = global i32 0 -// @r5 = global i32 0 -// declare void @print() -// define void @fnc() { -// store i32 123, i32* @r5 -// store i32 456, i32* @r3 -// store i32 789, i32* @r4 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "big", -// "name" : "powerpc" -// }, -// "registers" : [ -// { -// "name" : "r3", -// "storage" : { "type" : "register", "value" : "r3", -// "registerClass" : "gpregs", "registerNumber" : 3 } -// }, -// { -// "name" : "r4", -// "storage" : { "type" : "register", "value" : "r4", -// "registerClass" : "gpregs", "registerNumber" : 4 } -// }, -// { -// "name" : "r5", -// "storage" : { "type" : "register", "value" : "r5", -// "registerClass" : "gpregs", "registerNumber" : 5 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r3 = global i32 0 -// @r4 = global i32 0 -// @r5 = global i32 0 -// declare void @print(i32, i32, i32) -// declare void @0() -// define void @fnc() { -// store i32 123, i32* @r5 -// store i32 456, i32* @r3 -// store i32 789, i32* @r4 -// %1 = load i32, i32* @r3 -// %2 = load i32, i32* @r4 -// %3 = load i32, i32* @r5 -// call void @print(i32 %1, i32 %2, i32 %3) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, ppcExternalCallDoNotUseStacksIfLessThan7RegistersUsed) -//{ -// parseInput(R"( -// @r3 = global i32 0 -// declare void @print() -// define void @fnc() { -// %stack_-4 = alloca i32 -// store i32 123, i32* @r3 -// store i32 456, i32* %stack_-4 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "big", -// "name" : "powerpc" -// }, -// "functions" : [ -// { -// "name" : "fnc", -// "locals" : [ -// { -// "name" : "stack_-4", -// "storage" : { "type" : "stack", "value" : -4 } -// } -// ] -// } -// ], -// "registers" : [ -// { -// "name" : "r3", -// "storage" : { "type" : "register", "value" : "r3", -// "registerClass" : "gpregs", "registerNumber" : 3 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r3 = global i32 0 -// declare void @print(i32) -// declare void @0() -// define void @fnc() { -// %stack_-4 = alloca i32 -// store i32 123, i32* @r3 -// store i32 456, i32* %stack_-4 -// %1 = load i32, i32* @r3 -// call void @print(i32 %1) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, ppcExternalCallUseStacksIf7RegistersUsed) -//{ -// parseInput(R"( -// @r3 = global i32 0 -// @r4 = global i32 0 -// @r5 = global i32 0 -// @r6 = global i32 0 -// @r7 = global i32 0 -// @r8 = global i32 0 -// @r9 = global i32 0 -// @r10 = global i32 0 -// declare void @print() -// define void @fnc() { -// %stack_-4 = alloca i32 -// %stack_-8 = alloca i32 -// store i32 1, i32* @r3 -// store i32 1, i32* @r4 -// store i32 1, i32* @r5 -// store i32 2, i32* %stack_-4 -// store i32 1, i32* @r6 -// store i32 1, i32* @r7 -// store i32 1, i32* @r8 -// store i32 2, i32* %stack_-8 -// store i32 1, i32* @r9 -// store i32 1, i32* @r10 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "big", -// "name" : "powerpc" -// }, -// "functions" : [ -// { -// "name" : "fnc", -// "locals" : [ -// { -// "name" : "stack_-4", -// "storage" : { "type" : "stack", "value" : -4 } -// }, -// { -// "name" : "stack_-8", -// "storage" : { "type" : "stack", "value" : -8 } -// } -// ] -// } -// ], -// "registers" : [ -// { -// "name" : "r3", -// "storage" : { "type" : "register", "value" : "r3", -// "registerClass" : "gpregs", "registerNumber" : 3 } -// }, -// { -// "name" : "r4", -// "storage" : { "type" : "register", "value" : "r4", -// "registerClass" : "gpregs", "registerNumber" : 4 } -// }, -// { -// "name" : "r5", -// "storage" : { "type" : "register", "value" : "r5", -// "registerClass" : "gpregs", "registerNumber" : 5 } -// }, -// { -// "name" : "r6", -// "storage" : { "type" : "register", "value" : "r6", -// "registerClass" : "gpregs", "registerNumber" : 6 } -// }, -// { -// "name" : "r7", -// "storage" : { "type" : "register", "value" : "r7", -// "registerClass" : "gpregs", "registerNumber" : 7 } -// }, -// { -// "name" : "r8", -// "storage" : { "type" : "register", "value" : "r8", -// "registerClass" : "gpregs", "registerNumber" : 8 } -// }, -// { -// "name" : "r9", -// "storage" : { "type" : "register", "value" : "r9", -// "registerClass" : "gpregs", "registerNumber" : 9 } -// }, -// { -// "name" : "r10", -// "storage" : { "type" : "register", "value" : "r10", -// "registerClass" : "gpregs", "registerNumber" : 10 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r3 = global i32 0 -// @r4 = global i32 0 -// @r5 = global i32 0 -// @r6 = global i32 0 -// @r7 = global i32 0 -// @r8 = global i32 0 -// @r9 = global i32 0 -// @r10 = global i32 0 -// declare void @print(i32, i32, i32, i32, i32, i32, i32, i32, i32) -// declare void @0() -// define void @fnc() { -// %stack_-4 = alloca i32 -// %stack_-8 = alloca i32 -// store i32 1, i32* @r3 -// store i32 1, i32* @r4 -// store i32 1, i32* @r5 -// store i32 2, i32* %stack_-4 -// store i32 1, i32* @r6 -// store i32 1, i32* @r7 -// store i32 1, i32* @r8 -// store i32 2, i32* %stack_-8 -// store i32 1, i32* @r9 -// store i32 1, i32* @r10 -// %1 = load i32, i32* @r3 -// %2 = load i32, i32* @r4 -// %3 = load i32, i32* @r5 -// %4 = load i32, i32* @r6 -// %5 = load i32, i32* @r7 -// %6 = load i32, i32* @r8 -// %7 = load i32, i32* @r9 -// %8 = load i32, i32* %stack_-8 -// %9 = load i32, i32* %stack_-4 -// call void @print(i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6, i32 %7, i32 %8, i32 %9) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//// -//// ARM (+THUMB) -//// -// -//TEST_F(ParamReturnTests, armPtrCallBasicFunctionality) -//{ -// parseInput(R"( -// @r = global i32 0 -// @r0 = global i32 0 -// @r1 = global i32 0 -// define void @fnc() { -// store i32 123, i32* @r0 -// store i32 456, i32* @r1 -// %a = bitcast i32* @r to void()* -// call void %a() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "little", -// "name" : "arm" -// }, -// "registers" : [ -// { -// "name" : "r0", -// "storage" : { "type" : "register", "value" : "r0", -// "registerClass" : "regs", "registerNumber" : 0 } -// }, -// { -// "name" : "r1", -// "storage" : { "type" : "register", "value" : "r1", -// "registerClass" : "regs", "registerNumber" : 1 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r = global i32 0 -// @r0 = global i32 0 -// @r1 = global i32 0 -// define void @fnc() { -// store i32 123, i32* @r0 -// store i32 456, i32* @r1 -// %a = bitcast i32* @r to void()* -// %1 = load i32, i32* @r0 -// %2 = load i32, i32* @r1 -// %3 = bitcast void ()* %a to void (i32, i32)* -// call void %3(i32 %1, i32 %2) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, armExternalCallBasicFunctionality) -//{ -// parseInput(R"( -// @r0 = global i32 0 -// @r1 = global i32 0 -// declare void @print() -// define void @fnc() { -// store i32 123, i32* @r0 -// store i32 456, i32* @r1 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "little", -// "name" : "arm" -// }, -// "registers" : [ -// { -// "name" : "r0", -// "storage" : { "type" : "register", "value" : "r0", -// "registerClass" : "regs", "registerNumber" : 0 } -// }, -// { -// "name" : "r1", -// "storage" : { "type" : "register", "value" : "r1", -// "registerClass" : "regs", "registerNumber" : 1 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r0 = global i32 0 -// @r1 = global i32 0 -// declare void @print(i32, i32) -// declare void @0() -// define void @fnc() { -// store i32 123, i32* @r0 -// store i32 456, i32* @r1 -// %1 = load i32, i32* @r0 -// %2 = load i32, i32* @r1 -// call void @print(i32 %1, i32 %2) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, armExternalCallUseStacksIf4RegistersUsed) -//{ -// parseInput(R"( -// @r0 = global i32 0 -// @r1 = global i32 0 -// @r2 = global i32 0 -// @r3 = global i32 0 -// @r4 = global i32 0 -// declare void @print() -// define void @fnc() { -// %stack_-4 = alloca i32 -// %stack_-8 = alloca i32 -// store i32 1, i32* @r2 -// store i32 1, i32* @r1 -// store i32 2, i32* %stack_-4 -// store i32 1, i32* @r4 -// store i32 1, i32* @r0 -// store i32 2, i32* %stack_-8 -// store i32 1, i32* @r3 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "little", -// "name" : "arm" -// }, -// "functions" : [ -// { -// "name" : "fnc", -// "locals" : [ -// { -// "name" : "stack_-4", -// "storage" : { "type" : "stack", "value" : -4 } -// }, -// { -// "name" : "stack_-8", -// "storage" : { "type" : "stack", "value" : -8 } -// } -// ] -// } -// ], -// "registers" : [ -// { -// "name" : "r0", -// "storage" : { "type" : "register", "value" : "r0", -// "registerClass" : "regs", "registerNumber" : 0 } -// }, -// { -// "name" : "r1", -// "storage" : { "type" : "register", "value" : "r1", -// "registerClass" : "regs", "registerNumber" : 1 } -// }, -// { -// "name" : "r2", -// "storage" : { "type" : "register", "value" : "r2", -// "registerClass" : "regs", "registerNumber" : 2 } -// }, -// { -// "name" : "r3", -// "storage" : { "type" : "register", "value" : "r3", -// "registerClass" : "regs", "registerNumber" : 3 } -// }, -// { -// "name" : "r4", -// "storage" : { "type" : "register", "value" : "r4", -// "registerClass" : "regs", "registerNumber" : 4 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r0 = global i32 0 -// @r1 = global i32 0 -// @r2 = global i32 0 -// @r3 = global i32 0 -// @r4 = global i32 0 -// declare void @print(i32, i32, i32, i32, i32, i32) -// declare void @0() -// define void @fnc() { -// %stack_-4 = alloca i32 -// %stack_-8 = alloca i32 -// store i32 1, i32* @r2 -// store i32 1, i32* @r1 -// store i32 2, i32* %stack_-4 -// store i32 1, i32* @r4 -// store i32 1, i32* @r0 -// store i32 2, i32* %stack_-8 -// store i32 1, i32* @r3 -// %1 = load i32, i32* @r0 -// %2 = load i32, i32* @r1 -// %3 = load i32, i32* @r2 -// %4 = load i32, i32* @r3 -// %5 = load i32, i32* %stack_-8 -// %6 = load i32, i32* %stack_-4 -// call void @print(i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//// -//// MIPS (+Pic32) -//// -// -//TEST_F(ParamReturnTests, mipsPtrCallBasicFunctionality) -//{ -// parseInput(R"( -// @r = global i32 0 -// @a0 = global i32 0 -// @a1 = global i32 0 -// define void @fnc() { -// store i32 123, i32* @a0 -// store i32 456, i32* @a1 -// %a = bitcast i32* @r to void()* -// call void %a() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "little", -// "name" : "mips" -// }, -// "registers" : [ -// { -// "name" : "a0", -// "storage" : { "type" : "register", "value" : "a0", -// "registerClass" : "gpregs", "registerNumber" : 4 } -// }, -// { -// "name" : "a1", -// "storage" : { "type" : "register", "value" : "a1", -// "registerClass" : "gpregs", "registerNumber" : 5 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @r = global i32 0 -// @a0 = global i32 0 -// @a1 = global i32 0 -// define void @fnc() { -// store i32 123, i32* @a0 -// store i32 456, i32* @a1 -// %a = bitcast i32* @r to void()* -// %1 = load i32, i32* @a0 -// %2 = load i32, i32* @a1 -// %3 = bitcast void ()* %a to void (i32, i32)* -// call void %3(i32 %1, i32 %2) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} -// -//TEST_F(ParamReturnTests, mipsExternalCallBasicFunctionality) -//{ -// parseInput(R"( -// @a0 = global i32 0 -// @a1 = global i32 0 -// declare void @print() -// define void @fnc() { -// store i32 123, i32* @a0 -// store i32 456, i32* @a1 -// call void @print() -// ret void -// } -// )"); -// auto config = Config::fromJsonString(module.get(), R"({ -// "architecture" : { -// "bitSize" : 32, -// "endian" : "little", -// "name" : "mips" -// }, -// "registers" : [ -// { -// "name" : "a0", -// "storage" : { "type" : "register", "value" : "a0", -// "registerClass" : "gpregs", "registerNumber" : 4 } -// }, -// { -// "name" : "a1", -// "storage" : { "type" : "register", "value" : "a1", -// "registerClass" : "gpregs", "registerNumber" : 5 } -// } -// ] -// })"); -// auto abi = AbiProvider::addAbi(module.get(), &config); -// -// pass.runOnModuleCustom(*module, &config, abi); + +TEST_F(ParamReturnTests, ppcPtrCallBasicFunctionality) +{ + parseInput(R"( + @r = global i32 0 + @r3 = global i32 0 + @r4 = global i32 0 + define void @fnc() { + store i32 123, i32* @r3 + store i32 456, i32* @r4 + %a = bitcast i32* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "big", + "name" : "powerpc" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(PPC_REG_R3, getGlobalByName("r3")); + abi->addRegister(PPC_REG_R4, getGlobalByName("r4")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r = global i32 0 + @r3 = global i32 0 + @r4 = global i32 0 + + define i32 @fnc() { + store i32 123, i32* @r3 + store i32 456, i32* @r4 + %a = bitcast i32* @r to void ()* + %1 = load i32, i32* @r3 + %2 = load i32, i32* @r4 + %3 = bitcast void ()* %a to void (i32, i32)* + call void %3(i32 %1, i32 %2) + %4 = load i32, i32* @r3 + ret i32 %4 + } + + declare void @0() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ppcExternalCallBasicFunctionality) +{ + parseInput(R"( + @r3 = global i32 0 + @r4 = global i32 0 + declare void @print() + define void @fnc() { + store i32 123, i32* @r3 + store i32 456, i32* @r4 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "big", + "name" : "powerpc" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(PPC_REG_R3, getGlobalByName("r3")); + abi->addRegister(PPC_REG_R4, getGlobalByName("r4")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r3 = global i32 0 + @r4 = global i32 0 + + declare i32 @print(i32, i32) + declare void @0() + + define i32 @fnc() { + store i32 123, i32* @r3 + store i32 456, i32* @r4 + %1 = load i32, i32* @r3 + %2 = load i32, i32* @r4 + %3 = call i32 @print(i32 %1, i32 %2) + store i32 %3, i32* @r3 + %4 = load i32, i32* @r3 + ret i32 %4 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ppcExternalCallBasicFPFunctionality) +{ + parseInput(R"( + @r3 = global i32 0 + @r4 = global i32 0 + @f1 = global double 0.0 + @f2 = global double 0.0 + declare void @print() + define void @fnc() { + store i32 123, i32* @r3 + store i32 456, i32* @r4 + store double 0.0, double* @f1 + store double 0.0, double* @f2 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "big", + "name" : "powerpc" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(PPC_REG_R3, getGlobalByName("r3")); + abi->addRegister(PPC_REG_R4, getGlobalByName("r4")); + abi->addRegister(PPC_REG_F1, getGlobalByName("f1")); + abi->addRegister(PPC_REG_F2, getGlobalByName("f2")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r3 = global i32 0 + @r4 = global i32 0 + @f1 = global double 0.0 + @f2 = global double 0.0 + + declare i32 @print(i32, i32, double, double) + declare void @0() + + define i32 @fnc() { + store i32 123, i32* @r3 + store i32 456, i32* @r4 + store double 0.0, double* @f1 + store double 0.0, double* @f2 + %1 = load i32, i32* @r3 + %2 = load i32, i32* @r4 + %3 = load double, double* @f1 + %4 = load double, double* @f2 + %5 = call i32 @print(i32 %1, i32 %2, double %3, double %4) + store i32 %5, i32* @r3 + %6 = load i32, i32* @r3 + ret i32 %6 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ppcExternalCallDoNotUseObjectsIfTheyAreNotRegisters) +{ + parseInput(R"( + @r3 = global i32 0 + declare void @print() + define void @fnc() { + store i32 123, i32* @r3 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "big", + "name" : "powerpc" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r3 = global i32 0 + declare void @print() + define void @fnc() { + store i32 123, i32* @r3 + call void @print() + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} +/* +TEST_F(ParamReturnTests, ppcExternalCallFilterRegistersOnMultiplePlaces) +{ + parseInput(R"( + @r3 = global i32 0 + @r4 = global i32 0 + @r5 = global i32 0 + declare void @print() + define void @fnc1() { + store i32 123, i32* @r3 + store i32 456, i32* @r4 + call void @print() + ret void + } + define void @fnc2() { + store i32 123, i32* @r3 + store i32 456, i32* @r5 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "big", + "name" : "powerpc" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + abi->addRegister(PPC_REG_R3, getGlobalByName("r3")); + abi->addRegister(PPC_REG_R4, getGlobalByName("r4")); + abi->addRegister(PPC_REG_R5, getGlobalByName("r5")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r3 = global i32 0 + @r4 = global i32 0 + @r5 = global i32 0 + + declare i32 @print(i32) + + declare void @0() + + define i32 @fnc1() { + store i32 123, i32* @r3 + store i32 456, i32* @r4 + %1 = load i32, i32* @r3 + %2 = call i32 @print(i32 %1) + store i32 %2, i32* @r3 + %3 = load i32, i32* @r3 + ret i32 %3 + } + + declare void @1() + + define i32 @fnc2() { + store i32 123, i32* @r3 + store i32 456, i32* @r5 + %1 = load i32, i32* @r3 + %2 = call i32 @print(i32 %1) + store i32 %2, i32* @r3 + %3 = load i32, i32* @r3 + ret i32 %3 + } + + declare void @2() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ppcExternalCallDoNotUseAllRegisters) +{ + parseInput(R"( + @r1 = global i32 0 + @r2 = global i32 0 + @r3 = global i32 0 + declare void @print() + define void @fnc() { + store i32 123, i32* @r1 + store i32 456, i32* @r3 + store i32 789, i32* @r2 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "big", + "name" : "powerpc" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(PPC_REG_R1, getGlobalByName("r1")); + abi->addRegister(PPC_REG_R2, getGlobalByName("r2")); + abi->addRegister(PPC_REG_R3, getGlobalByName("r3")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r1 = global i32 0 + @r2 = global i32 0 + @r3 = global i32 0 + + declare i32 @print(i32) + + declare void @0() + + define i32 @fnc() { + store i32 123, i32* @r1 + store i32 456, i32* @r3 + store i32 789, i32* @r2 + %1 = load i32, i32* @r3 + %2 = call i32 @print(i32 %1) + store i32 %2, i32* @r3 + %3 = load i32, i32* @r3 + ret i32 %3 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} +*/ + +TEST_F(ParamReturnTests, ppcExternalCallSortRegistersIntoCorrectOrder) +{ + parseInput(R"( + @r3 = global i32 0 + @r4 = global i32 0 + @r5 = global i32 0 + declare void @print() + define void @fnc() { + store i32 123, i32* @r5 + store i32 456, i32* @r3 + store i32 789, i32* @r4 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "big", + "name" : "powerpc" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(PPC_REG_R3, getGlobalByName("r3")); + abi->addRegister(PPC_REG_R4, getGlobalByName("r4")); + abi->addRegister(PPC_REG_R5, getGlobalByName("r5")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r3 = global i32 0 + @r4 = global i32 0 + @r5 = global i32 0 + + declare i32 @print(i32, i32, i32) + + declare void @0() + + define i32 @fnc() { + store i32 123, i32* @r5 + store i32 456, i32* @r3 + store i32 789, i32* @r4 + %1 = load i32, i32* @r3 + %2 = load i32, i32* @r4 + %3 = load i32, i32* @r5 + %4 = call i32 @print(i32 %1, i32 %2, i32 %3) + store i32 %4, i32* @r3 + %5 = load i32, i32* @r3 + ret i32 %5 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ppcExternalCallDoNotUseStacksIfLessThan7RegistersUsed) +{ + parseInput(R"( + @r3 = global i32 0 + declare void @print() + define void @fnc() { + %stack_-4 = alloca i32 + store i32 123, i32* @r3 + store i32 456, i32* %stack_-4 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "big", + "name" : "powerpc" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + } + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(PPC_REG_R3, getGlobalByName("r3")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r3 = global i32 0 + + declare i32 @print(i32) + + declare void @0() + + define i32 @fnc() { + %stack_-4 = alloca i32 + store i32 123, i32* @r3 + store i32 456, i32* %stack_-4 + %1 = load i32, i32* @r3 + %2 = call i32 @print(i32 %1) + store i32 %2, i32* @r3 + %3 = load i32, i32* @r3 + ret i32 %3 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, ppc64PtrCallBasicFunctionality) +{ + parseInput(R"( + target datalayout = "E-m:e-p:64:64-i64:64-n32" + @r = global i64 0 + @r3 = global i64 0 + @r4 = global i64 0 + define void @fnc() { + store i64 123, i64* @r3 + store i64 456, i64* @r4 + %a = bitcast i64* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "big", + "name" : "powerpc64" + } + })"); + + AbiPowerpc64 abi(module.get(), &config); + + abi.addRegister(PPC_REG_R3, getGlobalByName("r3")); + abi.addRegister(PPC_REG_R4, getGlobalByName("r4")); + + pass.runOnModuleCustom(*module, &config, &abi); + + std::string exp = R"( + target datalayout = "E-m:e-p:64:64-i64:64-n32" + + @r = global i64 0 + @r3 = global i64 0 + @r4 = global i64 0 + + define i64 @fnc() { + store i64 123, i64* @r3 + store i64 456, i64* @r4 + %a = bitcast i64* @r to void ()* + %1 = load i64, i64* @r3 + %2 = load i64, i64* @r4 + %3 = bitcast void ()* %a to void (i64, i64)* + call void %3(i64 %1, i64 %2) + %4 = load i64, i64* @r3 + ret i64 %4 + } + + declare void @0() + )"; + checkModuleAgainstExpectedIr(exp); +} + // // std::string exp = R"( -// @a0 = global i32 0 -// @a1 = global i32 0 -// declare void @print(i32, i32) -// declare void @0() +// @r = global i32 0 // define void @fnc() { -// store i32 123, i32* @a0 -// store i32 456, i32* @a1 -// %1 = load i32, i32* @a0 -// %2 = load i32, i32* @a1 -// call void @print(i32 %1, i32 %2) +// %stack_-4 = alloca i32 +// %stack_-16 = alloca i32 +// %stack_-20 = alloca i32 +// %stack_-24 = alloca i32 +// store i32 1, i32* %stack_-16 +// store i32 2, i32* %stack_-20 +// store i32 3, i32* %stack_-24 +// store i32 4, i32* %stack_-4 +// %a = bitcast i32* @r to void()* +// %1 = load i32, i32* %stack_-24 +// %2 = load i32, i32* %stack_-20 +// %3 = load i32, i32* %stack_-16 +// %4 = bitcast void ()* %a to void (i32, i32, i32)* +// call void %4(i32 %1, i32 %2, i32 %3) // ret void // } // )"; // checkModuleAgainstExpectedIr(exp); //} // -//TEST_F(ParamReturnTests, mipsExternalCallUseStacksIf4RegistersUsed) + +TEST_F(ParamReturnTests, armPtrCallBasicFunctionality) +{ + parseInput(R"( + @r = global i32 0 + @r0 = global i32 0 + @r1 = global i32 0 + define void @fnc() { + store i32 123, i32* @r0 + store i32 456, i32* @r1 + %a = bitcast i32* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "arm" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(ARM_REG_R0, getGlobalByName("r0")); + abi->addRegister(ARM_REG_R1, getGlobalByName("r1")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r = global i32 0 + @r0 = global i32 0 + @r1 = global i32 0 + + define i32 @fnc() { + store i32 123, i32* @r0 + store i32 456, i32* @r1 + %a = bitcast i32* @r to void ()* + %1 = load i32, i32* @r0 + %2 = load i32, i32* @r1 + %3 = bitcast void ()* %a to void (i32, i32)* + call void %3(i32 %1, i32 %2) + %4 = load i32, i32* @r0 + ret i32 %4 + } + + declare void @0() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, armExternalCallBasicFunctionality) +{ + parseInput(R"( + @r0 = global i32 0 + @r1 = global i32 0 + declare void @print() + define void @fnc() { + store i32 123, i32* @r0 + store i32 456, i32* @r1 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "arm" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(ARM_REG_R0, getGlobalByName("r0")); + abi->addRegister(ARM_REG_R1, getGlobalByName("r1")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r0 = global i32 0 + @r1 = global i32 0 + + declare i32 @print(i32, i32) + + declare void @0() + + define i32 @fnc() { + store i32 123, i32* @r0 + store i32 456, i32* @r1 + %1 = load i32, i32* @r0 + %2 = load i32, i32* @r1 + %3 = call i32 @print(i32 %1, i32 %2) + store i32 %3, i32* @r0 + %4 = load i32, i32* @r0 + ret i32 %4 + } + + declare void @1() + + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, armExternalCallUseStacksIf4RegistersUsed) +{ + parseInput(R"( + @r0 = global i32 0 + @r1 = global i32 0 + @r2 = global i32 0 + @r3 = global i32 0 + @r4 = global i32 0 + declare void @print() + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @r2 + store i32 1, i32* @r1 + store i32 2, i32* %stack_-4 + store i32 1, i32* @r4 + store i32 1, i32* @r0 + store i32 2, i32* %stack_-8 + store i32 1, i32* @r3 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "arm" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(ARM_REG_R0, getGlobalByName("r0")); + abi->addRegister(ARM_REG_R1, getGlobalByName("r1")); + abi->addRegister(ARM_REG_R2, getGlobalByName("r2")); + abi->addRegister(ARM_REG_R3, getGlobalByName("r3")); + abi->addRegister(ARM_REG_R4, getGlobalByName("r4")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r0 = global i32 0 + @r1 = global i32 0 + @r2 = global i32 0 + @r3 = global i32 0 + @r4 = global i32 0 + + declare i32 @print(i32, i32, i32, i32, i32, i32) + + declare void @0() + + define i32 @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @r2 + store i32 1, i32* @r1 + store i32 2, i32* %stack_-4 + store i32 1, i32* @r4 + store i32 1, i32* @r0 + store i32 2, i32* %stack_-8 + store i32 1, i32* @r3 + %1 = load i32, i32* @r0 + %2 = load i32, i32* @r1 + %3 = load i32, i32* @r2 + %4 = load i32, i32* @r3 + %5 = load i32, i32* %stack_-8 + %6 = load i32, i32* %stack_-4 + %7 = call i32 @print(i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6) + store i32 %7, i32* @r0 + %8 = load i32, i32* @r0 + ret i32 %8 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, arm64PtrCallBasicFunctionality) +{ + parseInput(R"( + target datalayout = "E-m:e-p:64:64-i64:64-n32" + + @r = global i64 0 + @x0 = global i64 0 + @x1 = global i64 0 + define void @fnc() { + store i64 123, i64* @x0 + store i64 456, i64* @x1 + %a = bitcast i64* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "arm64" + } + })"); + AbiArm64 abi(module.get(), &config); + + abi.addRegister(ARM64_REG_X0, getGlobalByName("x0")); + abi.addRegister(ARM64_REG_X1, getGlobalByName("x1")); + + pass.runOnModuleCustom(*module, &config, &abi); + + std::string exp = R"( + target datalayout = "E-m:e-p:64:64-i64:64-n32" + + @r = global i64 0 + @x0 = global i64 0 + @x1 = global i64 0 + + define i64 @fnc() { + store i64 123, i64* @x0 + store i64 456, i64* @x1 + %a = bitcast i64* @r to void ()* + %1 = load i64, i64* @x0 + %2 = load i64, i64* @x1 + %3 = bitcast void ()* %a to void (i64, i64)* + call void %3(i64 %1, i64 %2) + %4 = load i64, i64* @x0 + ret i64 %4 + } + + declare void @0() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, arm64ExternalCallBasicFunctionality) +{ + parseInput(R"( + @x0 = global i64 0 + @x1 = global i64 0 + declare void @print() + define void @fnc() { + store i64 123, i64* @x0 + store i64 456, i64* @x1 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "arm64" + } + })"); + + AbiArm64 abi(module.get(), &config); + + abi.addRegister(ARM64_REG_X0, getGlobalByName("x0")); + abi.addRegister(ARM64_REG_X1, getGlobalByName("x1")); + + pass.runOnModuleCustom(*module, &config, &abi); + + std::string exp = R"( + @x0 = global i64 0 + @x1 = global i64 0 + + declare i64 @print(i64, i64) + + declare void @0() + + define i64 @fnc() { + store i64 123, i64* @x0 + store i64 456, i64* @x1 + %1 = load i64, i64* @x0 + %2 = load i64, i64* @x1 + %3 = call i64 @print(i64 %1, i64 %2) + store i64 %3, i64* @x0 + %4 = load i64, i64* @x0 + ret i64 %4 + } + + declare void @1() + + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, arm64ExternalCallUseStacksIf8RegistersUsed) +{ + parseInput(R"( + @x0 = global i64 0 + @x1 = global i64 0 + @x2 = global i64 0 + @x3 = global i64 0 + @x4 = global i64 0 + @x5 = global i64 0 + @x6 = global i64 0 + @x7 = global i64 0 + @x8 = global i64 0 + declare void @print() + define void @fnc() { + %stack_-4 = alloca i64 + %stack_-12 = alloca i64 + store i64 1, i64* @x2 + store i64 1, i64* @x1 + store i64 1, i64* @x5 + store i64 1, i64* @x6 + store i64 1, i64* @x8 + store i64 1, i64* @x7 + store i64 2, i64* %stack_-4 + store i64 1, i64* @x4 + store i64 1, i64* @x0 + store i64 2, i64* %stack_-12 + store i64 1, i64* @x3 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "arm64" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-12", + "storage" : { "type" : "stack", "value" : -12 } + } + ] + } + ] + })"); + AbiArm64 abi(module.get(), &config); + + abi.addRegister(ARM64_REG_X0, getGlobalByName("x0")); + abi.addRegister(ARM64_REG_X1, getGlobalByName("x1")); + abi.addRegister(ARM64_REG_X2, getGlobalByName("x2")); + abi.addRegister(ARM64_REG_X3, getGlobalByName("x3")); + abi.addRegister(ARM64_REG_X4, getGlobalByName("x4")); + abi.addRegister(ARM64_REG_X5, getGlobalByName("x5")); + abi.addRegister(ARM64_REG_X6, getGlobalByName("x6")); + abi.addRegister(ARM64_REG_X7, getGlobalByName("x7")); + abi.addRegister(ARM64_REG_X8, getGlobalByName("x8")); + + pass.runOnModuleCustom(*module, &config, &abi); + + std::string exp = R"( + @x0 = global i64 0 + @x1 = global i64 0 + @x2 = global i64 0 + @x3 = global i64 0 + @x4 = global i64 0 + @x5 = global i64 0 + @x6 = global i64 0 + @x7 = global i64 0 + @x8 = global i64 0 + + declare i64 @print(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64) + + declare void @0() + + define i64 @fnc() { + %stack_-4 = alloca i64 + %stack_-12 = alloca i64 + store i64 1, i64* @x2 + store i64 1, i64* @x1 + store i64 1, i64* @x5 + store i64 1, i64* @x6 + store i64 1, i64* @x8 + store i64 1, i64* @x7 + store i64 2, i64* %stack_-4 + store i64 1, i64* @x4 + store i64 1, i64* @x0 + store i64 2, i64* %stack_-12 + store i64 1, i64* @x3 + + %1 = load i64, i64* @x0 + %2 = load i64, i64* @x1 + %3 = load i64, i64* @x2 + %4 = load i64, i64* @x3 + %5 = load i64, i64* @x4 + %6 = load i64, i64* @x5 + %7 = load i64, i64* @x6 + %8 = load i64, i64* @x7 + %9 = load i64, i64* %stack_-12 + %10 = load i64, i64* %stack_-4 + %11 = call i64 @print(i64 %1, i64 %2, i64 %3, i64 %4, i64 %5, i64 %6, i64 %7, i64 %8, i64 %9, i64 %10) + store i64 %11, i64* @x0 + %12 = load i64, i64* @x0 + ret i64 %12 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, arm64ExternalCallHasDouleParameter) +{ + parseInput(R"( + @x0 = global i64 0 + @x1 = global i64 0 + @x2 = global i64 0 + @x3 = global i64 0 + @x4 = global i64 0 + @v0 = global double 0.0 + declare void @foo() + define void @fnc() { + store double 0.0, double* @v0 + call void @foo() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "arm64" + } + })"); + AbiArm64 abi(module.get(), &config); + + abi.addRegister(ARM64_REG_X0, getGlobalByName("x0")); + abi.addRegister(ARM64_REG_X1, getGlobalByName("x1")); + abi.addRegister(ARM64_REG_X2, getGlobalByName("x2")); + abi.addRegister(ARM64_REG_X3, getGlobalByName("x3")); + abi.addRegister(ARM64_REG_X4, getGlobalByName("x4")); + abi.addRegister(ARM64_REG_V0, getGlobalByName("v0")); + + pass.runOnModuleCustom(*module, &config, &abi); + + std::string exp = R"( + @x0 = global i64 0 + @x1 = global i64 0 + @x2 = global i64 0 + @x3 = global i64 0 + @x4 = global i64 0 + @v0 = global double 0.0 + + declare i64 @foo(double) + + declare void @0() + + define i64 @fnc() { + store double 0.0, double* @v0 + %1 = load double, double* @v0 + %2 = call i64 @foo(double %1) + store i64 %2, i64* @x0 + %3 = load i64, i64* @x0 + ret i64 %3 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +// +//TEST_F(ParamReturnTests, ppcExternalCallUseStacksIf7RegistersUsed) //{ // parseInput(R"( -// @a0 = global i32 0 -// @a1 = global i32 0 -// @a2 = global i32 0 -// @a3 = global i32 0 -// @t0 = global i32 0 +// @r3 = global i32 0 +// @r4 = global i32 0 +// @r5 = global i32 0 +// @r6 = global i32 0 +// @r7 = global i32 0 +// @r8 = global i32 0 +// @r9 = global i32 0 +// @r10 = global i32 0 // declare void @print() // define void @fnc() { // %stack_-4 = alloca i32 // %stack_-8 = alloca i32 -// store i32 1, i32* @a2 -// store i32 1, i32* @a1 +// store i32 1, i32* @r3 +// store i32 1, i32* @r4 +// store i32 1, i32* @r5 // store i32 2, i32* %stack_-4 -// store i32 1, i32* @t0 -// store i32 1, i32* @a0 +// store i32 1, i32* @r6 +// store i32 1, i32* @r7 +// store i32 1, i32* @r8 // store i32 2, i32* %stack_-8 -// store i32 1, i32* @a3 +// store i32 1, i32* @r9 +// store i32 1, i32* @r10 // call void @print() // ret void // } @@ -1530,8 +2895,8 @@ class ParamReturnTests: public LlvmIrTests // auto config = Config::fromJsonString(module.get(), R"({ // "architecture" : { // "bitSize" : 32, -// "endian" : "little", -// "name" : "mips" +// "endian" : "big", +// "name" : "powerpc" // }, // "functions" : [ // { @@ -1550,66 +2915,1036 @@ class ParamReturnTests: public LlvmIrTests // ], // "registers" : [ // { -// "name" : "a0", -// "storage" : { "type" : "register", "value" : "a0", +// "name" : "r3", +// "storage" : { "type" : "register", "value" : "r3", +// "registerClass" : "gpregs", "registerNumber" : 3 } +// }, +// { +// "name" : "r4", +// "storage" : { "type" : "register", "value" : "r4", // "registerClass" : "gpregs", "registerNumber" : 4 } // }, // { -// "name" : "a1", -// "storage" : { "type" : "register", "value" : "a1", +// "name" : "r5", +// "storage" : { "type" : "register", "value" : "r5", // "registerClass" : "gpregs", "registerNumber" : 5 } // }, // { -// "name" : "a2", -// "storage" : { "type" : "register", "value" : "a2", +// "name" : "r6", +// "storage" : { "type" : "register", "value" : "r6", // "registerClass" : "gpregs", "registerNumber" : 6 } // }, // { -// "name" : "a3", -// "storage" : { "type" : "register", "value" : "a3", +// "name" : "r7", +// "storage" : { "type" : "register", "value" : "r7", // "registerClass" : "gpregs", "registerNumber" : 7 } // }, // { -// "name" : "t0", -// "storage" : { "type" : "register", "value" : "t0", +// "name" : "r8", +// "storage" : { "type" : "register", "value" : "r8", // "registerClass" : "gpregs", "registerNumber" : 8 } +// }, +// { +// "name" : "r9", +// "storage" : { "type" : "register", "value" : "r9", +// "registerClass" : "gpregs", "registerNumber" : 9 } +// }, +// { +// "name" : "r10", +// "storage" : { "type" : "register", "value" : "r10", +// "registerClass" : "gpregs", "registerNumber" : 10 } // } // ] // })"); // auto abi = AbiProvider::addAbi(module.get(), &config); // -// pass.runOnModuleCustom(*module, &config, abi); -// -// std::string exp = R"( -// @a0 = global i32 0 -// @a1 = global i32 0 -// @a2 = global i32 0 -// @a3 = global i32 0 -// @t0 = global i32 0 -// declare void @print(i32, i32, i32, i32, i32, i32) -// declare void @0() -// define void @fnc() { -// %stack_-4 = alloca i32 -// %stack_-8 = alloca i32 -// store i32 1, i32* @a2 -// store i32 1, i32* @a1 -// store i32 2, i32* %stack_-4 -// store i32 1, i32* @t0 -// store i32 1, i32* @a0 -// store i32 2, i32* %stack_-8 -// store i32 1, i32* @a3 -// %1 = load i32, i32* @a0 -// %2 = load i32, i32* @a1 -// %3 = load i32, i32* @a2 -// %4 = load i32, i32* @a3 -// %5 = load i32, i32* %stack_-8 -// %6 = load i32, i32* %stack_-4 -// call void @print(i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6) -// ret void -// } -// )"; -// checkModuleAgainstExpectedIr(exp); -//} + +TEST_F(ParamReturnTests, mipsPtrCallBasicFunctionality) +{ + parseInput(R"( + @r = global i32 0 + @a0 = global i32 0 + @a1 = global i32 0 + define void @fnc() { + store i32 123, i32* @a0 + store i32 456, i32* @a1 + %a = bitcast i32* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "mips" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(MIPS_REG_A0, getGlobalByName("a0")); + abi->addRegister(MIPS_REG_A1, getGlobalByName("a1")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r = global i32 0 + @a0 = global i32 0 + @a1 = global i32 0 + define void @fnc() { + store i32 123, i32* @a0 + store i32 456, i32* @a1 + %a = bitcast i32* @r to void()* + %1 = load i32, i32* @a0 + %2 = load i32, i32* @a1 + %3 = bitcast void ()* %a to void (i32, i32)* + call void %3(i32 %1, i32 %2) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, mipsExternalCallBasicFunctionality) +{ + parseInput(R"( + @a0 = global i32 0 + @a1 = global i32 0 + declare void @print() + define void @fnc() { + store i32 123, i32* @a0 + store i32 456, i32* @a1 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "mips" + } + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(MIPS_REG_A0, getGlobalByName("a0")); + abi->addRegister(MIPS_REG_A1, getGlobalByName("a1")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @a0 = global i32 0 + @a1 = global i32 0 + declare void @print(i32, i32) + declare void @0() + define void @fnc() { + store i32 123, i32* @a0 + store i32 456, i32* @a1 + %1 = load i32, i32* @a0 + %2 = load i32, i32* @a1 + call void @print(i32 %1, i32 %2) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, mipsExternalCallUseStacksIf4RegistersUsed) +{ + parseInput(R"( + @a0 = global i32 0 + @a1 = global i32 0 + @a2 = global i32 0 + @a3 = global i32 0 + @t0 = global i32 0 + declare void @print() + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @a2 + store i32 1, i32* @a1 + store i32 2, i32* %stack_-4 + store i32 1, i32* @t0 + store i32 1, i32* @a0 + store i32 2, i32* %stack_-8 + store i32 1, i32* @a3 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "mips" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + } + ] + })"); + auto abi = AbiProvider::addAbi(module.get(), &config); + + abi->addRegister(MIPS_REG_A0, getGlobalByName("a0")); + abi->addRegister(MIPS_REG_A1, getGlobalByName("a1")); + abi->addRegister(MIPS_REG_A2, getGlobalByName("a2")); + abi->addRegister(MIPS_REG_A3, getGlobalByName("a3")); + abi->addRegister(MIPS_REG_T0, getGlobalByName("t0")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @a0 = global i32 0 + @a1 = global i32 0 + @a2 = global i32 0 + @a3 = global i32 0 + @t0 = global i32 0 + declare void @print(i32, i32, i32, i32, i32, i32) + declare void @0() + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @a2 + store i32 1, i32* @a1 + store i32 2, i32* %stack_-4 + store i32 1, i32* @t0 + store i32 1, i32* @a0 + store i32 2, i32* %stack_-8 + store i32 1, i32* @a3 + %1 = load i32, i32* @a0 + %2 = load i32, i32* @a1 + %3 = load i32, i32* @a2 + %4 = load i32, i32* @a3 + %5 = load i32, i32* %stack_-8 + %6 = load i32, i32* %stack_-4 + call void @print(i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, mips64PtrCallBasicFunctionality) +{ + parseInput(R"( + target datalayout = "E-m:e-p:64:64-i64:64-f64:64" + + @r = global i64 0 + @a0 = global i64 0 + @a1 = global i64 0 + define void @fnc() { + store i64 123, i64* @a0 + store i64 456, i64* @a1 + %a = bitcast i64* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "mips64" + } + })"); + + AbiMips64 abi(module.get(), &config); + + abi.addRegister(MIPS_REG_A0, getGlobalByName("a0")); + abi.addRegister(MIPS_REG_A1, getGlobalByName("a1")); + + pass.runOnModuleCustom(*module, &config, &abi); + + std::string exp = R"( + target datalayout = "E-m:e-p:64:64-i64:64-f64:64" + + @r = global i64 0 + @a0 = global i64 0 + @a1 = global i64 0 + define void @fnc() { + store i64 123, i64* @a0 + store i64 456, i64* @a1 + %a = bitcast i64* @r to void()* + %1 = load i64, i64* @a0 + %2 = load i64, i64* @a1 + %3 = bitcast void ()* %a to void (i64, i64)* + call void %3(i64 %1, i64 %2) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, mips64ExternalCallBasicFunctionality) +{ + parseInput(R"( + target datalayout = "E-m:e-p:64:64-i64:64-f64:64" + + @a0 = global i64 0 + @a1 = global i64 0 + declare void @print() + define void @fnc() { + store i64 123, i64* @a0 + store i64 456, i64* @a1 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "mips64" + } + })"); + + AbiMips64 abi(module.get(), &config); + + abi.addRegister(MIPS_REG_A0, getGlobalByName("a0")); + abi.addRegister(MIPS_REG_A1, getGlobalByName("a1")); + + pass.runOnModuleCustom(*module, &config, &abi); + + std::string exp = R"( + target datalayout = "E-m:e-p:64:64-i64:64-f64:64" + + @a0 = global i64 0 + @a1 = global i64 0 + declare void @print(i64, i64) + declare void @0() + define void @fnc() { + store i64 123, i64* @a0 + store i64 456, i64* @a1 + %1 = load i64, i64* @a0 + %2 = load i64, i64* @a1 + call void @print(i64 %1, i64 %2) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, mips64ExternalCallUseStacksIf8RegistersUsed) +{ + parseInput(R"( + target datalayout = "E-m:e-p:64:64-i64:64-f64:64" + + @a0 = global i64 0 + @a1 = global i64 0 + @a2 = global i64 0 + @a3 = global i64 0 + @a4 = global i64 0 + @a5 = global i64 0 + @a6 = global i64 0 + @a7 = global i64 0 + @t4 = global i64 0 + declare void @print() + define void @fnc() { + %stack_-4 = alloca i64 + %stack_-12 = alloca i64 + store i64 1, i64* @a2 + store i64 1, i64* @a1 + store i64 1, i64* @a7 + store i64 1, i64* @a4 + store i64 2, i64* %stack_-4 + store i64 1, i64* @t4 + store i64 1, i64* @a0 + store i64 2, i64* %stack_-12 + store i64 1, i64* @a6 + store i64 1, i64* @a5 + store i64 1, i64* @a3 + call void @print() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 64, + "endian" : "little", + "name" : "mips64" + }, + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-12", + "storage" : { "type" : "stack", "value" : -12 } + } + ] + } + ] + })"); + + AbiMips64 abi(module.get(), &config); + + abi.addRegister(MIPS_REG_A0, getGlobalByName("a0")); + abi.addRegister(MIPS_REG_A1, getGlobalByName("a1")); + abi.addRegister(MIPS_REG_A2, getGlobalByName("a2")); + abi.addRegister(MIPS_REG_A3, getGlobalByName("a3")); + abi.addRegister(MIPS_REG_T0, getGlobalByName("a4")); + abi.addRegister(MIPS_REG_T1, getGlobalByName("a5")); + abi.addRegister(MIPS_REG_T2, getGlobalByName("a6")); + abi.addRegister(MIPS_REG_T3, getGlobalByName("a7")); + abi.addRegister(MIPS_REG_T4, getGlobalByName("t4")); + + pass.runOnModuleCustom(*module, &config, &abi); + + std::string exp = R"( + target datalayout = "E-m:e-p:64:64-i64:64-f64:64" + + @a0 = global i64 0 + @a1 = global i64 0 + @a2 = global i64 0 + @a3 = global i64 0 + @a4 = global i64 0 + @a5 = global i64 0 + @a6 = global i64 0 + @a7 = global i64 0 + @t4 = global i64 0 + + declare void @print(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64) + declare void @0() + + define void @fnc() { + %stack_-4 = alloca i64 + %stack_-12 = alloca i64 + store i64 1, i64* @a2 + store i64 1, i64* @a1 + store i64 1, i64* @a7 + store i64 1, i64* @a4 + store i64 2, i64* %stack_-4 + store i64 1, i64* @t4 + store i64 1, i64* @a0 + store i64 2, i64* %stack_-12 + store i64 1, i64* @a6 + store i64 1, i64* @a5 + store i64 1, i64* @a3 + + %1 = load i64, i64* @a0 + %2 = load i64, i64* @a1 + %3 = load i64, i64* @a2 + %4 = load i64, i64* @a3 + %5 = load i64, i64* @a4 + %6 = load i64, i64* @a5 + %7 = load i64, i64* @a6 + %8 = load i64, i64* @a7 + %9 = load i64, i64* %stack_-12 + %10 = load i64, i64* %stack_-4 + call void @print(i64 %1, i64 %2, i64 %3, i64 %4, i64 %5, i64 %6, i64 %7, i64 %8, i64 %9, i64 %10) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86FastcallBasic) +{ + parseInput(R"( + @eax = global i32 0 + @edx = global i32 0 + @ecx = global i32 0 + @r = global i32 0 + declare void @a() + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @ecx + store i32 1, i32* @edx + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + call void @a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "tools" : + [ + { "name" : "gcc" } + ], + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + }, + { + "name" : "a", + "callingConvention" : "fastcall" + } + ] + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + abi->addRegister(X86_REG_EAX, getGlobalByName("eax")); + abi->addRegister(X86_REG_ECX, getGlobalByName("ecx")); + abi->addRegister(X86_REG_EDX, getGlobalByName("edx")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @eax = global i32 0 + @edx = global i32 0 + @ecx = global i32 0 + @r = global i32 0 + + declare i32 @a(i32, i32, i32, i32) + + declare void @0() + + define i32 @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @ecx + store i32 1, i32* @edx + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + %1 = load i32, i32* @ecx + %2 = load i32, i32* @edx + %3 = load i32, i32* %stack_-8 + %4 = load i32, i32* %stack_-4 + %5 = call i32 @a(i32 %1, i32 %2, i32 %3, i32 %4) + store i32 %5, i32* @eax + %6 = load i32, i32* @eax + ret i32 %6 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + + +TEST_F(ParamReturnTests, x86FastcallLargeTypeCatch) +{ + parseInput(R"( + @r = global i32 0 + @ecx = global i32 0 + @edx = global i32 0 + @eax = global i32 0 + declare void @a() + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 123, i32* @ecx + store i32 456, i32* %stack_-4 + store i32 789, i32* %stack_-8 + call void @a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "tools" : + [ + { "name" : "gcc" } + ], + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ], + "calingConvention" : "fastcall" + }, + { + "name" : "a", + "callingConvention" : "fastcall" + } + ] + + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + abi->addRegister(X86_REG_ECX, getGlobalByName("ecx")); + abi->addRegister(X86_REG_EAX, getGlobalByName("eax")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r = global i32 0 + @ecx = global i32 0 + @edx = global i32 0 + @eax = global i32 0 + + declare i32 @a(i32, i32, i32) + + declare void @0() + + define i32 @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 123, i32* @ecx + store i32 456, i32* %stack_-4 + store i32 789, i32* %stack_-8 + %1 = load i32, i32* @ecx + %2 = load i32, i32* %stack_-8 + %3 = load i32, i32* %stack_-4 + %4 = call i32 @a(i32 %1, i32 %2, i32 %3) + store i32 %4, i32* @eax + %5 = load i32, i32* @eax + ret i32 %5 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86PascalBasic) +{ + parseInput(R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + %a = bitcast i32* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "tools" : + [ + { "name" : "borland" } + ], + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + } + ] + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + %a = bitcast i32* @r to void()* + %1 = load i32, i32* %stack_-4 + %2 = load i32, i32* %stack_-8 + %3 = bitcast void ()* %a to void (i32, i32)* + call void %3(i32 %1, i32 %2) + ret void + } + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86PascalFastcallBasic) +{ + parseInput(R"( + @eax = global i32 0 + @edx = global i32 0 + @ecx = global i32 0 + @r = global i32 0 + + declare void @a() + + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @eax + store i32 1, i32* @ecx + store i32 1, i32* @edx + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + call void @a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "tools" : + [ + { "name" : "borland" } + ], + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ], + "callingConvention" : "fastcall" + }, + { + "name" : "a", + "callingConvention" : "fastcall" + } + ] + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + abi->addRegister(X86_REG_EAX, getGlobalByName("eax")); + abi->addRegister(X86_REG_EDX, getGlobalByName("edx")); + abi->addRegister(X86_REG_ECX, getGlobalByName("ecx")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @eax = global i32 0 + @edx = global i32 0 + @ecx = global i32 0 + @r = global i32 0 + + declare i32 @a(i32, i32, i32, i32, i32) + + declare void @0() + + define i32 @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @eax + store i32 1, i32* @ecx + store i32 1, i32* @edx + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + %1 = load i32, i32* @eax + %2 = load i32, i32* @edx + %3 = load i32, i32* @ecx + %4 = load i32, i32* %stack_-4 + %5 = load i32, i32* %stack_-8 + %6 = call i32 @a(i32 %1, i32 %2, i32 %3, i32 %4, i32 %5) + store i32 %6, i32* @eax + %7 = load i32, i32* @eax + ret i32 %7 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86PascalFastcallLargeType) +{ + parseInput(R"( + @eax = global i32 0 + @edx = global i32 0 + @r = global i32 0 + + declare void @a() + + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @eax + store i32 456, i32* %stack_-8 + store i32 123, i32* %stack_-4 + store i32 1, i32* @edx + call void @a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "tools" : + [ + { "name" : "borland" } + ], + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ], + "callingConvention" : "fastcall" + }, + { + "name" : "a", + "callingConvention" : "fastcall" + } + ] + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + abi->addRegister(X86_REG_EAX, getGlobalByName("eax")); + abi->addRegister(X86_REG_EDX, getGlobalByName("edx")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @eax = global i32 0 + @edx = global i32 0 + @r = global i32 0 + + declare i32 @a(i32, i32, i32, i32) + + declare void @0() + + define i32 @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @eax + store i32 456, i32* %stack_-8 + store i32 123, i32* %stack_-4 + store i32 1, i32* @edx + %1 = load i32, i32* @eax + %2 = load i32, i32* @edx + %3 = load i32, i32* %stack_-4 + %4 = load i32, i32* %stack_-8 + %5 = call i32 @a(i32 %1, i32 %2, i32 %3, i32 %4) + store i32 %5, i32* @eax + %6 = load i32, i32* @eax + ret i32 %6 + } + + declare void @1() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86WatcomBasic) +{ + parseInput(R"( + @eax = global i32 0 + @ebx = global i32 0 + @edx = global i32 0 + @ecx = global i32 0 + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @eax + store i32 1, i32* @ebx + store i32 1, i32* @ecx + store i32 1, i32* @edx + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + %a = bitcast i32* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "tools" : + [ + { "name" : "open_watcom" } + ], + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + } + ] + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + abi->addRegister(X86_REG_EAX, getGlobalByName("eax")); + abi->addRegister(X86_REG_EBX, getGlobalByName("ebx")); + abi->addRegister(X86_REG_ECX, getGlobalByName("ecx")); + abi->addRegister(X86_REG_EDX, getGlobalByName("edx")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @eax = global i32 0 + @ebx = global i32 0 + @edx = global i32 0 + @ecx = global i32 0 + @r = global i32 0 + + define i32 @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @eax + store i32 1, i32* @ebx + store i32 1, i32* @ecx + store i32 1, i32* @edx + store i32 123, i32* %stack_-4 + store i32 456, i32* %stack_-8 + %a = bitcast i32* @r to void()* + %1 = load i32, i32* @eax + %2 = load i32, i32* @edx + %3 = load i32, i32* @ebx + %4 = load i32, i32* @ecx + %5 = load i32, i32* %stack_-8 + %6 = load i32, i32* %stack_-4 + %7 = bitcast void ()* %a to void (i32, i32, i32, i32, i32, i32)* + call void %7(i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6) + %8 = load i32, i32* @eax + ret i32 %8 + } + + declare void @0() + )"; + checkModuleAgainstExpectedIr(exp); +} + +TEST_F(ParamReturnTests, x86WatcomPassDouble) +{ + parseInput(R"( + @eax = global i32 0 + @edx = global i32 0 + @r = global i32 0 + define void @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @eax + store i32 1, i32* @edx + store i32 456, i32* %stack_-8 + store i32 123, i32* %stack_-4 + %a = bitcast i32* @r to void()* + call void %a() + ret void + } + )"); + auto config = Config::fromJsonString(module.get(), R"({ + "architecture" : { + "bitSize" : 32, + "endian" : "little", + "name" : "x86" + }, + "tools" : + [ + { "name" : "open_watcom" } + ], + "functions" : [ + { + "name" : "fnc", + "locals" : [ + { + "name" : "stack_-4", + "storage" : { "type" : "stack", "value" : -4 } + }, + { + "name" : "stack_-8", + "storage" : { "type" : "stack", "value" : -8 } + } + ] + } + ] + })"); + + auto abi = AbiProvider::addAbi(module.get(), &config); + abi->addRegister(X86_REG_EAX, getGlobalByName("eax")); + abi->addRegister(X86_REG_EDX, getGlobalByName("edx")); + + pass.runOnModuleCustom(*module, &config, abi); + + std::string exp = R"( + @eax = global i32 0 + @edx = global i32 0 + @r = global i32 0 + + define i32 @fnc() { + %stack_-4 = alloca i32 + %stack_-8 = alloca i32 + store i32 1, i32* @eax + store i32 1, i32* @edx + store i32 456, i32* %stack_-8 + store i32 123, i32* %stack_-4 + %a = bitcast i32* @r to void()* + %1 = load i32, i32* @eax + %2 = load i32, i32* @edx + %3 = load i32, i32* %stack_-8 + %4 = load i32, i32* %stack_-4 + %5 = bitcast void ()* %a to void (i32, i32, i32, i32)* + call void %5(i32 %1, i32 %2, i32 %3, i32 %4) + %6 = load i32, i32* @eax + ret i32 %6 + } + + declare void @0() + )"; + checkModuleAgainstExpectedIr(exp); +} } // namespace tests } // namespace bin2llvmir diff --git a/tests/bin2llvmir/utils/llvmir_tests.h b/tests/bin2llvmir/utils/llvmir_tests.h index 9f58965bf..00fce3a33 100644 --- a/tests/bin2llvmir/utils/llvmir_tests.h +++ b/tests/bin2llvmir/utils/llvmir_tests.h @@ -53,6 +53,7 @@ class LlvmIrTests : public ::testing::Test */ void clearAllStaticData() { + AbiProvider::clear(); ConfigProvider::clear(); DebugFormatProvider::clear(); DemanglerProvider::clear(); diff --git a/tests/capstone2llvmir/arm_tests.cpp b/tests/capstone2llvmir/arm_tests.cpp index d7676177a..8ab432776 100644 --- a/tests/capstone2llvmir/arm_tests.cpp +++ b/tests/capstone2llvmir/arm_tests.cpp @@ -2908,8 +2908,54 @@ TEST_P(Capstone2LlvmIrTranslatorArmTests, ARM_INS_STRD) EXPECT_NO_REGISTERS_STORED(); EXPECT_NO_MEMORY_LOADED(); EXPECT_JUST_MEMORY_STORED({ -// {0x1000, 0x1234567890abcdef_qw} - {0x1000, 0x12345678_dw} + {0x1000, 0x12345678_dw}, + {0x1004, 0x90abcdef_dw} + }); + EXPECT_NO_VALUE_CALLED(); +} + +TEST_P(Capstone2LlvmIrTranslatorArmTests, ARM_INS_STRD_sp_imm) +{ + ALL_MODES; + + setRegisters({ + {ARM_REG_R0, 0x12345678}, + {ARM_REG_R1, 0x90abcdef}, + {ARM_REG_SP, 0x1000}, + }); + + emulate("strd r0, r1, [sp, #0x18]"); + + EXPECT_JUST_REGISTERS_LOADED({ARM_REG_R0, ARM_REG_R1, ARM_REG_SP}); + EXPECT_NO_REGISTERS_STORED(); + EXPECT_NO_MEMORY_LOADED(); + EXPECT_JUST_MEMORY_STORED({ + {0x1018, 0x12345678_dw}, + {0x101c, 0x90abcdef_dw} + }); + EXPECT_NO_VALUE_CALLED(); +} + +TEST_P(Capstone2LlvmIrTranslatorArmTests, ARM_INS_STRD_sp_post_imm) +{ + ALL_MODES; + + setRegisters({ + {ARM_REG_R0, 0x12345678}, + {ARM_REG_R1, 0x90abcdef}, + {ARM_REG_SP, 0x1000}, + }); + + emulate("strd r0, r1, [sp], #0x18"); + + EXPECT_JUST_REGISTERS_LOADED({ARM_REG_R0, ARM_REG_R1, ARM_REG_SP}); + EXPECT_JUST_REGISTERS_STORED({ + {ARM_REG_SP, ANY}, // Not an exact value because some strange behaviour. + }); + EXPECT_NO_MEMORY_LOADED(); + EXPECT_JUST_MEMORY_STORED({ + {0x1000, 0x12345678_dw}, + {0x1004, 0x90abcdef_dw} }); EXPECT_NO_VALUE_CALLED(); }