Index: include/lldb/Expression/IRInterpreter.h =================================================================== --- include/lldb/Expression/IRInterpreter.h +++ include/lldb/Expression/IRInterpreter.h @@ -44,7 +44,8 @@ static bool CanInterpret (llvm::Module &module, llvm::Function &function, - lldb_private::Error &error); + lldb_private::Error &error, + const bool support_function_calls); static bool Interpret (llvm::Module &module, @@ -53,7 +54,8 @@ lldb_private::IRMemoryMap &memory_map, lldb_private::Error &error, lldb::addr_t stack_frame_bottom, - lldb::addr_t stack_frame_top); + lldb::addr_t stack_frame_top, + lldb_private::ExecutionContext &exe_ctx); private: static bool Index: include/lldb/Expression/IRMemoryMap.h =================================================================== --- include/lldb/Expression/IRMemoryMap.h +++ include/lldb/Expression/IRMemoryMap.h @@ -60,7 +60,7 @@ void ReadMemory (uint8_t *bytes, lldb::addr_t process_address, size_t size, Error &error); void ReadScalarFromMemory (Scalar &scalar, lldb::addr_t process_address, size_t size, Error &error); void ReadPointerFromMemory (lldb::addr_t *address, lldb::addr_t process_address, Error &error); - + bool GetAllocSize(lldb::addr_t address, size_t &size); void GetMemoryData (DataExtractor &extractor, lldb::addr_t process_address, size_t size, Error &error); lldb::ByteOrder GetByteOrder(); Index: include/lldb/Target/ABI.h =================================================================== --- include/lldb/Target/ABI.h +++ include/lldb/Target/ABI.h @@ -39,10 +39,9 @@ }; eType type; /* value of eType */ size_t size; /* size in bytes of this argument */ - union { - lldb::addr_t value; /* literal value */ - uint8_t *data; /* host data pointer */ - }; + + lldb::addr_t value; /* literal value */ + std::unique_ptr data_ap; /* host data pointer */ }; virtual @@ -58,7 +57,7 @@ lldb::addr_t returnAddress, llvm::ArrayRef args) const = 0; - // Prepare trivial call used from ThreadPlanFunctionCallGDB + // Prepare trivial call used from ThreadPlanFunctionCallUsingABI // AD: // . Because i don't want to change other ABI's this is not declared pure virtual. // The dummy implementation will simply fail. Only HexagonABI will currently Index: include/lldb/Target/Process.h =================================================================== --- include/lldb/Target/Process.h +++ include/lldb/Target/Process.h @@ -2439,8 +2439,41 @@ /// True if execution of JIT code is possible; false otherwise. //------------------------------------------------------------------ void SetCanJIT (bool can_jit); + + //------------------------------------------------------------------ + /// Determines whether executing function calls using the interpreter + /// is possible for this process. + /// + /// @return + /// True if possible; false otherwise. + //------------------------------------------------------------------ + bool CanInterpretFunctionCalls () + { + return m_can_interpret_function_calls; + } //------------------------------------------------------------------ + /// Sets whether executing function calls using the interpreter + /// is possible for this process. + /// + /// @param[in] can_interpret_function_calls + /// True if possible; false otherwise. + //------------------------------------------------------------------ + void SetCanInterpretFunctionCalls (bool can_interpret_function_calls) + { + m_can_interpret_function_calls = can_interpret_function_calls; + } + + //------------------------------------------------------------------ + /// Sets whether executing code in this process is possible. + /// This could be either through JIT or interpreting. + /// + /// @param[in] can_run_code + /// True if execution of code is possible; false otherwise. + //------------------------------------------------------------------ + void SetCanRunCode (bool can_run_code); + + //------------------------------------------------------------------ /// Actually deallocate memory in the process. /// /// This function will deallocate memory in the process's address @@ -3236,6 +3269,7 @@ lldb::StateType m_last_broadcast_state; /// This helps with the Public event coalescing in ShouldBroadcastEvent. std::map m_resolved_indirect_addresses; bool m_destroy_in_process; + bool m_can_interpret_function_calls; // Some targets, e.g the OSX kernel, don't support the ability to modify the stack. enum { eCanJITDontKnow= 0, Index: include/lldb/Target/ThreadPlanCallFunction.h =================================================================== --- include/lldb/Target/ThreadPlanCallFunction.h +++ include/lldb/Target/ThreadPlanCallFunction.h @@ -34,6 +34,10 @@ llvm::ArrayRef args, const EvaluateExpressionOptions &options); + ThreadPlanCallFunction(Thread &thread, + const Address &function, + const EvaluateExpressionOptions &options); + virtual ~ThreadPlanCallFunction (); @@ -133,9 +137,10 @@ virtual bool DoPlanExplainsStop (Event *event_ptr); + + virtual void + SetReturnValue(); -private: - bool ConstructorSetup (Thread &thread, ABI *& abi, @@ -153,7 +158,7 @@ bool BreakpointsExplainStop (); - + bool m_valid; bool m_stop_other_threads; bool m_unwind_on_error; @@ -172,13 +177,14 @@ // it's nice to know the real stop reason. // This gets set in DoTakedown. StreamString m_constructor_errors; - ClangASTType m_return_type; lldb::ValueObjectSP m_return_valobj_sp; // If this contains a valid pointer, use the ABI to extract values when complete bool m_takedown_done; // We want to ensure we only do the takedown once. This ensures that. bool m_should_clear_objc_exception_bp; bool m_should_clear_cxx_exception_bp; lldb::addr_t m_stop_address; // This is the address we stopped at. Also set in DoTakedown; +private: + ClangASTType m_return_type; DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallFunction); }; Index: include/lldb/Target/ThreadPlanCallFunctionUsingABI.h =================================================================== --- include/lldb/Target/ThreadPlanCallFunctionUsingABI.h +++ include/lldb/Target/ThreadPlanCallFunctionUsingABI.h @@ -0,0 +1,58 @@ +//===-- ThreadPlanCallFunctionUsingABI.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanCallFunctionUsingABI_h_ +#define liblldb_ThreadPlanCallFunctionUsingABI_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanCallFunction.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/Type.h" + +namespace lldb_private { + +class ThreadPlanCallFunctionUsingABI : public ThreadPlanCallFunction +{ + // Create a thread plan to call a function at the address passed in the "function" + // argument, this function is executed using register manipulation instead of JIT. + // Class derives from ThreadPlanCallFunction and differs by calling a alternative + // ABI interface ABI::PrepareTrivialCall() which provides more detailed information. +public: + ThreadPlanCallFunctionUsingABI (Thread &thread, + const Address &function_address, + llvm::Type &function_prototype, + llvm::Type &return_type, + llvm::ArrayRef args, + const EvaluateExpressionOptions &options); + + ~ThreadPlanCallFunctionUsingABI (); + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) override; + +protected: + void + SetReturnValue () override; + + +private: + llvm::Type &m_return_type; + DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallFunctionUsingABI); +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanCallFunctionUsingABI_h_ Index: lldb.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -859,6 +859,7 @@ E7723D481AC4A8C8002BA082 /* RegisterContextFreeBSD_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7723D461AC4A8C8002BA082 /* RegisterContextFreeBSD_arm64.cpp */; }; E7723D4C1AC4A944002BA082 /* RegisterContextPOSIX_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7723D4A1AC4A944002BA082 /* RegisterContextPOSIX_arm64.cpp */; }; E778E9A21B062D1700247609 /* EmulateInstructionMIPS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E778E99F1B062D1700247609 /* EmulateInstructionMIPS.cpp */; }; + EBDD01251B39B62F00E04792 /* ThreadPlanCallFunctionUsingABI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EBDD01241B39B62F00E04792 /* ThreadPlanCallFunctionUsingABI.cpp */; }; ED88244E15114A9200BC98B9 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDB919B414F6F10D008FF64B /* Security.framework */; }; ED88245015114CA200BC98B9 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED88244F15114CA200BC98B9 /* main.mm */; }; ED88245115114CA200BC98B9 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED88244F15114CA200BC98B9 /* main.mm */; }; @@ -2684,6 +2685,8 @@ E7723D4B1AC4A944002BA082 /* RegisterContextPOSIX_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextPOSIX_arm64.h; path = Utility/RegisterContextPOSIX_arm64.h; sourceTree = ""; }; E778E99F1B062D1700247609 /* EmulateInstructionMIPS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EmulateInstructionMIPS.cpp; sourceTree = ""; }; E778E9A01B062D1700247609 /* EmulateInstructionMIPS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EmulateInstructionMIPS.h; sourceTree = ""; }; + EBDD01241B39B62F00E04792 /* ThreadPlanCallFunctionUsingABI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanCallFunctionUsingABI.cpp; path = source/Target/ThreadPlanCallFunctionUsingABI.cpp; sourceTree = ""; }; + EBDD01261B39B66700E04792 /* ThreadPlanCallFunctionUsingABI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ThreadPlanCallFunctionUsingABI.h; path = include/lldb/Target/ThreadPlanCallFunctionUsingABI.h; sourceTree = ""; }; ED88244F15114CA200BC98B9 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; ED88245215114CFC00BC98B9 /* LauncherRootXPCService.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LauncherRootXPCService.mm; sourceTree = ""; }; EDB919B214F6EC85008FF64B /* LauncherXPCService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LauncherXPCService.h; sourceTree = ""; }; @@ -4515,6 +4518,8 @@ 26BC7DEF10F1B80200F91463 /* Target */ = { isa = PBXGroup; children = ( + EBDD01261B39B66700E04792 /* ThreadPlanCallFunctionUsingABI.h */, + EBDD01241B39B62F00E04792 /* ThreadPlanCallFunctionUsingABI.cpp */, 8CF02AE019DCBF3B00B14BE0 /* InstrumentationRuntime.h */, 8CF02ADF19DCBF3B00B14BE0 /* InstrumentationRuntime.cpp */, 8CF02AEE19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.h */, @@ -6307,6 +6312,7 @@ 26744EF11338317700EF765A /* GDBRemoteCommunicationClient.cpp in Sources */, 26744EF31338317700EF765A /* GDBRemoteCommunicationServer.cpp in Sources */, 264A97BF133918BC0017F0BE /* PlatformRemoteGDBServer.cpp in Sources */, + EBDD01251B39B62F00E04792 /* ThreadPlanCallFunctionUsingABI.cpp in Sources */, 2697A54D133A6305004E4240 /* PlatformDarwin.cpp in Sources */, 26651A18133BF9E0005B64B7 /* Opcode.cpp in Sources */, 3FDFED0B19B7C8DE009756A7 /* HostThreadMacOSX.mm in Sources */, Index: source/Expression/ClangExpressionParser.cpp =================================================================== --- source/Expression/ClangExpressionParser.cpp +++ source/Expression/ClangExpressionParser.cpp @@ -541,11 +541,12 @@ bool ir_can_run = ir_for_target.runOnModule(*execution_unit_sp->GetModule()); Error interpret_error; + Process *process = exe_ctx.GetProcessPtr(); + + bool interpret_function_calls = !process ? false : process->CanInterpretFunctionCalls(); + can_interpret = IRInterpreter::CanInterpret(*execution_unit_sp->GetModule(), *execution_unit_sp->GetFunction(), interpret_error, interpret_function_calls); - can_interpret = IRInterpreter::CanInterpret(*execution_unit_sp->GetModule(), *execution_unit_sp->GetFunction(), interpret_error); - Process *process = exe_ctx.GetProcessPtr(); - if (!ir_can_run) { err.SetErrorString("The expression could not be prepared to run in the target"); Index: source/Expression/ClangUserExpression.cpp =================================================================== --- source/Expression/ClangUserExpression.cpp +++ source/Expression/ClangUserExpression.cpp @@ -891,7 +891,8 @@ *m_execution_unit_sp.get(), interpreter_error, function_stack_bottom, - function_stack_top); + function_stack_top, + exe_ctx); if (!interpreter_error.Success()) { Index: source/Expression/IRInterpreter.cpp =================================================================== --- source/Expression/IRInterpreter.cpp +++ source/Expression/IRInterpreter.cpp @@ -10,17 +10,28 @@ #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Module.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObject.h" #include "lldb/Expression/IRMemoryMap.h" #include "lldb/Expression/IRInterpreter.h" #include "lldb/Host/Endian.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallFunctionUsingABI.h" + #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Support/raw_ostream.h" @@ -455,7 +466,8 @@ bool IRInterpreter::CanInterpret (llvm::Module &module, llvm::Function &function, - lldb_private::Error &error) + lldb_private::Error &error, + const bool support_function_calls) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); @@ -507,7 +519,7 @@ return false; } - if (!CanIgnoreCall(call_inst)) + if (!CanIgnoreCall(call_inst) && !support_function_calls) { if (log) log->Printf("Unsupported instruction: %s", PrintValue(ii).c_str()); @@ -611,7 +623,8 @@ lldb_private::IRMemoryMap &memory_map, lldb_private::Error &error, lldb::addr_t stack_frame_bottom, - lldb::addr_t stack_frame_top) + lldb::addr_t stack_frame_top, + lldb_private::ExecutionContext &exe_ctx) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); @@ -668,29 +681,7 @@ { default: break; - case Instruction::Call: - { - const CallInst *call_inst = dyn_cast(inst); - if (!call_inst) - { - if (log) - log->Printf("getOpcode() returns %s, but instruction is not a CallInst", inst->getOpcodeName()); - error.SetErrorToGenericError(); - error.SetErrorString(interpreter_internal_error); - return false; - } - - if (!CanIgnoreCall(call_inst)) - { - if (log) - log->Printf("The interpreter shouldn't have accepted %s", PrintValue(call_inst).c_str()); - error.SetErrorToGenericError(); - error.SetErrorString(interpreter_internal_error); - return false; - } - } - break; case Instruction::Add: case Instruction::Sub: case Instruction::Mul: @@ -1476,6 +1467,242 @@ } } break; + case Instruction::Call: + { + const CallInst *call_inst = dyn_cast(inst); + + if (!call_inst) + { + if (log) + log->Printf("getOpcode() returns %s, but instruction is not a CallInst", inst->getOpcodeName()); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + if (CanIgnoreCall(call_inst)) + break; + + // Get the return type + llvm::Type *returnType = call_inst->getType(); + if (returnType == nullptr) + { + error.SetErrorToGenericError(); + error.SetErrorString("unable to access return type"); + return false; + } + + // Work with void, integer and pointer return types + if (!returnType->isVoidTy() && + !returnType->isIntegerTy() && + !returnType->isPointerTy()) + { + error.SetErrorToGenericError(); + error.SetErrorString("return type is not supported"); + return false; + } + + // Check we can actually get a thread + if (exe_ctx.GetThreadPtr() == nullptr) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("unable to acquire thread"); + return false; + } + + // Make sure we have a valid process + if (!exe_ctx.GetProcessPtr()) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("unable to get the process"); + return false; + } + + // Find the address of the callee function + lldb_private::Scalar I; + const llvm::Value *val = call_inst->getCalledValue(); + + if (!frame.EvaluateValue(I, val, module)) + { + error.SetErrorToGenericError(); + error.SetErrorString("unable to get address of function"); + return false; + } + lldb_private::Address funcAddr(I.ULongLong(LLDB_INVALID_ADDRESS)); + + lldb_private::StreamString error_stream; + lldb_private::EvaluateExpressionOptions options; + + // We generally receive a function pointer which we must dereference + llvm::Type* prototype = val->getType(); + if (!prototype->isPointerTy()) + { + error.SetErrorToGenericError(); + error.SetErrorString("call need function pointer"); + return false; + } + + // Dereference the function pointer + prototype = prototype->getPointerElementType(); + if (!(prototype->isFunctionTy() || prototype->isFunctionVarArg())) + { + error.SetErrorToGenericError(); + error.SetErrorString("call need function pointer"); + return false; + } + + // Find number of arguments + const int numArgs = call_inst->getNumArgOperands(); + + // We work with a fixed array of 16 arguments which is our upper limit + static lldb_private::ABI::CallArgument rawArgs[16]; + if (numArgs >= 16) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("function takes too many arguments"); + return false; + } + + // Push all function arguments to the argument list that will + // be passed to the call function thread plan + for (int i = 0; i < numArgs; i++) + { + // Get details of this argument + llvm::Value *arg_op = call_inst->getArgOperand(i); + llvm::Type *arg_ty = arg_op->getType(); + + // Ensure that this argument is an supported type + if (!arg_ty->isIntegerTy() && !arg_ty->isPointerTy()) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("argument %d must be integer type", i); + return false; + } + + // Extract the arguments value + lldb_private::Scalar tmp_op = 0; + if (!frame.EvaluateValue(tmp_op, arg_op, module)) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("unable to evaluate argument %d", i); + return false; + } + + // Check if this is a string literal or constant string pointer + if (arg_ty->isPointerTy()) + { + // Pointer to just one type + assert(arg_ty->getNumContainedTypes() == 1); + + lldb::addr_t addr = tmp_op.ULongLong(); + size_t dataSize = 0; + + if (memory_map.GetAllocSize(addr, dataSize)) + { + // Create the required buffer + rawArgs[i].size = dataSize; + rawArgs[i].data_ap.reset(new uint8_t[dataSize + 1]); + + // Read string from host memory + memory_map.ReadMemory(rawArgs[i].data_ap.get(), addr, dataSize, error); + if (error.Fail()) + { + assert(!"we have failed to read the string from memory"); + return false; + } + // Add null terminator + rawArgs[i].data_ap[dataSize] = '\0'; + rawArgs[i].type = lldb_private::ABI::CallArgument::HostPointer; + } + else + { + assert(!"unable to locate host data for transfer to device"); + return false; + } + } + else /* if ( arg_ty->isPointerTy() ) */ + { + rawArgs[i].type = lldb_private::ABI::CallArgument::TargetValue; + // Get argument size in bytes + rawArgs[i].size = arg_ty->getIntegerBitWidth() / 8; + // Push value into argument list for thread plan + rawArgs[i].value = tmp_op.ULongLong(); + } + + } + + // Pack the arguments into an llvm::array + llvm::ArrayRef args(rawArgs, numArgs); + + // Setup a thread plan to call the target function + lldb::ThreadPlanSP call_plan_sp + ( + new lldb_private::ThreadPlanCallFunctionUsingABI + ( + exe_ctx.GetThreadRef(), + funcAddr, + *prototype, + *returnType, + args, + options + ) + ); + + // Check if the plan is valid + if (!call_plan_sp || !call_plan_sp->ValidatePlan(&error_stream)) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("unable to make ThreadPlanCallFunctionUsingABI for 0x%llx", I.ULongLong()); + return false; + } + + exe_ctx.GetProcessPtr()->SetRunningUserExpression(true); + + // Execute the actual function call thread plan + lldb::ExpressionResults res = exe_ctx.GetProcessRef().RunThreadPlan(exe_ctx, call_plan_sp, options, error_stream); + + // Check that the thread plan completed successfully + if (res != lldb::ExpressionResults::eExpressionCompleted) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("ThreadPlanCallFunctionUsingABI failed"); + return false; + } + + exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); + + // Void return type + if (returnType->isVoidTy()) + { + // Cant assign to void types, so we leave the frame untouched + } + else + // Integer or pointer return type + if (returnType->isIntegerTy() || returnType->isPointerTy()) + { + // Get the encapsulated return value + lldb::ValueObjectSP retVal = call_plan_sp.get()->GetReturnValueObject(); + + lldb_private::Scalar returnVal = -1; + lldb_private::ValueObject *vobj = retVal.get(); + + // Check if the return value is valid + if (vobj == nullptr || retVal.empty()) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("unable to get the return value"); + return false; + } + + // Extract the return value as a integer + lldb_private::Value & value = vobj->GetValue(); + returnVal = value.GetScalar(); + + // Push the return value as the result + frame.AssignValue(inst, returnVal, module); + } + } + break; } ++frame.m_ii; Index: source/Expression/IRMemoryMap.cpp =================================================================== --- source/Expression/IRMemoryMap.cpp +++ source/Expression/IRMemoryMap.cpp @@ -418,6 +418,32 @@ m_allocations.erase(iter); } +bool +IRMemoryMap::GetAllocSize(lldb::addr_t address, size_t &size) +{ + AllocationMap::iterator iter = FindAllocation(address, size); + if (iter == m_allocations.end()) + return false; + + Allocation &al = iter->second; + + if (address > (al.m_process_start + al.m_size)) + { + size = 0; + return false; + } + + if (address > al.m_process_start) + { + int dif = address - al.m_process_start; + size = al.m_size - dif; + return true; + } + + size = al.m_size; + return true; +} + void IRMemoryMap::WriteMemory (lldb::addr_t process_address, const uint8_t *bytes, size_t size, Error &error) { Index: source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.cpp =================================================================== --- source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.cpp +++ source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.cpp @@ -257,7 +257,7 @@ sp -= argSize; // write this argument onto the stack of the host process - proc.get( )->WriteMemory( sp, arg.data, arg.size, error ); + proc.get( )->WriteMemory( sp, arg.data_ap.get(), arg.size, error ); if ( error.Fail( ) ) return false; Index: source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp =================================================================== --- source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp +++ source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp @@ -182,7 +182,7 @@ addr_t kernel_load_address = SearchForDarwinKernel (process); if (kernel_load_address != LLDB_INVALID_ADDRESS) { - process->SetCanJIT(false); + process->SetCanRunCode(false); return new DynamicLoaderDarwinKernel (process, kernel_load_address); } return NULL; Index: source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp =================================================================== --- source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp +++ source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp @@ -167,6 +167,9 @@ // Disable JIT for hexagon targets because its not supported m_process->SetCanJIT(false); + + // Enable Interpreting of function call expressions + m_process->SetCanInterpretFunctionCalls(true); // Add the current executable to the module list ModuleList module_list; Index: source/Target/CMakeLists.txt =================================================================== --- source/Target/CMakeLists.txt +++ source/Target/CMakeLists.txt @@ -40,6 +40,7 @@ ThreadPlan.cpp ThreadPlanBase.cpp ThreadPlanCallFunction.cpp + ThreadPlanCallFunctionUsingABI.cpp ThreadPlanCallUserExpression.cpp ThreadPlanPython.cpp ThreadPlanRunToAddress.cpp Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -754,6 +754,7 @@ m_force_next_event_delivery (false), m_last_broadcast_state (eStateInvalid), m_destroy_in_process (false), + m_can_interpret_function_calls(false), m_can_jit(eCanJITDontKnow) { CheckInWithManager (); @@ -3011,6 +3012,13 @@ m_can_jit = (can_jit ? eCanJITYes : eCanJITNo); } +void +Process::SetCanRunCode (bool can_run_code) +{ + SetCanJIT(can_run_code); + m_can_interpret_function_calls = can_run_code; +} + Error Process::DeallocateMemory (addr_t ptr) { Index: source/Target/ThreadPlanCallFunction.cpp =================================================================== --- source/Target/ThreadPlanCallFunction.cpp +++ source/Target/ThreadPlanCallFunction.cpp @@ -147,15 +147,16 @@ m_trap_exceptions (options.GetTrapExceptions()), m_function_addr (function), m_function_sp (0), - m_return_type (return_type), m_takedown_done (false), m_should_clear_objc_exception_bp(false), m_should_clear_cxx_exception_bp (false), - m_stop_address (LLDB_INVALID_ADDRESS) + m_stop_address (LLDB_INVALID_ADDRESS), + m_return_type (return_type) { - lldb::addr_t start_load_addr; - ABI *abi; - lldb::addr_t function_load_addr; + lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS; + ABI *abi = nullptr; + if (!ConstructorSetup (thread, abi, start_load_addr, function_load_addr)) return; @@ -171,6 +172,27 @@ m_valid = true; } +ThreadPlanCallFunction::ThreadPlanCallFunction(Thread &thread, + const Address &function, + const EvaluateExpressionOptions &options) : + ThreadPlan(ThreadPlan::eKindCallFunction, "Call function plan", thread, eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), + m_stop_other_threads(options.GetStopOthers()), + m_unwind_on_error(options.DoesUnwindOnError()), + m_ignore_breakpoints(options.DoesIgnoreBreakpoints()), + m_debug_execution(options.GetDebug()), + m_trap_exceptions(options.GetTrapExceptions()), + m_function_addr(function), + m_function_sp(0), + m_takedown_done(false), + m_should_clear_objc_exception_bp(false), + m_should_clear_cxx_exception_bp(false), + m_stop_address(LLDB_INVALID_ADDRESS), + m_return_type(ClangASTType()) +{ + +} + ThreadPlanCallFunction::~ThreadPlanCallFunction () { DoTakedown(PlanSucceeded()); @@ -222,13 +244,7 @@ { if (success) { - ProcessSP process_sp (m_thread.GetProcess()); - const ABI *abi = process_sp ? process_sp->GetABI().get() : NULL; - if (abi && m_return_type.IsValid()) - { - const bool persistent = false; - m_return_valobj_sp = abi->GetReturnValueObject (m_thread, m_return_type, persistent); - } + SetReturnValue(); } if (log) log->Printf ("ThreadPlanCallFunction(%p): DoTakedown called for thread 0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n", @@ -574,3 +590,15 @@ return GetThread().RestoreThreadStateFromCheckpoint(m_stored_thread_state); } + +void +ThreadPlanCallFunction::SetReturnValue() +{ + ProcessSP process_sp(m_thread.GetProcess()); + const ABI *abi = process_sp ? process_sp->GetABI().get() : NULL; + if (abi && m_return_type.IsValid()) + { + const bool persistent = false; + m_return_valobj_sp = abi->GetReturnValueObject(m_thread, m_return_type, persistent); + } +} Index: source/Target/ThreadPlanCallFunctionUsingABI.cpp =================================================================== --- source/Target/ThreadPlanCallFunctionUsingABI.cpp +++ source/Target/ThreadPlanCallFunctionUsingABI.cpp @@ -0,0 +1,91 @@ +//===-- ThreadPlanCallFunctionUsingABI.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanCallFunctionUsingABI.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Address.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//-------------------------------------------------------------------------------------------- +// ThreadPlanCallFunctionUsingABI: Plan to call a single function using the ABI instead of JIT +//------------------------------------------------------------------------------------------- +ThreadPlanCallFunctionUsingABI::ThreadPlanCallFunctionUsingABI (Thread &thread, + const Address &function, + llvm::Type &prototype, + llvm::Type &return_type, + llvm::ArrayRef args, + const EvaluateExpressionOptions &options) : + ThreadPlanCallFunction(thread,function,options), + m_return_type(return_type) +{ + lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS; + ABI *abi = nullptr; + + if (!ConstructorSetup(thread, abi, start_load_addr, function_load_addr)) + return; + + if (!abi->PrepareTrivialCall(thread, + m_function_sp, + function_load_addr, + start_load_addr, + prototype, + args)) + return; + + ReportRegisterState("ABI Function call was set up. Register state was:"); + + m_valid = true; +} + +ThreadPlanCallFunctionUsingABI::~ThreadPlanCallFunctionUsingABI() +{ + +} + +void +ThreadPlanCallFunctionUsingABI::GetDescription(Stream *s, DescriptionLevel level) +{ + if (level == eDescriptionLevelBrief) + { + s->Printf("Function call thread plan using ABI instead of JIT"); + } + else + { + TargetSP target_sp(m_thread.CalculateTarget()); + s->Printf("Thread plan to call 0x%" PRIx64" using ABI instead of JIT", m_function_addr.GetLoadAddress(target_sp.get())); + } +} + +void +ThreadPlanCallFunctionUsingABI::SetReturnValue() +{ + ProcessSP process_sp(m_thread.GetProcess()); + const ABI *abi = process_sp ? process_sp->GetABI().get() : NULL; + + // Ask the abi for the return value + if (abi) + { + const bool persistent = false; + m_return_valobj_sp = abi->GetReturnValueObject(m_thread, m_return_type, persistent); + } +} Index: test/expression_command/call-function/TestCallUserDefinedFunction.py =================================================================== --- test/expression_command/call-function/TestCallUserDefinedFunction.py +++ test/expression_command/call-function/TestCallUserDefinedFunction.py @@ -0,0 +1,68 @@ +""" +Test calling user defined functions using expression evaluation. + +Note: + LLDBs current first choice of evaluating functions is using the IR interpreter, + which is only supported on Hexagon. Otherwise JIT is used for the evaluation. + +""" + +import unittest2 +import lldb +import lldbutil +from lldbtest import * + +class ExprCommandCallUserDefinedFunction(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.c. + self.line = line_number('main.cpp', + '// Please test these expressions while stopped at this line:') + @skipUnlessDarwin + @dsym_test + @expectedFailureDarwin("llvm.org/pr20274") # intermittent failure on MacOSX + def test_with_dsym(self): + """Test return values of user defined function calls.""" + self.buildDsym() + self.call_function() + + @dwarf_test + @expectedFailureFreeBSD("llvm.org/pr20274") # intermittent failure + def test_with_dwarf(self): + """Test return values of user defined function calls.""" + self.buildDwarf() + self.call_functions() + + def call_functions(self): + """Test return values of user defined function calls.""" + + # Set breakpoint in main and run exe + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # Test recursive function call. + self.expect("expr fib(5)", substrs = ['$0 = 5']) + + # Test function with more than one paramter + self.expect("expr add(4,8)", substrs = ['$1 = 12']) + + # Test nesting function calls in function paramters + self.expect("expr add(add(5,2),add(3,4))", substrs = ['$2 = 14']) + self.expect("expr add(add(5,2),fib(5))", substrs = ['$3 = 12']) + + # Test function with pointer paramter + self.expect("exp stringCompare((const char*) \"Hello world\")", substrs = ['$4 = true']) + self.expect("exp stringCompare((const char*) \"Hellworld\")", substrs = ['$5 = false']) + + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() Index: test/expression_command/call-function/main.cpp =================================================================== --- test/expression_command/call-function/main.cpp +++ test/expression_command/call-function/main.cpp @@ -1,5 +1,6 @@ #include #include +#include struct Five { @@ -14,6 +15,30 @@ return my_five; } +unsigned int +fib(unsigned int n) +{ + if (n < 2) + return n; + else + return fib(n - 1) + fib(n - 2); +} + +int +add(int a, int b) +{ + return a + b; +} + +bool +stringCompare(const char *str) +{ + if (strcmp( str, "Hello world" ) == 0) + return true; + else + return false; +} + int main (int argc, char const *argv[]) { std::string str = "Hello world";