From 8201baef2b1374709ccb84e2c1e143c155ba67cd Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Thu, 10 Dec 2020 09:39:41 -0500 Subject: [PATCH] GP-71: Prepping for source release. --- .gitignore | 4 +- DebuggerDevGuide.md | 107 + DevGuide.md | 8 + .../Debug/AnnotationValidator/Module.manifest | 0 Ghidra/Debug/AnnotationValidator/build.gradle | 45 + .../certification.manifest | 6 + .../AbstractDBAnnotationValidator.java | 43 + .../util/database/annotproc/AccessSpec.java | 62 + .../annotproc/DBAnnotatedColumnValidator.java | 56 + .../annotproc/DBAnnotatedFieldValidator.java | 214 ++ .../annotproc/DBAnnotatedObjectProcessor.java | 96 + .../annotproc/DBAnnotatedObjectValidator.java | 137 + .../database/annotproc/ValidationContext.java | 326 ++ .../javax.annotation.processing.Processor | 1 + .../Debugger-agent-dbgeng/Module.manifest | 2 + .../Debug/Debugger-agent-dbgeng/build.gradle | 66 + .../certification.manifest | 8 + .../src/javaprovider/cpp/javaprovider.cpp | 224 ++ .../src/javaprovider/def/javaprovider.def | 13 + .../src/javaprovider/headers/afxres.h | 16 + .../src/javaprovider/headers/resource.h | 31 + .../src/javaprovider/rc/javaprovider.rc | 0 .../DbgEngInJvmDebuggerModelFactory.java | 48 + .../agent/dbgeng/dbgeng/COMUtilsExtra.java | 72 + .../main/java/agent/dbgeng/dbgeng/DbgEng.java | 163 + .../agent/dbgeng/dbgeng/DebugAdvanced.java | 83 + .../agent/dbgeng/dbgeng/DebugBreakpoint.java | 122 + .../java/agent/dbgeng/dbgeng/DebugClient.java | 398 +++ .../dbgeng/dbgeng/DebugClientReentrant.java | 46 + .../agent/dbgeng/dbgeng/DebugControl.java | 315 ++ .../dbgeng/dbgeng/DebugControlReentrant.java | 27 + .../agent/dbgeng/dbgeng/DebugDataSpaces.java | 285 ++ .../dbgeng/dbgeng/DebugEventCallbacks.java | 116 + .../dbgeng/dbgeng/DebugEventInformation.java | 70 + .../dbgeng/dbgeng/DebugExceptionRecord64.java | 41 + .../dbgeng/dbgeng/DebugInputCallbacks.java | 32 + .../java/agent/dbgeng/dbgeng/DebugModule.java | 47 + .../agent/dbgeng/dbgeng/DebugModuleInfo.java | 44 + .../dbgeng/dbgeng/DebugOutputCallbacks.java | 32 + .../agent/dbgeng/dbgeng/DebugProcessId.java | 61 + .../agent/dbgeng/dbgeng/DebugProcessInfo.java | 36 + .../agent/dbgeng/dbgeng/DebugRegisters.java | 158 + .../dbgeng/dbgeng/DebugRunningProcess.java | 123 + .../agent/dbgeng/dbgeng/DebugServerId.java | 58 + .../agent/dbgeng/dbgeng/DebugSessionId.java | 61 + .../dbgeng/dbgeng/DebugStackInformation.java | 37 + .../agent/dbgeng/dbgeng/DebugSymbolEntry.java | 56 + .../agent/dbgeng/dbgeng/DebugSymbolId.java | 34 + .../agent/dbgeng/dbgeng/DebugSymbolName.java | 34 + .../agent/dbgeng/dbgeng/DebugSymbols.java | 78 + .../dbgeng/dbgeng/DebugSystemObjects.java | 102 + .../agent/dbgeng/dbgeng/DebugThreadId.java | 61 + .../agent/dbgeng/dbgeng/DebugThreadInfo.java | 35 + .../java/agent/dbgeng/dbgeng/DebugValue.java | 457 +++ .../dbgeng/dbgeng/err/DbgEngException.java | 29 + .../dbgeng/err/DbgEngRuntimeException.java | 29 + .../agent/dbgeng/dbgeng/ext/JavaProvider.java | 49 + .../util/DebugEventCallbacksAdapter.java | 125 + .../agent/dbgeng/gadp/DbgEngGadpServer.java | 242 ++ .../gadp/DbgEngLocalDebuggerModelFactory.java | 85 + .../impl/AbstractClientThreadExecutor.java | 236 ++ .../gadp/impl/DbgEngClientThreadExecutor.java | 63 + .../gadp/impl/DbgEngGadpServerImpl.java | 64 + .../agent/dbgeng/impl/dbgeng/DbgEngUtil.java | 86 + .../impl/dbgeng/DebugRunningProcessImpl.java | 66 + .../dbgeng/advanced/DebugAdvancedImpl1.java | 38 + .../dbgeng/advanced/DebugAdvancedImpl2.java | 85 + .../dbgeng/advanced/DebugAdvancedImpl3.java | 28 + .../advanced/DebugAdvancedInternal.java | 88 + .../breakpoint/DebugBreakpointImpl1.java | 164 + .../breakpoint/DebugBreakpointImpl2.java | 28 + .../breakpoint/DebugBreakpointImpl3.java | 28 + .../breakpoint/DebugBreakpointInternal.java | 66 + .../impl/dbgeng/client/DebugClientImpl1.java | 323 ++ .../impl/dbgeng/client/DebugClientImpl2.java | 50 + .../impl/dbgeng/client/DebugClientImpl3.java | 68 + .../impl/dbgeng/client/DebugClientImpl4.java | 39 + .../impl/dbgeng/client/DebugClientImpl5.java | 82 + .../impl/dbgeng/client/DebugClientImpl6.java | 35 + .../impl/dbgeng/client/DebugClientImpl7.java | 28 + .../dbgeng/client/DebugClientInternal.java | 96 + .../dbgeng/control/DebugControlImpl1.java | 288 ++ .../dbgeng/control/DebugControlImpl2.java | 28 + .../dbgeng/control/DebugControlImpl3.java | 28 + .../dbgeng/control/DebugControlImpl4.java | 95 + .../dbgeng/control/DebugControlImpl5.java | 28 + .../dbgeng/control/DebugControlImpl6.java | 28 + .../dbgeng/control/DebugControlImpl7.java | 28 + .../dbgeng/control/DebugControlInternal.java | 81 + .../dataspaces/DebugDataSpacesImpl1.java | 271 ++ .../dataspaces/DebugDataSpacesImpl2.java | 52 + .../dataspaces/DebugDataSpacesImpl3.java | 28 + .../dataspaces/DebugDataSpacesImpl4.java | 28 + .../dataspaces/DebugDataSpacesInternal.java | 67 + .../WrapCallbackIDebugEventCallbacks.java | 296 ++ .../WrapCallbackIDebugEventCallbacksWide.java | 295 ++ .../io/WrapCallbackIDebugInputCallbacks.java | 122 + .../io/WrapCallbackIDebugOutputCallbacks.java | 82 + ...WrapCallbackIDebugOutputCallbacksWide.java | 84 + .../dbgeng/registers/DebugRegistersImpl1.java | 154 + .../dbgeng/registers/DebugRegistersImpl2.java | 50 + .../registers/DebugRegistersInternal.java | 53 + .../impl/dbgeng/symbols/DebugModuleImpl.java | 70 + .../dbgeng/symbols/DebugSymbolsImpl1.java | 213 ++ .../dbgeng/symbols/DebugSymbolsImpl2.java | 48 + .../dbgeng/symbols/DebugSymbolsImpl3.java | 101 + .../dbgeng/symbols/DebugSymbolsImpl4.java | 28 + .../dbgeng/symbols/DebugSymbolsImpl5.java | 28 + .../dbgeng/symbols/DebugSymbolsInternal.java | 72 + .../sysobj/DebugSystemObjectsImpl1.java | 249 ++ .../sysobj/DebugSystemObjectsImpl2.java | 28 + .../sysobj/DebugSystemObjectsImpl3.java | 96 + .../sysobj/DebugSystemObjectsImpl4.java | 28 + .../sysobj/DebugSystemObjectsInternal.java | 67 + .../agent/dbgeng/jna/dbgeng/DbgEngNative.java | 432 +++ .../dbgeng/jna/dbgeng/Kernel32Extra.java | 126 + .../agent/dbgeng/jna/dbgeng/ToolhelpUtil.java | 107 + .../dbgeng/jna/dbgeng/UnknownWithUtils.java | 43 + .../agent/dbgeng/jna/dbgeng/WinNTExtra.java | 281 ++ .../jna/dbgeng/advanced/IDebugAdvanced.java | 46 + .../jna/dbgeng/advanced/IDebugAdvanced2.java | 60 + .../jna/dbgeng/advanced/IDebugAdvanced3.java | 53 + .../dbgeng/advanced/WrapIDebugAdvanced.java | 45 + .../dbgeng/advanced/WrapIDebugAdvanced2.java | 70 + .../dbgeng/advanced/WrapIDebugAdvanced3.java | 56 + .../dbgeng/breakpoint/IDebugBreakpoint.java | 104 + .../dbgeng/breakpoint/IDebugBreakpoint2.java | 52 + .../dbgeng/breakpoint/IDebugBreakpoint3.java | 40 + .../breakpoint/WrapIDebugBreakpoint.java | 143 + .../breakpoint/WrapIDebugBreakpoint2.java | 56 + .../breakpoint/WrapIDebugBreakpoint3.java | 38 + .../jna/dbgeng/client/IDebugClient.java | 184 ++ .../jna/dbgeng/client/IDebugClient2.java | 62 + .../jna/dbgeng/client/IDebugClient3.java | 54 + .../jna/dbgeng/client/IDebugClient4.java | 59 + .../jna/dbgeng/client/IDebugClient5.java | 135 + .../jna/dbgeng/client/IDebugClient6.java | 40 + .../jna/dbgeng/client/IDebugClient7.java | 41 + .../jna/dbgeng/client/WrapIDebugClient.java | 281 ++ .../jna/dbgeng/client/WrapIDebugClient2.java | 79 + .../jna/dbgeng/client/WrapIDebugClient3.java | 64 + .../jna/dbgeng/client/WrapIDebugClient4.java | 72 + .../jna/dbgeng/client/WrapIDebugClient5.java | 206 ++ .../jna/dbgeng/client/WrapIDebugClient6.java | 42 + .../jna/dbgeng/client/WrapIDebugClient7.java | 41 + .../jna/dbgeng/control/IDebugControl.java | 187 ++ .../jna/dbgeng/control/IDebugControl2.java | 43 + .../jna/dbgeng/control/IDebugControl3.java | 48 + .../jna/dbgeng/control/IDebugControl4.java | 106 + .../jna/dbgeng/control/IDebugControl5.java | 40 + .../jna/dbgeng/control/IDebugControl6.java | 41 + .../jna/dbgeng/control/IDebugControl7.java | 36 + .../jna/dbgeng/control/WrapIDebugControl.java | 181 ++ .../dbgeng/control/WrapIDebugControl2.java | 31 + .../dbgeng/control/WrapIDebugControl3.java | 31 + .../dbgeng/control/WrapIDebugControl4.java | 79 + .../dbgeng/control/WrapIDebugControl5.java | 31 + .../dbgeng/control/WrapIDebugControl6.java | 38 + .../dbgeng/control/WrapIDebugControl7.java | 31 + .../dbgeng/dataspaces/IDebugDataSpaces.java | 104 + .../dbgeng/dataspaces/IDebugDataSpaces2.java | 46 + .../dbgeng/dataspaces/IDebugDataSpaces3.java | 40 + .../dbgeng/dataspaces/IDebugDataSpaces4.java | 45 + .../dataspaces/WrapIDebugDataSpaces.java | 141 + .../dataspaces/WrapIDebugDataSpaces2.java | 42 + .../dataspaces/WrapIDebugDataSpaces3.java | 34 + .../dataspaces/WrapIDebugDataSpaces4.java | 34 + .../event/CallbackIDebugEventCallbacks.java | 21 + .../CallbackIDebugEventCallbacksWide.java | 22 + .../CallbackIDebugEventContextCallbacks.java | 22 + .../dbgeng/event/IDebugEventCallbacks.java | 59 + .../event/IDebugEventCallbacksWide.java | 60 + .../event/IDebugEventContextCallbacks.java | 66 + .../event/ListenerIDebugEventCallbacks.java | 184 ++ .../ListenerIDebugEventCallbacksWide.java | 185 ++ .../ListenerIDebugEventContextCallbacks.java | 198 ++ .../dbgeng/event/MarkerEventCallbacks.java | 20 + .../event/VTableIDebugEventCallbacks.java | 127 + .../event/VTableIDebugEventCallbacksWide.java | 126 + .../VTableIDebugEventContextCallbacks.java | 132 + .../io/CallbackIDebugInputCallbacks.java | 21 + .../io/CallbackIDebugOutputCallbacks.java | 21 + .../io/CallbackIDebugOutputCallbacks2.java | 21 + .../io/CallbackIDebugOutputCallbacksWide.java | 22 + .../jna/dbgeng/io/IDebugInputCallbacks.java | 28 + .../jna/dbgeng/io/IDebugOutputCallbacks.java | 26 + .../jna/dbgeng/io/IDebugOutputCallbacks2.java | 29 + .../dbgeng/io/IDebugOutputCallbacksWide.java | 27 + .../io/ListenerIDebugInputCallbacks.java | 72 + .../io/ListenerIDebugOutputCallbacks.java | 64 + .../io/ListenerIDebugOutputCallbacks2.java | 81 + .../io/ListenerIDebugOutputCallbacksWide.java | 65 + .../jna/dbgeng/io/MarkerInputCallbacks.java | 20 + .../jna/dbgeng/io/MarkerOutputCallbacks.java | 20 + .../dbgeng/io/VTableIDebugInputCallbacks.java | 66 + .../io/VTableIDebugOutputCallbacks.java | 61 + .../io/VTableIDebugOutputCallbacks2.java | 50 + .../io/VTableIDebugOutputCallbacksWide.java | 60 + .../jna/dbgeng/registers/IDebugRegisters.java | 75 + .../dbgeng/registers/IDebugRegisters2.java | 92 + .../dbgeng/registers/WrapIDebugRegisters.java | 94 + .../registers/WrapIDebugRegisters2.java | 124 + .../jna/dbgeng/symbols/IDebugSymbolGroup.java | 22 + .../dbgeng/symbols/IDebugSymbolGroup2.java | 22 + .../jna/dbgeng/symbols/IDebugSymbols.java | 120 + .../jna/dbgeng/symbols/IDebugSymbols2.java | 48 + .../jna/dbgeng/symbols/IDebugSymbols3.java | 126 + .../jna/dbgeng/symbols/IDebugSymbols4.java | 42 + .../jna/dbgeng/symbols/IDebugSymbols5.java | 37 + .../jna/dbgeng/symbols/WrapIDebugSymbols.java | 108 + .../dbgeng/symbols/WrapIDebugSymbols2.java | 40 + .../dbgeng/symbols/WrapIDebugSymbols3.java | 83 + .../dbgeng/symbols/WrapIDebugSymbols4.java | 31 + .../dbgeng/symbols/WrapIDebugSymbols5.java | 31 + .../dbgeng/sysobj/IDebugSystemObjects.java | 101 + .../dbgeng/sysobj/IDebugSystemObjects2.java | 40 + .../dbgeng/sysobj/IDebugSystemObjects3.java | 57 + .../dbgeng/sysobj/IDebugSystemObjects4.java | 37 + .../sysobj/WrapIDebugSystemObjects.java | 123 + .../sysobj/WrapIDebugSystemObjects2.java | 33 + .../sysobj/WrapIDebugSystemObjects3.java | 63 + .../sysobj/WrapIDebugSystemObjects4.java | 33 + .../jna/javaprovider/JavaProviderNative.java | 25 + .../java/agent/dbgeng/manager/DbgCause.java | 33 + .../java/agent/dbgeng/manager/DbgCommand.java | 79 + .../java/agent/dbgeng/manager/DbgEvent.java | 63 + .../agent/dbgeng/manager/DbgEventHandler.java | 62 + .../dbgeng/manager/DbgEventsListener.java | 247 ++ .../manager/DbgEventsListenerAdapter.java | 145 + .../java/agent/dbgeng/manager/DbgManager.java | 351 +++ .../dbgeng/manager/DbgMemoryOperations.java | 70 + .../java/agent/dbgeng/manager/DbgModule.java | 41 + .../agent/dbgeng/manager/DbgModuleMemory.java | 43 + .../dbgeng/manager/DbgModuleSection.java | 38 + .../java/agent/dbgeng/manager/DbgProcess.java | 214 ++ .../java/agent/dbgeng/manager/DbgReason.java | 45 + .../java/agent/dbgeng/manager/DbgSession.java | 73 + .../agent/dbgeng/manager/DbgStackFrame.java | 71 + .../manager/DbgStackFrameOperations.java | 72 + .../java/agent/dbgeng/manager/DbgState.java | 80 + .../dbgeng/manager/DbgStateListener.java | 36 + .../java/agent/dbgeng/manager/DbgThread.java | 136 + .../manager/breakpoint/DbgBreakpointDisp.java | 32 + .../manager/breakpoint/DbgBreakpointInfo.java | 272 ++ .../breakpoint/DbgBreakpointInsertions.java | 84 + .../breakpoint/DbgBreakpointLocation.java | 138 + .../manager/breakpoint/DbgBreakpointType.java | 65 + .../manager/cmd/AbstractDbgCommand.java | 63 + .../manager/cmd/DbgAddProcessCommand.java | 37 + .../manager/cmd/DbgAddSessionCommand.java | 37 + .../dbgeng/manager/cmd/DbgAttachCommand.java | 82 + .../manager/cmd/DbgAttachKernelCommand.java | 68 + .../dbgeng/manager/cmd/DbgCommandError.java | 55 + .../manager/cmd/DbgConsoleExecCommand.java | 68 + .../manager/cmd/DbgContinueCommand.java | 52 + .../cmd/DbgDeleteBreakpointsCommand.java | 44 + .../dbgeng/manager/cmd/DbgDetachCommand.java | 55 + .../cmd/DbgDisableBreakpointsCommand.java | 44 + .../cmd/DbgEnableBreakpointsCommand.java | 43 + .../manager/cmd/DbgEvaluateCommand.java | 42 + .../cmd/DbgFileExecAndSymbolsCommand.java | 37 + .../cmd/DbgInsertBreakpointCommand.java | 100 + .../dbgeng/manager/cmd/DbgKillCommand.java | 39 + .../manager/cmd/DbgLaunchProcessCommand.java | 83 + .../cmd/DbgListAvailableProcessesCommand.java | 58 + .../cmd/DbgListBreakpointsCommand.java | 50 + .../DbgListKernelMemoryRegionsCommand.java | 93 + .../manager/cmd/DbgListMappingsCommand.java | 69 + .../cmd/DbgListMemoryRegionsCommand.java | 93 + .../manager/cmd/DbgListModulesCommand.java | 66 + .../manager/cmd/DbgListProcessesCommand.java | 70 + .../DbgListRegisterDescriptionsCommand.java | 48 + .../manager/cmd/DbgListSessionsCommand.java | 67 + .../manager/cmd/DbgListThreadsCommand.java | 69 + .../manager/cmd/DbgOpenDumpCommand.java | 73 + .../dbgeng/manager/cmd/DbgPendingCommand.java | 205 ++ .../manager/cmd/DbgProcessSelectCommand.java | 47 + .../manager/cmd/DbgReadBusDataCommand.java | 62 + .../manager/cmd/DbgReadControlCommand.java | 57 + .../cmd/DbgReadDebuggerDataCommand.java | 54 + .../dbgeng/manager/cmd/DbgReadIoCommand.java | 62 + .../manager/cmd/DbgReadMemoryCommand.java | 54 + .../cmd/DbgReadPhysicalMemoryCommand.java | 55 + .../manager/cmd/DbgReadRegistersCommand.java | 69 + .../manager/cmd/DbgRemoveProcessCommand.java | 37 + .../manager/cmd/DbgRemoveSessionCommand.java | 37 + .../dbgeng/manager/cmd/DbgRunCommand.java | 61 + .../manager/cmd/DbgSessionSelectCommand.java | 47 + .../cmd/DbgStackListFramesCommand.java | 68 + .../dbgeng/manager/cmd/DbgStepCommand.java | 64 + .../manager/cmd/DbgThreadSelectCommand.java | 46 + .../manager/cmd/DbgWriteBusDataCommand.java | 52 + .../manager/cmd/DbgWriteControlCommand.java | 47 + .../dbgeng/manager/cmd/DbgWriteIoCommand.java | 52 + .../manager/cmd/DbgWriteMemoryCommand.java | 44 + .../cmd/DbgWritePhysicalMemoryCommand.java | 45 + .../manager/cmd/DbgWriteRegistersCommand.java | 71 + .../evt/AbstractDbgCompletedCommandEvent.java | 40 + .../dbgeng/manager/evt/AbstractDbgEvent.java | 85 + .../evt/DbgBreakpointCreatedEvent.java | 47 + .../evt/DbgBreakpointDeletedEvent.java | 46 + .../manager/evt/DbgBreakpointEvent.java | 25 + .../evt/DbgBreakpointModifiedEvent.java | 56 + .../manager/evt/DbgCommandDoneEvent.java | 41 + .../manager/evt/DbgCommandErrorEvent.java | 48 + .../manager/evt/DbgCommandRunningEvent.java | 36 + .../manager/evt/DbgConsoleOutputEvent.java | 37 + .../dbgeng/manager/evt/DbgExceptionEvent.java | 25 + .../manager/evt/DbgModuleLoadedEvent.java | 25 + .../manager/evt/DbgModuleUnloadedEvent.java | 25 + .../manager/evt/DbgProcessCreatedEvent.java | 25 + .../manager/evt/DbgProcessExitedEvent.java | 31 + .../manager/evt/DbgProcessSelectedEvent.java | 52 + .../dbgeng/manager/evt/DbgRunningEvent.java | 53 + .../manager/evt/DbgSessionSelectedEvent.java | 52 + .../manager/evt/DbgStateChangedEvent.java | 53 + .../dbgeng/manager/evt/DbgStoppedEvent.java | 64 + .../dbgeng/manager/evt/DbgSystemsEvent.java | 34 + .../manager/evt/DbgThreadCreatedEvent.java | 25 + .../manager/evt/DbgThreadExitedEvent.java | 24 + .../manager/evt/DbgThreadSelectedEvent.java | 67 + .../impl/DbgDebugEventCallbacksAdapter.java | 131 + .../manager/impl/DbgDebugOutputCallbacks.java | 34 + .../dbgeng/manager/impl/DbgManagerImpl.java | 1508 +++++++++ .../dbgeng/manager/impl/DbgMinimalSymbol.java | 48 + .../dbgeng/manager/impl/DbgModuleImpl.java | 105 + .../manager/impl/DbgModuleMemoryImpl.java | 104 + .../dbgeng/manager/impl/DbgProcessImpl.java | 360 +++ .../dbgeng/manager/impl/DbgRegister.java | 89 + .../dbgeng/manager/impl/DbgRegisterSet.java | 68 + .../dbgeng/manager/impl/DbgSectionImpl.java | 54 + .../dbgeng/manager/impl/DbgSessionImpl.java | 152 + .../manager/impl/DbgStackFrameImpl.java | 162 + .../dbgeng/manager/impl/DbgThreadImpl.java | 281 ++ .../reason/DbgEndSteppingRangeReason.java | 34 + .../manager/reason/DbgExitNormallyReason.java | 34 + .../manager/reason/DbgExitedReason.java | 45 + .../reason/DbgSignalReceivedReason.java | 45 + .../agent/dbgeng/model/AbstractDbgModel.java | 40 + .../iface1/DbgModelSelectableObject.java | 56 + .../DbgModelTargetAccessConditioned.java | 36 + .../iface1/DbgModelTargetAttachable.java | 31 + .../model/iface1/DbgModelTargetAttacher.java | 76 + .../model/iface1/DbgModelTargetBptHelper.java | 44 + .../model/iface1/DbgModelTargetDeletable.java | 36 + .../iface1/DbgModelTargetDetachable.java | 40 + .../iface1/DbgModelTargetEnvironment.java | 41 + .../iface1/DbgModelTargetEventScope.java | 29 + .../DbgModelTargetExecutionStateful.java | 52 + .../iface1/DbgModelTargetFocusScope.java | 89 + .../iface1/DbgModelTargetInterpreter.java | 43 + .../iface1/DbgModelTargetInterruptible.java | 39 + .../model/iface1/DbgModelTargetKillable.java | 40 + .../model/iface1/DbgModelTargetLauncher.java | 46 + .../model/iface1/DbgModelTargetMethod.java | 29 + .../model/iface1/DbgModelTargetResumable.java | 40 + .../model/iface1/DbgModelTargetSteppable.java | 68 + .../model/iface2/DbgModelTargetAvailable.java | 25 + .../DbgModelTargetAvailableContainer.java | 22 + .../DbgModelTargetBreakpointContainer.java | 84 + .../DbgModelTargetBreakpointLocation.java | 31 + .../iface2/DbgModelTargetBreakpointSpec.java | 181 ++ .../model/iface2/DbgModelTargetConnector.java | 41 + .../DbgModelTargetConnectorContainer.java | 22 + .../iface2/DbgModelTargetDebugContainer.java | 21 + .../iface2/DbgModelTargetEnvironmentEx.java | 42 + .../iface2/DbgModelTargetMemoryContainer.java | 35 + .../iface2/DbgModelTargetMemoryRegion.java | 36 + .../model/iface2/DbgModelTargetModule.java | 61 + .../iface2/DbgModelTargetModuleContainer.java | 38 + .../iface2/DbgModelTargetModuleSection.java | 27 + .../DbgModelTargetModuleSectionContainer.java | 21 + .../model/iface2/DbgModelTargetObject.java | 84 + .../model/iface2/DbgModelTargetProcess.java | 89 + .../DbgModelTargetProcessContainer.java | 32 + .../model/iface2/DbgModelTargetRegister.java | 29 + .../iface2/DbgModelTargetRegisterBank.java | 123 + .../DbgModelTargetRegisterContainer.java | 127 + .../model/iface2/DbgModelTargetRoot.java | 33 + .../model/iface2/DbgModelTargetSession.java | 92 + .../DbgModelTargetSessionAttributes.java | 26 + ...bgModelTargetSessionAttributesMachine.java | 24 + .../DbgModelTargetSessionContainer.java | 34 + .../model/iface2/DbgModelTargetStack.java | 26 + .../iface2/DbgModelTargetStackFrame.java | 100 + .../model/iface2/DbgModelTargetSymbol.java | 32 + .../iface2/DbgModelTargetSymbolContainer.java | 27 + .../model/iface2/DbgModelTargetTTD.java | 54 + .../model/iface2/DbgModelTargetThread.java | 78 + .../iface2/DbgModelTargetThreadContainer.java | 34 + .../impl/DbgModelDefaultTargetModelRoot.java | 26 + .../agent/dbgeng/model/impl/DbgModelImpl.java | 113 + .../dbgeng/model/impl/DbgModelImplUtils.java | 38 + .../DbgModelTargetAvailableContainerImpl.java | 65 + .../impl/DbgModelTargetAvailableImpl.java | 69 + ...DbgModelTargetBreakpointContainerImpl.java | 104 + .../DbgModelTargetBreakpointSpecImpl.java | 146 + .../DbgModelTargetConnectorContainerImpl.java | 65 + .../DbgModelTargetDebugContainerImpl.java | 41 + .../DbgModelTargetKernelConnectorImpl.java | 82 + .../DbgModelTargetMemoryContainerImpl.java | 255 ++ .../impl/DbgModelTargetMemoryRegionImpl.java | 125 + .../DbgModelTargetModuleContainerImpl.java | 128 + .../model/impl/DbgModelTargetModuleImpl.java | 88 + ...ModelTargetModuleSectionContainerImpl.java | 62 + .../impl/DbgModelTargetModuleSectionImpl.java | 54 + .../model/impl/DbgModelTargetObjectImpl.java | 188 ++ ...ModelTargetProcessAttachConnectorImpl.java | 81 + .../DbgModelTargetProcessContainerImpl.java | 135 + .../model/impl/DbgModelTargetProcessImpl.java | 225 ++ ...ModelTargetProcessLaunchConnectorImpl.java | 81 + .../DbgModelTargetRegisterContainerImpl.java | 172 ++ .../impl/DbgModelTargetRegisterImpl.java | 70 + .../model/impl/DbgModelTargetRootImpl.java | 132 + .../DbgModelTargetSessionAttributesImpl.java | 75 + ...delTargetSessionAttributesMachineImpl.java | 84 + .../DbgModelTargetSessionContainerImpl.java | 84 + .../model/impl/DbgModelTargetSessionImpl.java | 97 + .../impl/DbgModelTargetStackFrameImpl.java | 162 + .../model/impl/DbgModelTargetStackImpl.java | 102 + .../DbgModelTargetSymbolContainerImpl.java | 59 + .../model/impl/DbgModelTargetSymbolImpl.java | 69 + .../DbgModelTargetThreadContainerImpl.java | 127 + .../model/impl/DbgModelTargetThreadImpl.java | 184 ++ ...bgModelTargetTraceOrDumpConnectorImpl.java | 79 + .../src/sctldbg/cpp/sctldbg.cpp | 315 ++ .../src/test/java/agent/dbgeng/DummyProc.java | 51 + .../java/agent/dbgeng/dbgeng/DbgEngTest.java | 899 ++++++ .../agent/dbgeng/dbgeng/DbgEngTestOld.java | 675 ++++ .../manager/impl/AbstractDbgManagerTest.java | 449 +++ .../manager/impl/SpawnedDbgManagerTest.java | 27 + .../dbgeng/model/AbstractModelForDbgTest.java | 1070 +++++++ .../agent/dbgeng/model/GadpForDbgTest.java | 125 + .../agent/dbgeng/model/ModelForDbgTest.java | 54 + .../java/agent/dbgeng/testutil/DummyProc.java | 80 + .../Module.manifest | 2 + .../build.gradle | 13 + .../certification.manifest | 6 + .../ghidra_scripts/PopulateTraceLocal.java | 409 +++ .../ghidra_scripts/PopulateTraceRemote.java | 174 ++ .../Debugger-agent-dbgmodel/Module.manifest | 2 + .../Debugger-agent-dbgmodel/build.gradle | 61 + .../certification.manifest | 8 + .../src/javaprovider/cpp/javaprovider.cpp | 224 ++ .../src/javaprovider/def/javaprovider.def | 13 + .../src/javaprovider/headers/afxres.h | 16 + .../src/javaprovider/headers/resource.h | 31 + .../src/javaprovider/rc/javaprovider.rc | 0 .../DbgModelInJvmDebuggerModelFactory.java | 48 + .../dbgmodel/dbgmodel/COMUtilsExtra.java | 72 + .../agent/dbgmodel/dbgmodel/DbgModel.java | 196 ++ .../agent/dbgmodel/dbgmodel/UnknownEx.java | 24 + .../dbgmodel/bridge/HostDataModelAccess.java | 38 + .../dbgmodel/concept/ComparableConcept.java | 26 + .../dbgmodel/dbgmodel/concept/Concept.java | 27 + .../dbgmodel/concept/DataModelConcept.java | 31 + .../DynamicConceptProviderConcept.java | 40 + .../concept/DynamicKeyProviderConcept.java | 32 + .../dbgmodel/concept/EquatableConcept.java | 27 + .../dbgmodel/concept/IndexableConcept.java | 31 + .../dbgmodel/concept/IterableConcept.java | 30 + .../concept/PreferredRuntimeTypeConcept.java | 26 + .../concept/StringDisplayableConcept.java | 28 + .../dbgmodel/datamodel/DataModelManager1.java | 84 + .../dbgmodel/datamodel/DataModelManager2.java | 36 + .../datamodel/script/DataModelNameBinder.java | 36 + .../datamodel/script/DataModelScript.java | 42 + .../script/DataModelScriptClient.java | 29 + .../script/DataModelScriptHostContext.java | 29 + .../script/DataModelScriptManager.java | 37 + .../script/DataModelScriptProvider.java | 34 + .../DataModelScriptProviderEnumerator.java | 28 + .../script/DataModelScriptTemplate.java | 30 + .../DataModelScriptTemplateEnumerator.java | 28 + .../script/debug/DataModelScriptDebug1.java | 24 + .../script/debug/DataModelScriptDebug2.java | 22 + .../debug/DataModelScriptDebugBreakpoint.java | 24 + ...aModelScriptDebugBreakpointEnumerator.java | 24 + .../debug/DataModelScriptDebugClient.java | 24 + .../debug/DataModelScriptDebugStack.java | 24 + .../debug/DataModelScriptDebugStackFrame.java | 24 + ...ModelScriptDebugVariableSetEnumerator.java | 24 + .../dbgmodel/debughost/DebugHost.java | 40 + .../dbgmodel/debughost/DebugHostBase.java | 45 + .../debughost/DebugHostBaseClass.java | 24 + .../dbgmodel/debughost/DebugHostConstant.java | 26 + .../dbgmodel/debughost/DebugHostContext.java | 26 + .../dbgmodel/debughost/DebugHostData.java | 32 + .../debughost/DebugHostErrorSink.java | 29 + .../debughost/DebugHostEvaluator1.java | 34 + .../debughost/DebugHostEvaluator2.java | 26 + .../debughost/DebugHostExtensability.java | 31 + .../dbgmodel/debughost/DebugHostField.java | 34 + .../dbgmodel/debughost/DebugHostMemory1.java | 42 + .../dbgmodel/debughost/DebugHostMemory2.java | 27 + .../dbgmodel/debughost/DebugHostModule1.java | 39 + .../dbgmodel/debughost/DebugHostModule2.java | 25 + .../debughost/DebugHostModuleSignature.java | 27 + .../dbgmodel/debughost/DebugHostPublic.java | 30 + .../debughost/DebugHostScriptHost.java | 28 + .../dbgmodel/debughost/DebugHostStatus.java | 27 + .../dbgmodel/debughost/DebugHostSymbol1.java | 37 + .../dbgmodel/debughost/DebugHostSymbol2.java | 29 + .../debughost/DebugHostSymbolEnumerator.java | 28 + .../dbgmodel/debughost/DebugHostSymbols.java | 45 + .../dbgmodel/debughost/DebugHostType1.java | 61 + .../dbgmodel/debughost/DebugHostType2.java | 33 + .../debughost/DebugHostTypeSignature.java | 32 + .../dbgmodel/err/DbgModelException.java | 29 + .../err/DbgModelRuntimeException.java | 29 + .../dbgmodel/dbgmodel/main/KeyEnumerator.java | 30 + .../dbgmodel/dbgmodel/main/KeyStore.java | 36 + .../dbgmodel/dbgmodel/main/ModelIterator.java | 30 + .../dbgmodel/main/ModelKeyReference1.java | 38 + .../dbgmodel/main/ModelKeyReference2.java | 24 + .../dbgmodel/dbgmodel/main/ModelMethod.java | 29 + .../dbgmodel/dbgmodel/main/ModelObject.java | 139 + .../dbgmodel/main/ModelPropertyAccessor.java | 28 + .../dbgmodel/dbgmodel/main/RawEnumerator.java | 34 + .../dbgmodel/gadp/DbgModelGadpServer.java | 121 + .../DbgModelLocalDebuggerModelFactory.java | 38 + .../impl/DbgModelClientThreadExecutor.java | 78 + .../gadp/impl/DbgModelGadpServerImpl.java | 63 + .../dbgmodel/gadp/impl/WrappedDbgModel.java | 969 ++++++ .../dbgmodel/impl/dbgmodel/DbgModelUtil.java | 88 + .../dbgmodel/DebugRunningProcessImpl.java | 66 + .../dbgmodel/impl/dbgmodel/UnknownExImpl.java | 45 + .../impl/dbgmodel/UnknownExInternal.java | 50 + .../impl/dbgmodel/bridge/HDMAUtil.java | 268 ++ .../bridge/HostDataModelAccessImpl.java | 105 + .../bridge/HostDataModelAccessInternal.java | 50 + .../concept/ComparableConceptImpl.java | 64 + .../concept/ComparableConceptInternal.java | 50 + .../concept/DataModelConceptImpl.java | 79 + .../concept/DataModelConceptInternal.java | 50 + .../DynamicConceptProviderConceptImpl.java | 128 + ...DynamicConceptProviderConceptInternal.java | 54 + .../DynamicKeyProviderConceptImpl.java | 101 + .../DynamicKeyProviderConceptInternal.java | 50 + .../concept/EquatableConceptImpl.java | 64 + .../concept/EquatableConceptInternal.java | 50 + .../concept/IndexableConceptImpl.java | 85 + .../concept/IndexableConceptInternal.java | 50 + .../dbgmodel/concept/IterableConceptImpl.java | 94 + .../concept/IterableConceptInternal.java | 49 + .../PreferredRuntimeTypeConceptImpl.java | 70 + .../PreferredRuntimeTypeConceptInternal.java | 50 + .../concept/StringDisplayableConceptImpl.java | 68 + .../StringDisplayableConceptInternal.java | 50 + .../datamodel/DataModelManagerImpl1.java | 338 ++ .../datamodel/DataModelManagerImpl2.java | 82 + .../datamodel/DataModelManagerInternal.java | 55 + .../script/DataModelNameBinderImpl.java | 136 + .../script/DataModelNameBinderInternal.java | 50 + .../script/DataModelScriptClientImpl.java | 53 + .../script/DataModelScriptClientInternal.java | 50 + .../DataModelScriptHostContextImpl.java | 67 + .../DataModelScriptHostContextInternal.java | 50 + .../datamodel/script/DataModelScriptImpl.java | 90 + .../script/DataModelScriptInternal.java | 50 + .../script/DataModelScriptManagerImpl.java | 114 + .../DataModelScriptManagerInternal.java | 50 + ...DataModelScriptProviderEnumeratorImpl.java | 68 + ...ModelScriptProviderEnumeratorInternal.java | 55 + .../script/DataModelScriptProviderImpl.java | 111 + .../DataModelScriptProviderInternal.java | 50 + ...DataModelScriptTemplateEnumeratorImpl.java | 69 + ...ModelScriptTemplateEnumeratorInternal.java | 55 + .../script/DataModelScriptTemplateImpl.java | 80 + .../DataModelScriptTemplateInternal.java | 50 + ...elScriptDebugBreakpointEnumeratorImpl.java | 103 + ...riptDebugBreakpointEnumeratorInternal.java | 57 + .../DataModelScriptDebugBreakpointImpl.java | 101 + ...ataModelScriptDebugBreakpointInternal.java | 54 + .../debug/DataModelScriptDebugClientImpl.java | 101 + .../DataModelScriptDebugClientInternal.java | 52 + .../debug/DataModelScriptDebugImpl.java | 101 + .../debug/DataModelScriptDebugImpl2.java | 98 + .../debug/DataModelScriptDebugInternal.java | 51 + .../DataModelScriptDebugStackFrameImpl.java | 101 + ...ataModelScriptDebugStackFrameInternal.java | 54 + .../debug/DataModelScriptDebugStackImpl.java | 101 + .../DataModelScriptDebugStackInternal.java | 52 + ...lScriptDebugVariableSetEnumeratorImpl.java | 103 + ...iptDebugVariableSetEnumeratorInternal.java | 58 + .../debughost/DebugHostBaseClassImpl.java | 138 + .../debughost/DebugHostBaseClassInternal.java | 51 + .../debughost/DebugHostConstantImpl.java | 47 + .../debughost/DebugHostConstantInternal.java | 50 + .../debughost/DebugHostContextImpl.java | 54 + .../debughost/DebugHostContextInternal.java | 50 + .../dbgmodel/debughost/DebugHostDataImpl.java | 62 + .../debughost/DebugHostDataInternal.java | 49 + .../debughost/DebugHostErrorSinkImpl.java | 49 + .../debughost/DebugHostErrorSinkInternal.java | 50 + .../debughost/DebugHostEvaluatorImpl1.java | 69 + .../debughost/DebugHostEvaluatorImpl2.java | 53 + .../debughost/DebugHostEvaluatorInternal.java | 55 + .../debughost/DebugHostExtensabilityImpl.java | 53 + .../DebugHostExtensabilityInternal.java | 50 + .../debughost/DebugHostFieldImpl.java | 70 + .../debughost/DebugHostFieldInternal.java | 49 + .../dbgmodel/debughost/DebugHostImpl.java | 111 + .../dbgmodel/debughost/DebugHostInternal.java | 49 + .../debughost/DebugHostMemoryImpl1.java | 114 + .../debughost/DebugHostMemoryImpl2.java | 46 + .../debughost/DebugHostMemoryInternal.java | 55 + .../debughost/DebugHostModuleImpl1.java | 149 + .../debughost/DebugHostModuleImpl2.java | 67 + .../debughost/DebugHostModuleInternal.java | 55 + .../DebugHostModuleSignatureImpl.java | 50 + .../DebugHostModuleSignatureInternal.java | 50 + .../debughost/DebugHostPublicImpl.java | 55 + .../debughost/DebugHostPublicInternal.java | 50 + .../debughost/DebugHostScriptHostImpl.java | 59 + .../DebugHostScriptHostInternal.java | 50 + .../debughost/DebugHostStatusImpl.java | 48 + .../debughost/DebugHostStatusInternal.java | 50 + .../DebugHostSymbolEnumeratorImpl.java | 72 + .../DebugHostSymbolEnumeratorInternal.java | 50 + .../debughost/DebugHostSymbolImpl1.java | 81 + .../debughost/DebugHostSymbolImpl2.java | 65 + .../debughost/DebugHostSymbolInternal.java | 55 + .../debughost/DebugHostSymbolsImpl.java | 171 ++ .../debughost/DebugHostSymbolsInternal.java | 50 + .../debughost/DebugHostTypeImpl1.java | 265 ++ .../debughost/DebugHostTypeImpl2.java | 100 + .../debughost/DebugHostTypeInternal.java | 53 + .../debughost/DebugHostTypeSignatureImpl.java | 85 + .../DebugHostTypeSignatureInternal.java | 50 + .../debughost/X_DebugHostBaseClassImpl.java | 51 + .../X_DebugHostBaseClassInternal.java | 50 + .../impl/dbgmodel/main/KeyEnumeratorImpl.java | 84 + .../dbgmodel/main/KeyEnumeratorInternal.java | 49 + .../impl/dbgmodel/main/KeyStoreImpl.java | 80 + .../impl/dbgmodel/main/KeyStoreInternal.java | 49 + .../impl/dbgmodel/main/ModelIteratorImpl.java | 83 + .../dbgmodel/main/ModelIteratorInternal.java | 49 + .../dbgmodel/main/ModelKeyReferenceImpl1.java | 117 + .../dbgmodel/main/ModelKeyReferenceImpl2.java | 45 + .../main/ModelKeyReferenceInternal.java | 55 + .../impl/dbgmodel/main/ModelMethodImpl.java | 56 + .../dbgmodel/main/ModelMethodInternal.java | 49 + .../impl/dbgmodel/main/ModelObjectImpl.java | 912 ++++++ .../dbgmodel/main/ModelObjectInternal.java | 49 + .../main/ModelPropertyAccessorImpl.java | 73 + .../main/ModelPropertyAccessorInternal.java | 50 + .../impl/dbgmodel/main/RawEnumeratorImpl.java | 96 + .../dbgmodel/main/RawEnumeratorInternal.java | 49 + .../jna/cmd/DbgApplyMethodsCommand.java | 57 + .../jna/cmd/DbgGetRegisterMapCommand.java | 63 + .../jna/cmd/DbgListAttributesCommand.java | 76 + .../jna/cmd/DbgListElementsCommand.java | 73 + .../dbgmodel/jna/dbgmodel/DbgModelNative.java | 183 ++ .../dbgmodel/jna/dbgmodel/IDebugClientEx.java | 26 + .../dbgmodel/jna/dbgmodel/IUnknownEx.java | 25 + .../jna/dbgmodel/UnknownWithUtils.java | 303 ++ .../dbgmodel/jna/dbgmodel/WrapIUnknownEx.java | 33 + .../dbgmodel/bridge/IHostDataModelAccess.java | 42 + .../bridge/WrapIHostDataModelAccess.java | 41 + .../dbgmodel/concept/IComparableConcept.java | 44 + .../dbgmodel/concept/IDataModelConcept.java | 47 + .../IDynamicConceptProviderConcept.java | 60 + .../concept/IDynamicKeyProviderConcept.java | 52 + .../dbgmodel/concept/IEquatableConcept.java | 43 + .../dbgmodel/concept/IIndexableConcept.java | 53 + .../dbgmodel/concept/IIterableConcept.java | 48 + .../concept/IPreferredRuntimeTypeConcept.java | 43 + .../concept/IStringDisplayableConcept.java | 43 + .../concept/WrapIComparableConcept.java | 43 + .../concept/WrapIDataModelConcept.java | 48 + .../WrapIDynamicConceptProviderConcept.java | 70 + .../WrapIDynamicKeyProviderConcept.java | 55 + .../concept/WrapIEquatableConcept.java | 43 + .../concept/WrapIIndexableConcept.java | 57 + .../concept/WrapIIterableConcept.java | 48 + .../WrapIPreferredRuntimeTypeConcept.java | 44 + .../WrapIStringDisplayableConcept.java | 45 + .../datamodel/IDataModelManager1.java | 109 + .../datamodel/IDataModelManager2.java | 49 + .../datamodel/WrapIDataModelManager1.java | 159 + .../datamodel/WrapIDataModelManager2.java | 49 + .../script/IDataModelNameBinder.java | 55 + .../datamodel/script/IDataModelScript.java | 63 + .../script/IDataModelScriptClient.java | 44 + .../script/IDataModelScriptHostContext.java | 47 + .../script/IDataModelScriptManager.java | 59 + .../script/IDataModelScriptProvider.java | 55 + .../IDataModelScriptProviderEnumerator.java | 46 + .../script/IDataModelScriptTemplate.java | 49 + .../IDataModelScriptTemplateEnumerator.java | 46 + .../script/WrapIDataModelNameBinder.java | 59 + .../script/WrapIDataModelScript.java | 71 + .../script/WrapIDataModelScriptClient.java | 43 + .../WrapIDataModelScriptHostContext.java | 49 + .../script/WrapIDataModelScriptManager.java | 70 + .../script/WrapIDataModelScriptProvider.java | 64 + ...rapIDataModelScriptProviderEnumerator.java | 48 + .../script/WrapIDataModelScriptTemplate.java | 54 + ...rapIDataModelScriptTemplateEnumerator.java | 48 + .../script/debug/IDataModelScriptDebug.java | 75 + .../script/debug/IDataModelScriptDebug2.java | 42 + .../IDataModelScriptDebugBreakpoint.java | 59 + ...aModelScriptDebugBreakpointEnumerator.java | 46 + .../debug/IDataModelScriptDebugClient.java | 43 + .../debug/IDataModelScriptDebugStack.java | 46 + .../IDataModelScriptDebugStackFrame.java | 66 + ...ModelScriptDebugVariableSetEnumerator.java | 48 + .../debug/WrapIDataModelScriptDebug.java | 98 + .../debug/WrapIDataModelScriptDebug2.java | 41 + .../WrapIDataModelScriptDebugBreakpoint.java | 66 + ...aModelScriptDebugBreakpointEnumerator.java | 48 + .../WrapIDataModelScriptDebugClient.java | 44 + .../debug/WrapIDataModelScriptDebugStack.java | 49 + .../WrapIDataModelScriptDebugStackFrame.java | 77 + ...ModelScriptDebugVariableSetEnumerator.java | 51 + .../jna/dbgmodel/debughost/IDebugHost.java | 47 + .../debughost/IDebugHostBaseClass.java | 63 + .../debughost/IDebugHostConstant.java | 41 + .../dbgmodel/debughost/IDebugHostContext.java | 43 + .../dbgmodel/debughost/IDebugHostData.java | 49 + .../debughost/IDebugHostErrorSink.java | 43 + .../debughost/IDebugHostEvaluator1.java | 49 + .../debughost/IDebugHostEvaluator2.java | 43 + .../debughost/IDebugHostExtensability.java | 46 + .../dbgmodel/debughost/IDebugHostField.java | 53 + .../dbgmodel/debughost/IDebugHostMemory1.java | 64 + .../dbgmodel/debughost/IDebugHostMemory2.java | 43 + .../dbgmodel/debughost/IDebugHostModule1.java | 60 + .../dbgmodel/debughost/IDebugHostModule2.java | 44 + .../debughost/IDebugHostModuleSignature.java | 43 + .../dbgmodel/debughost/IDebugHostPublic.java | 45 + .../debughost/IDebugHostScriptHost.java | 43 + .../dbgmodel/debughost/IDebugHostStatus.java | 42 + .../dbgmodel/debughost/IDebugHostSymbol1.java | 44 + .../dbgmodel/debughost/IDebugHostSymbol2.java | 48 + .../debughost/IDebugHostSymbolEnumerator.java | 45 + .../dbgmodel/debughost/IDebugHostSymbols.java | 68 + .../dbgmodel/debughost/IDebugHostType1.java | 99 + .../dbgmodel/debughost/IDebugHostType2.java | 55 + .../debughost/IDebugHostTypeSignature.java | 50 + .../dbgmodel/debughost/WrapIDebugHost.java | 51 + .../debughost/WrapIDebugHostBaseClass.java | 73 + .../debughost/WrapIDebugHostConstant.java | 40 + .../debughost/WrapIDebugHostContext.java | 41 + .../debughost/WrapIDebugHostData.java | 52 + .../debughost/WrapIDebugHostErrorSink.java | 40 + .../debughost/WrapIDebugHostEvaluator1.java | 50 + .../debughost/WrapIDebugHostEvaluator2.java | 43 + .../WrapIDebugHostExtensability.java | 46 + .../debughost/WrapIDebugHostField.java | 58 + .../debughost/WrapIDebugHostMemory1.java | 79 + .../debughost/WrapIDebugHostMemory2.java | 42 + .../debughost/WrapIDebugHostModule1.java | 68 + .../debughost/WrapIDebugHostModule2.java | 43 + .../WrapIDebugHostModuleSignature.java | 43 + .../debughost/WrapIDebugHostPublic.java | 46 + .../debughost/WrapIDebugHostScriptHost.java | 42 + .../debughost/WrapIDebugHostStatus.java | 41 + .../debughost/WrapIDebugHostSymbol1.java | 42 + .../debughost/WrapIDebugHostSymbol2.java | 49 + .../WrapIDebugHostSymbolEnumerator.java | 48 + .../debughost/WrapIDebugHostSymbols.java | 83 + .../debughost/WrapIDebugHostType1.java | 136 + .../debughost/WrapIDebugHostType2.java | 62 + .../WrapIDebugHostTypeSignature.java | 56 + .../debughost/X_IDebugHostBaseClass.java | 40 + .../debughost/X_WrapIDebugHostBaseClass.java | 41 + .../jna/dbgmodel/main/IKeyEnumerator.java | 46 + .../dbgmodel/jna/dbgmodel/main/IKeyStore.java | 56 + .../jna/dbgmodel/main/IModelIterator.java | 47 + .../jna/dbgmodel/main/IModelKeyReference.java | 62 + .../dbgmodel/main/IModelKeyReference2.java | 41 + .../jna/dbgmodel/main/IModelMethod.java | 45 + .../jna/dbgmodel/main/IModelObject.java | 150 + .../dbgmodel/main/IModelPropertyAccessor.java | 47 + .../jna/dbgmodel/main/IRawEnumerator.java | 47 + .../jna/dbgmodel/main/WrapIKeyEnumerator.java | 48 + .../jna/dbgmodel/main/WrapIKeyStore.java | 61 + .../jna/dbgmodel/main/WrapIModelIterator.java | 49 + .../main/WrapIModelKeyReference1.java | 73 + .../main/WrapIModelKeyReference2.java | 40 + .../jna/dbgmodel/main/WrapIModelMethod.java | 44 + .../jna/dbgmodel/main/WrapIModelObject.java | 220 ++ .../main/WrapIModelPropertyAccessor.java | 46 + .../jna/dbgmodel/main/WrapIRawEnumerator.java | 48 + .../jna/javaprovider/JavaProviderNative.java | 25 + .../dbgmodel/manager/DbgManager2Impl.java | 161 + .../impl/DbgModel2DefaultTargetModelRoot.java | 26 + .../dbgmodel/model/impl/DbgModel2Impl.java | 105 + ...DbgModel2TargetAvailableContainerImpl.java | 71 + .../impl/DbgModel2TargetAvailableImpl.java | 69 + .../model/impl/DbgModel2TargetObjectImpl.java | 375 +++ .../model/impl/DbgModel2TargetRootImpl.java | 483 +++ .../impl/DbgModel2TargetSystemMarkerImpl.java | 40 + .../impl/DelegateDbgModel2TargetObject.java | 376 +++ .../src/sctldbg/cpp/sctldbg.cpp | 316 ++ .../test/java/agent/dbgmodel/DummyProc.java | 51 + .../agent/dbgmodel/dbgmodel/DbgModelTest.java | 1309 ++++++++ .../Debug/Debugger-agent-gdb/Module.manifest | 1 + Ghidra/Debug/Debugger-agent-gdb/build.gradle | 87 + .../Debugger-agent-gdb/certification.manifest | 6 + .../gdb/GdbInJvmDebuggerModelFactory.java | 76 + .../agent/gdb/ffi/linux/FdInputStream.java | 80 + .../agent/gdb/ffi/linux/FdOutputStream.java | 78 + .../main/java/agent/gdb/ffi/linux/Pty.java | 153 + .../java/agent/gdb/ffi/linux/PtyEndpoint.java | 56 + .../java/agent/gdb/ffi/linux/PtyMaster.java | 25 + .../agent/gdb/ffi/linux/PtySessionLeader.java | 93 + .../java/agent/gdb/ffi/linux/PtySlave.java | 145 + .../main/java/agent/gdb/ffi/linux/Util.java | 41 + .../java/agent/gdb/gadp/GdbGadpServer.java | 205 ++ .../gadp/GdbLocalDebuggerModelFactory.java | 107 + .../gdb/gadp/impl/GdbGadpServerImpl.java | 62 + .../main/java/agent/gdb/manager/GdbCause.java | 35 + .../gdb/manager/GdbConsoleOutputListener.java | 28 + .../agent/gdb/manager/GdbEventsListener.java | 179 ++ .../gdb/manager/GdbEventsListenerAdapter.java | 95 + .../java/agent/gdb/manager/GdbInferior.java | 306 ++ .../gdb/manager/GdbInferiorThreadGroup.java | 54 + .../java/agent/gdb/manager/GdbLibraryId.java | 27 + .../java/agent/gdb/manager/GdbManager.java | 473 +++ .../gdb/manager/GdbMemoryOperations.java | 70 + .../java/agent/gdb/manager/GdbModule.java | 40 + .../agent/gdb/manager/GdbModuleSection.java | 30 + .../gdb/manager/GdbProcessThreadGroup.java | 50 + .../java/agent/gdb/manager/GdbRegister.java | 77 + .../agent/gdb/manager/GdbRegisterSet.java | 73 + .../java/agent/gdb/manager/GdbStackFrame.java | 60 + .../gdb/manager/GdbStackFrameOperations.java | 69 + .../main/java/agent/gdb/manager/GdbState.java | 79 + .../agent/gdb/manager/GdbStateListener.java | 36 + .../main/java/agent/gdb/manager/GdbTable.java | 320 ++ .../gdb/manager/GdbTargetOutputListener.java | 31 + .../java/agent/gdb/manager/GdbThread.java | 141 + .../manager/breakpoint/GdbBreakpointDisp.java | 68 + .../manager/breakpoint/GdbBreakpointInfo.java | 363 +++ .../breakpoint/GdbBreakpointInsertions.java | 75 + .../breakpoint/GdbBreakpointLocation.java | 145 + .../manager/breakpoint/GdbBreakpointType.java | 88 + .../evt/AbstractGdbCompletedCommandEvent.java | 65 + .../gdb/manager/evt/AbstractGdbEvent.java | 100 + .../evt/AbstractGdbEventWithFields.java | 50 + .../evt/AbstractGdbEventWithStateChange.java | 53 + .../evt/AbstractGdbEventWithString.java | 49 + .../manager/evt/AbstractGdbLibraryEvent.java | 102 + .../manager/evt/AbstractGdbOutputEvent.java | 71 + .../manager/evt/AbstractGdbThreadEvent.java | 60 + .../evt/AbstractGdbThreadGroupEvent.java | 48 + .../evt/GdbBreakpointCreatedEvent.java | 50 + .../evt/GdbBreakpointDeletedEvent.java | 48 + .../evt/GdbBreakpointModifiedEvent.java | 48 + .../manager/evt/GdbCommandConnectedEvent.java | 34 + .../gdb/manager/evt/GdbCommandDoneEvent.java | 199 ++ .../gdb/manager/evt/GdbCommandEchoEvent.java | 42 + .../gdb/manager/evt/GdbCommandErrorEvent.java | 55 + .../gdb/manager/evt/GdbCommandExitEvent.java | 40 + .../manager/evt/GdbCommandRunningEvent.java | 48 + .../manager/evt/GdbConsoleOutputEvent.java | 55 + .../gdb/manager/evt/GdbDebugOutputEvent.java | 34 + .../manager/evt/GdbLibraryLoadedEvent.java | 37 + .../manager/evt/GdbLibraryUnloadedEvent.java | 37 + .../manager/evt/GdbMemoryChangedEvent.java | 70 + .../gdb/manager/evt/GdbParamChangedEvent.java | 25 + .../gdb/manager/evt/GdbRunningEvent.java | 49 + .../gdb/manager/evt/GdbStoppedEvent.java | 77 + .../gdb/manager/evt/GdbTargetOutputEvent.java | 34 + .../manager/evt/GdbThreadCreatedEvent.java | 36 + .../gdb/manager/evt/GdbThreadExitedEvent.java | 36 + .../manager/evt/GdbThreadGroupAddedEvent.java | 36 + .../evt/GdbThreadGroupExitedEvent.java | 63 + .../evt/GdbThreadGroupRemovedEvent.java | 36 + .../evt/GdbThreadGroupStartedEvent.java | 48 + .../manager/evt/GdbThreadSelectedEvent.java | 61 + .../agent/gdb/manager/impl/GdbCommand.java | 90 + .../java/agent/gdb/manager/impl/GdbEvent.java | 69 + .../agent/gdb/manager/impl/GdbFrameInfo.java | 116 + .../gdb/manager/impl/GdbInferiorImpl.java | 495 +++ .../gdb/manager/impl/GdbManagerImpl.java | 1501 +++++++++ .../gdb/manager/impl/GdbMemoryMapping.java | 58 + .../gdb/manager/impl/GdbMinimalSymbol.java | 48 + .../agent/gdb/manager/impl/GdbModuleImpl.java | 177 ++ .../manager/impl/GdbModuleSectionImpl.java | 62 + .../gdb/manager/impl/GdbPendingCommand.java | 232 ++ .../gdb/manager/impl/GdbStackFrameImpl.java | 112 + .../agent/gdb/manager/impl/GdbThreadImpl.java | 274 ++ .../agent/gdb/manager/impl/GdbThreadInfo.java | 165 + .../manager/impl/cmd/AbstractGdbCommand.java | 61 + ...bstractGdbCommandWithThreadAndFrameId.java | 76 + .../cmd/AbstractGdbCommandWithThreadId.java | 71 + .../impl/cmd/GdbAddInferiorCommand.java | 52 + .../manager/impl/cmd/GdbAttachCommand.java | 66 + .../gdb/manager/impl/cmd/GdbClaimStopped.java | 57 + .../gdb/manager/impl/cmd/GdbCommandError.java | 55 + .../impl/cmd/GdbConsoleExecCommand.java | 77 + .../manager/impl/cmd/GdbContinueCommand.java | 95 + .../impl/cmd/GdbDeleteBreakpointsCommand.java | 60 + .../manager/impl/cmd/GdbDetachCommand.java | 70 + .../cmd/GdbDisableBreakpointsCommand.java | 60 + .../impl/cmd/GdbEnableBreakpointsCommand.java | 60 + .../manager/impl/cmd/GdbEvaluateCommand.java | 57 + .../cmd/GdbFileExecAndSymbolsCommand.java | 54 + .../impl/cmd/GdbGetThreadInfoCommand.java | 54 + .../manager/impl/cmd/GdbGetVarCommand.java | 53 + .../impl/cmd/GdbInferiorSelectCommand.java | 59 + .../manager/impl/cmd/GdbInfoOsCommand.java | 54 + .../impl/cmd/GdbInsertBreakpointCommand.java | 98 + .../manager/impl/cmd/GdbInterruptCommand.java | 81 + .../gdb/manager/impl/cmd/GdbKillCommand.java | 56 + .../cmd/GdbListAvailableProcessesCommand.java | 51 + .../impl/cmd/GdbListBreakpointsCommand.java | 68 + .../impl/cmd/GdbListInferiorsCommand.java | 77 + .../impl/cmd/GdbListRegisterNamesCommand.java | 53 + .../impl/cmd/GdbListThreadsCommand.java | 74 + .../impl/cmd/GdbReadMemoryCommand.java | 91 + .../impl/cmd/GdbReadRegistersCommand.java | 144 + .../impl/cmd/GdbRemoveInferiorCommand.java | 52 + .../gdb/manager/impl/cmd/GdbRunCommand.java | 70 + .../impl/cmd/GdbSetInferiorTtyCommand.java | 53 + .../manager/impl/cmd/GdbSetVarCommand.java | 55 + .../impl/cmd/GdbStackListFramesCommand.java | 61 + .../gdb/manager/impl/cmd/GdbStepCommand.java | 61 + .../impl/cmd/GdbThreadSelectCommand.java | 75 + .../impl/cmd/GdbWriteMemoryCommand.java | 75 + .../impl/cmd/GdbWriteRegistersCommand.java | 137 + .../gdb/manager/parsing/GdbCValueParser.java | 320 ++ .../gdb/manager/parsing/GdbMiParser.java | 469 +++ .../gdb/manager/parsing/GdbParsingUtils.java | 171 ++ .../reason/GdbBreakpointHitReason.java | 77 + .../reason/GdbEndSteppingRangeReason.java | 32 + .../manager/reason/GdbExitNormallyReason.java | 32 + .../gdb/manager/reason/GdbExitedReason.java | 43 + .../agent/gdb/manager/reason/GdbReason.java | 94 + .../reason/GdbSignalReceivedReason.java | 43 + .../agent/gdb/model/impl/GdbModelImpl.java | 149 + .../gdb/model/impl/GdbModelImplUtils.java | 50 + .../model/impl/GdbModelSelectableObject.java | 24 + .../model/impl/GdbModelTargetAttachable.java | 69 + .../GdbModelTargetAvailableContainer.java | 61 + .../GdbModelTargetBreakpointContainer.java | 164 + .../GdbModelTargetBreakpointLocation.java | 143 + .../impl/GdbModelTargetBreakpointSpec.java | 246 ++ .../model/impl/GdbModelTargetEnvironment.java | 181 ++ .../model/impl/GdbModelTargetInferior.java | 260 ++ .../impl/GdbModelTargetInferiorContainer.java | 257 ++ .../impl/GdbModelTargetMemoryRegion.java | 115 + .../gdb/model/impl/GdbModelTargetModule.java | 95 + .../impl/GdbModelTargetModuleContainer.java | 124 + .../impl/GdbModelTargetProcessMemory.java | 141 + .../model/impl/GdbModelTargetRegister.java | 72 + .../impl/GdbModelTargetRegisterContainer.java | 91 + .../gdb/model/impl/GdbModelTargetSection.java | 84 + .../impl/GdbModelTargetSectionContainer.java | 73 + .../gdb/model/impl/GdbModelTargetSession.java | 260 ++ .../gdb/model/impl/GdbModelTargetStack.java | 91 + .../model/impl/GdbModelTargetStackFrame.java | 175 ++ .../GdbModelTargetStackFrameRegister.java | 85 + ...odelTargetStackFrameRegisterContainer.java | 108 + .../gdb/model/impl/GdbModelTargetSymbol.java | 70 + .../impl/GdbModelTargetSymbolContainer.java | 63 + .../gdb/model/impl/GdbModelTargetThread.java | 229 ++ .../impl/GdbModelTargetThreadContainer.java | 102 + .../src/main/resources/session.py | 62 + .../Debugger-agent-gdb/src/main/sh/execjar.sh | 7 + .../java/agent/gdb/ffi/linux/PtyTest.java | 209 ++ .../java/agent/gdb/manager/GdbTableTest.java | 177 ++ .../manager/impl/AbstractGdbManagerTest.java | 396 +++ .../gdb/manager/impl/GdbCValueParserTest.java | 238 ++ .../manager/impl/JoinedGdbManagerTest.java | 77 + .../manager/impl/SpawnedGdbManagerTest.java | 34 + .../gdb/manager/parsing/GdbMiParserTest.java | 64 + .../gdb/model/AbstractModelForGdbTest.java | 1022 ++++++ .../gdb/model/EventSequenceListener.java | 88 + .../java/agent/gdb/model/GadpForGdbTest.java | 122 + .../java/agent/gdb/model/ModelForGdbTest.java | 56 + Ghidra/Debug/Debugger-gadp/Module.manifest | 1 + Ghidra/Debug/Debugger-gadp/build.gradle | 81 + .../Debugger-gadp/certification.manifest | 6 + .../java/ghidra/dbg/gadp/GadpRegistry.java | 132 + .../java/ghidra/dbg/gadp/GadpVersion.java | 48 + .../DelegateGadpClientTargetObject.java | 545 ++++ .../ghidra/dbg/gadp/client/GadpClient.java | 822 +++++ .../GadpClientTargetAccessConditioned.java | 29 + .../client/GadpClientTargetAggregate.java | 22 + .../client/GadpClientTargetAttachable.java | 23 + .../gadp/client/GadpClientTargetAttacher.java | 48 + .../GadpClientTargetBreakpointContainer.java | 90 + .../GadpClientTargetBreakpointLocation.java | 24 + .../GadpClientTargetBreakpointSpec.java | 71 + .../gadp/client/GadpClientTargetConsole.java | 38 + .../GadpClientTargetDataTypeMember.java | 23 + .../GadpClientTargetDataTypeNamespace.java | 23 + .../client/GadpClientTargetDeletable.java | 33 + .../client/GadpClientTargetDetachable.java | 33 + .../client/GadpClientTargetEnvironment.java | 23 + .../client/GadpClientTargetEventScope.java | 43 + .../GadpClientTargetExecutionStateful.java | 35 + .../client/GadpClientTargetFocusScope.java | 56 + .../client/GadpClientTargetInterpreter.java | 59 + .../client/GadpClientTargetInterruptible.java | 33 + .../gadp/client/GadpClientTargetKillable.java | 33 + .../gadp/client/GadpClientTargetLauncher.java | 42 + .../gadp/client/GadpClientTargetMemory.java | 106 + .../client/GadpClientTargetMemoryRegion.java | 23 + .../gadp/client/GadpClientTargetMethod.java | 37 + .../gadp/client/GadpClientTargetModule.java | 23 + .../GadpClientTargetModuleContainer.java | 23 + .../client/GadpClientTargetNamedDataType.java | 23 + .../gadp/client/GadpClientTargetObject.java | 89 + .../client/GadpClientTargetObjectStub.java | 57 + .../gadp/client/GadpClientTargetProcess.java | 23 + .../gadp/client/GadpClientTargetRegister.java | 23 + .../client/GadpClientTargetRegisterBank.java | 89 + .../GadpClientTargetRegisterContainer.java | 23 + .../client/GadpClientTargetResumable.java | 33 + .../gadp/client/GadpClientTargetSection.java | 23 + .../gadp/client/GadpClientTargetStack.java | 23 + .../client/GadpClientTargetStackFrame.java | 23 + .../client/GadpClientTargetSteppable.java | 34 + .../gadp/client/GadpClientTargetSymbol.java | 23 + .../GadpClientTargetSymbolNamespace.java | 23 + .../gadp/client/GadpClientTargetThread.java | 23 + .../client/GadpTcpDebuggerModelFactory.java | 80 + .../annot/GadpAttributeChangeCallback.java | 24 + .../gadp/client/annot/GadpEventHandler.java | 26 + .../dbg/gadp/error/GadpErrorException.java | 31 + .../ghidra/dbg/gadp/error/GadpException.java | 26 + .../gadp/error/GadpIllegalStateException.java | 26 + .../dbg/gadp/error/GadpMessageException.java | 26 + .../dbg/gadp/error/GadpRuntimeException.java | 26 + ...AbstractGadpLocalDebuggerModelFactory.java | 133 + .../dbg/gadp/server/AbstractGadpServer.java | 66 + .../dbg/gadp/server/GadpClientHandler.java | 760 +++++ .../util/AsyncProtobufMessageChannel.java | 178 ++ .../ghidra/dbg/gadp/util/GadpValueUtils.java | 738 +++++ .../gadp/util/ProtobufOneofByTypeHelper.java | 89 + .../Debugger-gadp/src/main/proto/gadp.proto | 595 ++++ .../ghidra/dbg/gadp/GadpClientServerTest.java | 1115 +++++++ .../dbg/gadp/client/GadpClientTest.java | 372 +++ .../dbg/gadp/client/GadpClientTestHelper.java | 31 + .../util/AsyncProtobufMessageChannelTest.java | 188 ++ Ghidra/Debug/Debugger-jpda/Module.manifest | 0 Ghidra/Debug/Debugger-jpda/build.gradle | 15 + .../Debugger-jpda/certification.manifest | 5 + .../dbg/jdi/JdiDebuggerModelFactory.java | 37 + .../java/ghidra/dbg/jdi/manager/JdiCause.java | 33 + .../jdi/manager/JdiConsoleOutputListener.java | 32 + .../dbg/jdi/manager/JdiEventHandler.java | 563 ++++ .../dbg/jdi/manager/JdiEventsListener.java | 267 ++ .../jdi/manager/JdiEventsListenerAdapter.java | 157 + .../ghidra/dbg/jdi/manager/JdiManager.java | 219 ++ .../dbg/jdi/manager/JdiMemoryMapping.java | 20 + .../ghidra/dbg/jdi/manager/JdiReason.java | 95 + .../dbg/jdi/manager/JdiStateListener.java | 36 + .../jdi/manager/JdiTargetOutputListener.java | 31 + .../ghidra/dbg/jdi/manager/JdiThreadInfo.java | 42 + .../dbg/jdi/manager/JdiVMThreadGroup.java | 54 + .../manager/breakpoint/JdiBreakpointInfo.java | 163 + .../manager/breakpoint/JdiBreakpointType.java | 65 + .../dbg/jdi/manager/impl/DebugStatus.java | 65 + .../dbg/jdi/manager/impl/JdiManagerImpl.java | 276 ++ .../ghidra/dbg/jdi/model/JdiModelImpl.java | 224 ++ .../JdiModelTargetAttributesContainer.java | 37 + .../JdiModelTargetBreakpointContainer.java | 137 + .../model/JdiModelTargetBreakpointSpec.java | 144 + .../model/JdiModelTargetClassContainer.java | 95 + .../jdi/model/JdiModelTargetConnector.java | 86 + .../JdiModelTargetConnectorContainer.java | 106 + .../jdi/model/JdiModelTargetConstantPool.java | 85 + .../JdiModelTargetElementsContainer.java | 48 + .../dbg/jdi/model/JdiModelTargetField.java | 121 + .../model/JdiModelTargetFieldContainer.java | 75 + .../model/JdiModelTargetLocalVariable.java | 90 + .../JdiModelTargetLocalVariableContainer.java | 78 + .../dbg/jdi/model/JdiModelTargetLocation.java | 114 + .../JdiModelTargetLocationContainer.java | 75 + .../dbg/jdi/model/JdiModelTargetMethod.java | 167 + .../model/JdiModelTargetMethodContainer.java | 86 + .../dbg/jdi/model/JdiModelTargetModule.java | 53 + .../model/JdiModelTargetModuleContainer.java | 124 + .../jdi/model/JdiModelTargetObjectImpl.java | 181 ++ .../model/JdiModelTargetObjectReference.java | 122 + ...diModelTargetObjectReferenceContainer.java | 73 + .../dbg/jdi/model/JdiModelTargetProcess.java | 126 + .../model/JdiModelTargetReferenceType.java | 167 + .../dbg/jdi/model/JdiModelTargetRegister.java | 85 + .../JdiModelTargetRegisterContainer.java | 135 + .../dbg/jdi/model/JdiModelTargetRoot.java | 296 ++ .../dbg/jdi/model/JdiModelTargetSection.java | 94 + .../model/JdiModelTargetSectionContainer.java | 134 + .../dbg/jdi/model/JdiModelTargetStack.java | 109 + .../jdi/model/JdiModelTargetStackFrame.java | 154 + .../dbg/jdi/model/JdiModelTargetThread.java | 389 +++ .../model/JdiModelTargetThreadContainer.java | 124 + .../JdiModelTargetThreadGroupContainer.java | 89 + .../dbg/jdi/model/JdiModelTargetType.java | 47 + .../model/JdiModelTargetTypeContainer.java | 77 + .../dbg/jdi/model/JdiModelTargetVM.java | 433 +++ .../jdi/model/JdiModelTargetVMContainer.java | 181 ++ .../dbg/jdi/model/JdiModelTargetValue.java | 37 + .../model/JdiModelTargetValueContainer.java | 77 + .../dbg/jdi/model/JdiModelTargetValueMap.java | 71 + .../iface1/JdiModelSelectableObject.java | 26 + .../JdiModelTargetAccessConditioned.java | 34 + .../model/iface1/JdiModelTargetAttacher.java | 42 + .../model/iface1/JdiModelTargetConsole.java | 31 + .../model/iface1/JdiModelTargetDeletable.java | 36 + .../iface1/JdiModelTargetDetachable.java | 36 + .../iface1/JdiModelTargetEnvironment.java | 41 + .../iface1/JdiModelTargetEventScope.java | 29 + .../JdiModelTargetExecutionStateful.java | 41 + .../iface1/JdiModelTargetFocusScope.java | 86 + .../iface1/JdiModelTargetInterruptible.java | 36 + .../model/iface1/JdiModelTargetKillable.java | 36 + .../model/iface1/JdiModelTargetLauncher.java | 95 + .../model/iface1/JdiModelTargetMethod.java | 29 + .../model/iface1/JdiModelTargetResumable.java | 36 + .../model/iface1/JdiModelTargetSteppable.java | 36 + .../iface2/JdiModelTargetAttachable.java | 31 + .../model/iface2/JdiModelTargetObject.java | 97 + .../ghidra/dbg/jdi/JdiExperimentsTest.java | 302 ++ .../ghidra/dbg/jdi/model/JdiModelTest.java | 73 + Ghidra/Debug/Debugger-sctl/Module.manifest | 0 Ghidra/Debug/Debugger-sctl/build.gradle | 21 + .../Debugger-sctl/certification.manifest | 5 + .../ghidra/dbg/sctl/client/SctlClient.java | 1824 +++++++++++ .../ghidra/dbg/sctl/client/SctlExtension.java | 37 + .../dbg/sctl/client/SctlMemoryProtection.java | 33 + .../dbg/sctl/client/SctlTargetAttachable.java | 73 + .../client/SctlTargetAttachableContainer.java | 36 + .../dbg/sctl/client/SctlTargetBreakpoint.java | 181 ++ .../client/SctlTargetBreakpointContainer.java | 132 + .../client/SctlTargetCompositeDataType.java | 56 + .../sctl/client/SctlTargetCompositeField.java | 41 + .../sctl/client/SctlTargetDataTypeMember.java | 71 + .../client/SctlTargetDataTypeNamespace.java | 240 ++ .../sctl/client/SctlTargetEnumConstant.java | 40 + .../sctl/client/SctlTargetEnumDataType.java | 72 + .../client/SctlTargetFunctionDataType.java | 63 + .../client/SctlTargetFunctionParameter.java | 44 + .../dbg/sctl/client/SctlTargetMemory.java | 97 + .../sctl/client/SctlTargetMemoryRegion.java | 96 + .../dbg/sctl/client/SctlTargetModule.java | 116 + .../client/SctlTargetModuleContainer.java | 83 + .../sctl/client/SctlTargetNamedDataType.java | 134 + .../dbg/sctl/client/SctlTargetObject.java | 210 ++ .../client/SctlTargetObjectsContainer.java | 118 + .../dbg/sctl/client/SctlTargetProcess.java | 278 ++ .../client/SctlTargetProcessContainer.java | 156 + .../client/SctlTargetRegisterDescription.java | 56 + .../dbg/sctl/client/SctlTargetRegisters.java | 187 ++ .../dbg/sctl/client/SctlTargetSection.java | 66 + .../client/SctlTargetSectionContainer.java | 50 + .../dbg/sctl/client/SctlTargetSession.java | 133 + .../sctl/client/SctlTargetStructDataType.java | 56 + .../dbg/sctl/client/SctlTargetSymbol.java | 118 + .../client/SctlTargetSymbolNamespace.java | 139 + .../dbg/sctl/client/SctlTargetThread.java | 278 ++ .../client/SctlTargetThreadContainer.java | 68 + .../client/SctlTargetTypedefDataType.java | 67 + .../dbg/sctl/client/SctlTargetTypedefDef.java | 24 + .../sctl/client/SctlTargetUnionDataType.java | 55 + .../client/SctlTcpDebuggerModelFactory.java | 76 + .../ghidra/dbg/sctl/client/SctlTrace.java | 118 + .../client/depr/DebuggerAddressMapper.java | 54 + .../depr/DefaultDebuggerAddressMapper.java | 56 + .../client/err/SctlIncorrectPingResponse.java | 31 + .../sctl/client/err/SctlIncorrectReply.java | 30 + .../err/SctlIncorrectVersionRequest.java | 25 + .../err/SctlIncorrectVersionResponse.java | 30 + .../client/err/SctlPartialWriteException.java | 32 + .../err/SctlProtocolSequenceException.java | 27 + .../dbg/sctl/dialect/HasOptionalPlatform.java | 39 + .../sctl/dialect/HasOptionalProcessID.java | 39 + .../ghidra/dbg/sctl/dialect/SctlDialect.java | 119 + .../dbg/sctl/dialect/SctlNullDialect.java | 67 + .../dialect/SelSctlNullDialectPacket.java | 36 + .../java/ghidra/dbg/sctl/err/SctlError.java | 49 + .../dbg/sctl/err/SctlRuntimeException.java | 25 + .../dbg/sctl/protocol/AbstractSctlNotify.java | 26 + .../dbg/sctl/protocol/AbstractSctlReply.java | 23 + .../sctl/protocol/AbstractSctlRequest.java | 23 + .../sctl/protocol/AbstractSelSctlPacket.java | 50 + .../dbg/sctl/protocol/SctlMarshaller.java | 103 + .../ghidra/dbg/sctl/protocol/SctlPacket.java | 25 + .../dbg/sctl/protocol/SctlVersionInfo.java | 149 + .../AbstractByLongFieldsSctlContext.java | 86 + .../protocol/common/AbstractSctlBinary.java | 46 + .../protocol/common/AbstractSctlContext.java | 68 + .../common/AbstractSctlObjectEntry.java | 96 + .../common/AbstractSctlProcessEntry.java | 73 + .../common/AbstractSctlProcessList.java | 41 + .../protocol/common/AbstractSctlRegion.java | 84 + .../protocol/common/AbstractSctlSection.java | 62 + .../protocol/common/AbstractSctlStatus.java | 56 + .../common/AbstractSctlThreadEntry.java | 37 + .../protocol/common/AbstractSctlTrapSpec.java | 58 + .../common/SctlRegisterDefinition.java | 40 + .../dbg/sctl/protocol/common/SctlString.java | 52 + .../dbg/sctl/protocol/common/SctlSymbol.java | 73 + .../notify/AbstractSctlEventNotification.java | 26 + .../AbstractSctlForkCloneNotification.java | 51 + .../notify/AbstractSctlForkNotification.java | 31 + ...ctSctlListsLibrariesEventNotification.java | 74 + .../AbstractSctlOnlyCtxEventNotification.java | 43 + .../notify/AbstractSctlSnapNotification.java | 39 + .../common/notify/SctlCloneNotification.java | 31 + .../common/notify/SctlEventNotify.java | 190 ++ .../common/notify/SctlExecNotification.java | 23 + .../common/notify/SctlExitNotification.java | 45 + .../common/notify/SctlLoadNotification.java | 32 + .../common/notify/SctlSignalNotification.java | 30 + .../notify/SctlSyscallNotification.java | 23 + .../common/notify/SctlTrapNotification.java | 45 + .../common/notify/SctlUnloadNotification.java | 32 + .../common/reply/AbstractSctlAttachReply.java | 43 + .../common/reply/AbstractSctlLaunchReply.java | 33 + .../reply/AbstractSctlSnapshotReply.java | 35 + .../common/reply/SctlClearTrapReply.java | 25 + .../common/reply/SctlContinueReply.java | 25 + .../common/reply/SctlDetachReply.java | 25 + .../reply/SctlEnumerateLocalsReply.java | 119 + .../reply/SctlEnumerateSegmentsReply.java | 25 + .../reply/SctlEnumerateSymbolsReply.java | 37 + .../common/reply/SctlEnumerateTypesReply.java | 37 + .../protocol/common/reply/SctlErrorReply.java | 34 + .../common/reply/SctlGetContextReply.java | 36 + .../protocol/common/reply/SctlKillReply.java | 25 + .../common/reply/SctlLookupAddressReply.java | 28 + .../reply/SctlLookupProgramCounterReply.java | 27 + .../common/reply/SctlLookupSourceReply.java | 34 + .../common/reply/SctlLookupSymbolReply.java | 35 + .../common/reply/SctlLookupTypeReply.java | 28 + .../protocol/common/reply/SctlNamesReply.java | 34 + .../protocol/common/reply/SctlPingReply.java | 34 + .../common/reply/SctlProcessListReply.java | 36 + .../protocol/common/reply/SctlReadReply.java | 34 + .../common/reply/SctlSetContextReply.java | 25 + .../common/reply/SctlSetTrapReply.java | 35 + .../common/reply/SctlStatusReply.java | 28 + .../protocol/common/reply/SctlStepReply.java | 37 + .../protocol/common/reply/SctlStopReply.java | 37 + .../protocol/common/reply/SctlTraceReply.java | 25 + .../common/reply/SctlUnwindOneFrameReply.java | 50 + .../common/reply/SctlVersionReply.java | 34 + .../protocol/common/reply/SctlWriteReply.java | 34 + .../common/request/SctlAttachRequest.java | 34 + .../common/request/SctlClearTrapRequest.java | 38 + .../common/request/SctlContinueRequest.java | 34 + .../common/request/SctlDetachRequest.java | 34 + .../request/SctlEnumerateLocalsRequest.java | 30 + .../request/SctlEnumerateSegmentsRequest.java | 25 + .../request/SctlEnumerateSymbolsRequest.java | 34 + .../request/SctlEnumerateTypesRequest.java | 34 + .../common/request/SctlGetContextRequest.java | 34 + .../common/request/SctlKillRequest.java | 34 + .../common/request/SctlLaunchRequest.java | 55 + .../request/SctlLookupAddressRequest.java | 30 + .../SctlLookupProgramCounterRequest.java | 34 + .../request/SctlLookupSourceRequest.java | 30 + .../request/SctlLookupSymbolRequest.java | 39 + .../common/request/SctlLookupTypeRequest.java | 40 + .../common/request/SctlNamesRequest.java | 32 + .../common/request/SctlPingRequest.java | 34 + .../request/SctlProcessListRequest.java | 25 + .../common/request/SctlReadRequest.java | 46 + .../common/request/SctlSetContextRequest.java | 39 + .../common/request/SctlSetTrapRequest.java | 39 + .../common/request/SctlSnapshotRequest.java | 34 + .../common/request/SctlStatusRequest.java | 34 + .../common/request/SctlStepRequest.java | 34 + .../common/request/SctlStopRequest.java | 34 + .../common/request/SctlTraceRequest.java | 42 + .../request/SctlUnwindOneFrameRequest.java | 30 + .../common/request/SctlVersionRequest.java | 34 + .../common/request/SctlWriteRequest.java | 46 + .../dbg/sctl/protocol/consts/Attrval.java | 23 + .../dbg/sctl/protocol/consts/Cbase.java | 57 + .../dbg/sctl/protocol/consts/Evkind.java | 64 + .../dbg/sctl/protocol/consts/Lkind.java | 23 + .../dbg/sctl/protocol/consts/Mkind.java | 102 + .../dbg/sctl/protocol/consts/Rkind.java | 70 + .../dbg/sctl/protocol/consts/Rulekind.java | 23 + .../dbg/sctl/protocol/consts/Stype.java | 36 + .../dbg/sctl/protocol/consts/Tkind.java | 46 + .../dbg/sctl/protocol/consts/Vkind.java | 23 + .../AbstractSctlAggregateTypeDefinition.java | 45 + .../types/AbstractSctlTaggedTypeName.java | 34 + .../types/AbstractSctlTypeDefinition.java | 25 + .../protocol/types/AbstractSctlTypeName.java | 25 + .../protocol/types/SctlAggregateField.java | 37 + .../protocol/types/SctlArrayTypeName.java | 29 + .../dbg/sctl/protocol/types/SctlAtom.java | 25 + .../protocol/types/SctlAttributeKeyVal.java | 49 + .../sctl/protocol/types/SctlAttributes.java | 43 + .../types/SctlBaseTypeDefinition.java | 36 + .../sctl/protocol/types/SctlBaseTypeName.java | 34 + .../protocol/types/SctlBitfieldTypeName.java | 32 + .../protocol/types/SctlEnumConstTypeName.java | 26 + .../sctl/protocol/types/SctlEnumConstant.java | 31 + .../types/SctlEnumTypeDefinition.java | 48 + .../sctl/protocol/types/SctlEnumTypeName.java | 30 + .../protocol/types/SctlFunctionParameter.java | 34 + .../protocol/types/SctlFunctionTypeName.java | 38 + .../protocol/types/SctlPointerTypeName.java | 26 + .../sctl/protocol/types/SctlStringAtom.java | 27 + .../types/SctlStructTypeDefinition.java | 26 + .../protocol/types/SctlStructTypeName.java | 30 + .../sctl/protocol/types/SctlSymbolAtom.java | 27 + .../types/SctlTypedefTypeDefinition.java | 35 + .../protocol/types/SctlTypedefTypeName.java | 34 + .../dbg/sctl/protocol/types/SctlUIntAtom.java | 26 + .../protocol/types/SctlUndefinedTypeName.java | 26 + .../types/SctlUnionTypeDefinition.java | 26 + .../protocol/types/SctlUnionTypeName.java | 30 + .../protocol/types/SelSctlTypeDefinition.java | 50 + .../sctl/protocol/types/SelSctlTypeName.java | 59 + .../v2012base/Sctl2012AttachReply.java | 35 + .../v2012base/Sctl2012ForkNotification.java | 48 + .../v2012base/Sctl2012LaunchReply.java | 53 + .../v2012base/Sctl2012SnapNotification.java | 38 + .../v2012base/Sctl2012SnapshotReply.java | 35 + .../protocol/v2012base/SelSctl2012Packet.java | 98 + .../v2012base/x86/SctlX86Context.java | 87 + .../x86/linux/Sctl2012LinuxBinary.java | 98 + .../x86/linux/Sctl2012LinuxProcessEntry.java | 76 + .../x86/linux/Sctl2012LinuxProcessList.java | 50 + .../x86/linux/Sctl2012LinuxRegion.java | 81 + .../x86/linux/Sctl2012LinuxStatus.java | 87 + .../x86/linux/Sctl2012LinuxThreadEntry.java | 37 + .../x86/linux/Sctl2012LinuxTrapSpec.java | 141 + .../x86/linux/Sctl2012LinuxX86Dialect.java | 93 + .../x86/win/Sctl2012WindowsX86Context.java | 27 + .../x86/win/Sctl2012WindowsX86Dialect.java | 84 + .../protocol/v2012ext/SctlExecuteReply.java | 40 + .../protocol/v2012ext/SctlExecuteRequest.java | 40 + .../v2012ext/SelSctl2012ExtPacket.java | 40 + .../x86/linux/Sctl2012ExtLinuxX86Dialect.java | 165 + .../x86/win/Sctl2012ExtWindowsX86Dialect.java | 85 + .../v2018base/Sctl2018AttachReply.java | 40 + .../v2018base/Sctl2018ForkNotification.java | 49 + .../v2018base/Sctl2018LaunchReply.java | 58 + .../v2018base/Sctl2018SnapNotification.java | 40 + .../v2018base/Sctl2018SnapshotReply.java | 39 + .../v2018base/SctlChooseContextReply.java | 32 + .../v2018base/SctlChooseContextRequest.java | 45 + .../v2018base/SctlEnumerateContextReply.java | 35 + .../SctlEnumerateContextRequest.java | 31 + .../protocol/v2018base/SctlFocusReply.java | 21 + .../protocol/v2018base/SctlFocusRequest.java | 31 + .../v2018base/SctlGetAttributesReply.java | 36 + .../v2018base/SctlGetAttributesRequest.java | 34 + .../v2018base/SctlGetElementsReply.java | 36 + .../v2018base/SctlGetElementsRequest.java | 33 + .../protocol/v2018base/SctlStepIntoReply.java | 20 + .../v2018base/SctlStepIntoRequest.java | 20 + .../protocol/v2018base/SctlStepOutReply.java | 20 + .../v2018base/SctlStepOutRequest.java | 20 + .../protocol/v2018base/SctlStepOverReply.java | 20 + .../v2018base/SctlStepOverRequest.java | 20 + .../protocol/v2018base/SelSctl2018Packet.java | 40 + .../v2018base/any/Sctl2018AnyAnyDialect.java | 90 + .../v2018base/any/Sctl2018Binary.java | 106 + .../v2018base/any/Sctl2018Context.java | 141 + .../v2018base/any/Sctl2018ObjectEntry.java | 91 + .../v2018base/any/Sctl2018ProcessEntry.java | 76 + .../v2018base/any/Sctl2018ProcessList.java | 49 + .../v2018base/any/Sctl2018Region.java | 78 + .../v2018base/any/Sctl2018Section.java | 61 + .../v2018base/any/Sctl2018Status.java | 81 + .../v2018base/any/Sctl2018ThreadEntry.java | 37 + .../v2018base/any/Sctl2018TrapSpec.java | 178 ++ .../server/AbstractSctlClientHandler.java | 281 ++ .../dbg/sctl/server/AbstractSctlServer.java | 155 + .../dbg/sctl/server/SctlSyntheticClient.java | 208 ++ .../dbg/sctl/client/SctlClientTest.java | 380 +++ .../dbg/sctl/client/SctlOnReferenceTest.java | 1455 +++++++++ .../dbg/sctl/client/SctlPacketsTest.java | 88 + Ghidra/Debug/Debugger/Module.manifest | 0 Ghidra/Debug/Debugger/build.gradle | 22 + Ghidra/Debug/Debugger/certification.manifest | 183 ++ .../Debugger/data/ExtensionPoint.manifest | 5 + .../ghidra_scripts/PopulateDemoTrace.java | 730 +++++ .../src/main/help/help/TOC_Source.xml | 128 + .../src/main/help/help/shared/Frontpage.css | 67 + .../src/main/help/help/shared/arrow.gif | Bin 0 -> 69 bytes .../src/main/help/help/shared/close16.gif | Bin 0 -> 859 bytes .../src/main/help/help/shared/menu16.gif | Bin 0 -> 62 bytes .../src/main/help/help/shared/note-red.png | Bin 0 -> 4182 bytes .../src/main/help/help/shared/note.png | Bin 0 -> 4193 bytes .../src/main/help/help/shared/note.yellow.png | Bin 0 -> 4170 bytes .../src/main/help/help/shared/redo.png | Bin 0 -> 187 bytes .../src/main/help/help/shared/tip.png | Bin 0 -> 1641 bytes .../src/main/help/help/shared/undo.png | Bin 0 -> 185 bytes .../src/main/help/help/shared/warning.png | Bin 0 -> 1354 bytes .../help/help/topics/Debugger/Debugger.html | 69 + .../help/topics/Debugger/GettingStarted.html | 165 + .../help/topics/Debugger/Troubleshooting.html | 111 + .../topics/DebuggerBots/DebuggerBots.html | 63 + .../DebuggerBreakpointMarkerPlugin.html | 113 + .../images/DebuggerBreakpointMarkerPlugin.png | Bin 0 -> 64007 bytes .../images/DebuggerPlaceBreakpointDialog.png | Bin 0 -> 7308 bytes .../images/breakpoint-clear.png | Bin 0 -> 365 bytes .../images/breakpoint-disable.png | Bin 0 -> 295 bytes .../images/breakpoint-enable.png | Bin 0 -> 269 bytes .../DebuggerBreakpointsPlugin.html | 165 + .../images/DebuggerBreakpointsPlugin.png | Bin 0 -> 29159 bytes .../images/breakpoint-disable.png | Bin 0 -> 295 bytes .../images/breakpoint-enable.png | Bin 0 -> 269 bytes .../images/breakpoint-mixed-ed.png | Bin 0 -> 373 bytes .../images/breakpoints-clear-all.png | Bin 0 -> 504 bytes .../images/breakpoints-disable-all.png | Bin 0 -> 420 bytes .../images/breakpoints-enable-all.png | Bin 0 -> 329 bytes .../DebuggerListingPlugin.html | 192 ++ .../images/DebuggerGoToDialog.png | Bin 0 -> 9986 bytes .../images/DebuggerListingPlugin.png | Bin 0 -> 42946 bytes .../images/DebuggerModuleImportDialog.png | Bin 0 -> 11360 bytes .../DebuggerModelServicePlugin.html | 42 + .../DebuggerModulesPlugin.html | 165 + .../DebuggerModuleMapProposalDialog.png | Bin 0 -> 15294 bytes .../images/DebuggerModulesPlugin.png | Bin 0 -> 29396 bytes .../DebuggerSectionMapProposalDialog.png | Bin 0 -> 22173 bytes .../DebuggerObjectsPlugin.html | 351 +++ .../images/DebuggerBreakpointDialog.png | Bin 0 -> 5981 bytes ...buggerMethodInvocationDialog_ForLaunch.png | Bin 0 -> 11168 bytes .../images/DebuggerObjectsPlugin.png | Bin 0 -> 29826 bytes .../DebuggerObjectsPlugin/images/attach.png | Bin 0 -> 562 bytes .../DebuggerObjectsPlugin/images/blank.png | Bin 0 -> 149 bytes .../images/breakpoint-set.png | Bin 0 -> 383 bytes .../DebuggerObjectsPlugin/images/console.png | Bin 0 -> 305 bytes .../DebuggerObjectsPlugin/images/continue.png | Bin 0 -> 277 bytes .../DebuggerObjectsPlugin/images/debugger.png | Bin 0 -> 537 bytes .../DebuggerObjectsPlugin/images/detach.png | Bin 0 -> 590 bytes .../images/display_as_graph.png | Bin 0 -> 368 bytes .../images/display_as_table.png | Bin 0 -> 566 bytes .../images/display_as_tree.png | Bin 0 -> 295 bytes .../images/display_as_xml.png | Bin 0 -> 857 bytes .../images/display_filtered_graph.png | Bin 0 -> 368 bytes .../images/display_filtered_table.png | Bin 0 -> 566 bytes .../images/display_filtered_tree.png | Bin 0 -> 295 bytes .../images/display_filtered_xml.png | Bin 0 -> 857 bytes .../images/export_as_facts.png | Bin 0 -> 545 bytes .../images/export_as_xml.png | Bin 0 -> 857 bytes .../images/import_from_facts.png | Bin 0 -> 545 bytes .../images/import_from_xml.png | Bin 0 -> 857 bytes .../DebuggerObjectsPlugin/images/kill.png | Bin 0 -> 533 bytes .../DebuggerObjectsPlugin/images/launch.png | Bin 0 -> 623 bytes .../DebuggerObjectsPlugin/images/record.png | Bin 0 -> 389 bytes .../DebuggerObjectsPlugin/images/reload.png | Bin 0 -> 1135 bytes .../DebuggerObjectsPlugin/images/stepinto.png | Bin 0 -> 286 bytes .../DebuggerObjectsPlugin/images/stepout.png | Bin 0 -> 265 bytes .../DebuggerObjectsPlugin/images/stepover.png | Bin 0 -> 270 bytes .../DebuggerObjectsPlugin/images/stop.png | Bin 0 -> 172 bytes .../DebuggerRegionsPlugin.html | 69 + .../images/DebuggerRegionsPlugin.png | Bin 0 -> 19430 bytes .../DebuggerRegistersPlugin.html | 120 + .../DebuggerAvailableRegistersDialog.png | Bin 0 -> 43243 bytes .../images/DebuggerRegistersPlugin.png | Bin 0 -> 36515 bytes .../images/select-registers.png | Bin 0 -> 269 bytes .../DebuggerStackPlugin.html | 52 + .../images/DebuggerStackPlugin.png | Bin 0 -> 9994 bytes .../DebuggerStaticMappingPlugin.html | 76 + .../images/DebuggerStaticMappingPlugin.png | Bin 0 -> 16921 bytes .../DebuggerTargetsPlugin.html | 72 + .../images/DebuggerConnectDialog.png | Bin 0 -> 12021 bytes .../images/DebuggerTargetsPlugin.png | Bin 0 -> 8595 bytes .../DebuggerTargetsPlugin/images/connect.png | Bin 0 -> 525 bytes .../images/disconnect.png | Bin 0 -> 429 bytes .../DebuggerThreadsPlugin.html | 146 + .../images/DebuggerThreadsPlugin.png | Bin 0 -> 23484 bytes .../DebuggerThreadsPlugin/images/continue.png | Bin 0 -> 277 bytes .../DebuggerThreadsPlugin/images/stepback.png | Bin 0 -> 298 bytes .../DebuggerThreadsPlugin/images/stepinto.png | Bin 0 -> 286 bytes .../DebuggerTimePlugin.html | 54 + .../images/DebuggerTimePlugin.png | Bin 0 -> 19520 bytes .../core/debug/AbstractDebuggerPlugin.java | 33 + .../core/debug/DebuggerCoordinates.java | 359 +++ .../core/debug/DebuggerPluginPackage.java | 27 + .../event/ModelActivatedPluginEvent.java | 36 + .../event/ModelObjectFocusedPluginEvent.java | 48 + .../event/TraceActivatedPluginEvent.java | 34 + .../debug/event/TraceClosedPluginEvent.java | 36 + .../debug/event/TraceLocationPluginEvent.java | 48 + .../debug/event/TraceOpenedPluginEvent.java | 36 + .../TraceRecorderAdvancedPluginEvent.java | 57 + .../event/TraceSelectionPluginEvent.java | 49 + .../debug/export/TraceViewAsciiExporter.java | 27 + .../debug/export/TraceViewBinaryExporter.java | 27 + .../debug/export/TraceViewHtmlExporter.java | 27 + .../export/TraceViewIntelHexExporter.java | 27 + .../debug/export/TraceViewXmlExporter.java | 56 + .../core/debug/gui/DebuggerProvider.java | 27 + .../core/debug/gui/DebuggerResources.java | 1245 ++++++++ .../debug/gui/DebuggerSnapActionContext.java | 31 + .../gui/breakpoint/BreakpointLocationRow.java | 73 + ...BreakpointEnablementTableCellRenderer.java | 52 + .../DebuggerBreakpointHistoryPlugin.java | 20 + .../DebuggerBreakpointHistoryProvider.java | 20 + ...uggerBreakpointLocationsActionContext.java | 33 + .../DebuggerBreakpointMarkerPlugin.java | 938 ++++++ .../breakpoint/DebuggerBreakpointsPlugin.java | 77 + .../DebuggerBreakpointsProvider.java | 971 ++++++ ...buggerLogicalBreakpointsActionContext.java | 33 + .../DebuggerPlaceBreakpointDialog.java | 141 + .../gui/breakpoint/LogicalBreakpointRow.java | 107 + ...tractDebuggerWrappedConsoleConnection.java | 167 + .../DebuggerInterpreterPlugin.java | 116 + .../DebuggerWrappedConsoleConnection.java | 34 + .../DebuggerWrappedInterpreterConnection.java | 41 + .../listing/CursorBackgroundColorModel.java | 81 + .../debug/gui/listing/DebuggerGoToDialog.java | 128 + .../DebuggerListingAutoReadMemoryAction.java | 190 ++ .../gui/listing/DebuggerListingPlugin.java | 495 +++ .../gui/listing/DebuggerListingProvider.java | 1181 +++++++ .../DebuggerListingTrackLocationAction.java | 290 ++ .../listing/DebuggerModuleImportDialog.java | 251 ++ ...edRegisterListingBackgroundColorModel.java | 82 + ...emoryStateListingBackgroundColorModel.java | 133 + ...ltiBlendedListingBackgroundColorModel.java | 101 + .../gui/memory/DebuggerMemoryBytesPlugin.java | 35 + .../memory/DebuggerRegionActionContext.java | 36 + .../gui/memory/DebuggerRegionsPlugin.java | 71 + .../gui/memory/DebuggerRegionsProvider.java | 416 +++ .../core/debug/gui/memory/RegionRow.java | 120 + .../AbstractDebuggerMapProposalDialog.java | 92 + .../modules/DebuggerBlockChooserDialog.java | 238 ++ .../modules/DebuggerModuleActionContext.java | 36 + .../DebuggerModuleMapProposalDialog.java | 144 + .../gui/modules/DebuggerModulesPlugin.java | 85 + .../gui/modules/DebuggerModulesProvider.java | 1137 +++++++ .../modules/DebuggerSectionActionContext.java | 46 + .../DebuggerSectionMapProposalDialog.java | 141 + .../DebuggerStaticMappingActionContext.java | 36 + .../modules/DebuggerStaticMappingPlugin.java | 86 + .../DebuggerStaticMappingProvider.java | 391 +++ .../core/debug/gui/modules/ModuleRow.java | 70 + .../core/debug/gui/modules/SectionRow.java | 82 + .../debug/gui/modules/StaticMappingRow.java | 64 + .../gui/objects/DebuggerObjectsPlugin.java | 274 ++ .../gui/objects/DebuggerObjectsProvider.java | 1749 +++++++++++ .../debug/gui/objects/ObjectContainer.java | 500 +++ .../gui/objects/ObjectContainerListener.java | 22 + .../gui/objects/ObjectUpdateService.java | 29 + .../debug/gui/objects/ObjectUpdatedEvent.java | 33 + .../gui/objects/actions/DisplayAsAction.java | 101 + .../objects/actions/DisplayAsGraphAction.java | 99 + .../objects/actions/DisplayAsTableAction.java | 47 + .../objects/actions/DisplayAsTreeAction.java | 48 + .../objects/actions/DisplayAsXMLAction.java | 69 + .../actions/DisplayFilteredAction.java | 103 + .../actions/DisplayFilteredGraphAction.java | 98 + .../actions/DisplayFilteredTableAction.java | 52 + .../actions/DisplayFilteredTreeAction.java | 51 + .../actions/DisplayFilteredXMLAction.java | 79 + .../objects/actions/DisplayMethodsAction.java | 111 + .../objects/actions/ExportAsFactsAction.java | 108 + .../objects/actions/ExportAsXMLAction.java | 76 + .../objects/actions/ImportExportAsAction.java | 89 + .../actions/ImportFromFactsAction.java | 146 + .../objects/actions/ImportFromXMLAction.java | 133 + .../gui/objects/actions/OpenTraceAction.java | 89 + .../AttachableProcessesTableColumns.java | 53 + .../components/DebuggerAttachDialog.java | 175 ++ .../components/DebuggerAttachDialogOld.java | 101 + .../components/DebuggerBreakpointDialog.java | 98 + .../DebuggerMethodInvocationDialog.java | 241 ++ .../objects/components/DummyTargetObject.java | 222 ++ .../components/ObjectAttributeColumn.java | 53 + .../components/ObjectAttributeRow.java | 84 + .../components/ObjectElementColumn.java | 57 + .../objects/components/ObjectElementRow.java | 109 + .../ObjectEnumeratedColumnTableModel.java | 319 ++ .../gui/objects/components/ObjectNode.java | 200 ++ .../gui/objects/components/ObjectPane.java | 51 + .../gui/objects/components/ObjectTable.java | 304 ++ .../gui/objects/components/ObjectTree.java | 402 +++ .../components/ObjectTreeCellRenderer.java | 123 + .../gui/register/AvailableRegisterRow.java | 104 + ...buggerAvailableRegistersActionContext.java | 32 + .../DebuggerAvailableRegistersDialog.java | 327 ++ .../DebuggerRegisterActionContext.java | 33 + .../gui/register/DebuggerRegistersPlugin.java | 241 ++ .../register/DebuggerRegistersProvider.java | 1230 ++++++++ .../core/debug/gui/register/RegisterRow.java | 107 + .../gui/stack/DebuggerStackActionContext.java | 35 + .../debug/gui/stack/DebuggerStackPlugin.java | 66 + .../gui/stack/DebuggerStackProvider.java | 461 +++ .../core/debug/gui/stack/StackFrameRow.java | 94 + .../gui/target/DebuggerConnectDialog.java | 310 ++ .../gui/target/DebuggerConnectionsNode.java | 114 + .../target/DebuggerModelActionContext.java | 56 + .../debug/gui/target/DebuggerModelNode.java | 60 + .../gui/target/DebuggerTargetsPlugin.java | 81 + .../gui/target/DebuggerTargetsProvider.java | 309 ++ .../thread/DebuggerThreadActionContext.java | 38 + .../gui/thread/DebuggerThreadsPlugin.java | 76 + .../gui/thread/DebuggerThreadsProvider.java | 674 ++++ .../thread/DebuggerThreadsTimelinePanel.java | 169 + .../core/debug/gui/thread/ThreadRow.java | 114 + .../core/debug/gui/thread/ThreadState.java | 43 + .../debug/gui/thread/ThreadTableColumns.java | 72 + .../debug/gui/time/DebuggerTimePlugin.java | 66 + .../debug/gui/time/DebuggerTimeProvider.java | 296 ++ .../core/debug/gui/time/SnapshotRow.java | 69 + .../DebuggerTimelineActionContext.java | 30 + .../gui/timeline/DebuggerTimelinePanel.java | 161 + .../gui/timeline/DebuggerTimelinePlugin.java | 124 + .../timeline/DebuggerTimelineProvider.java | 487 +++ .../core/debug/gui/timeline/TimelineRow.java | 109 + .../debug/gui/timeline/TimelineState.java | 43 + .../gui/timeline/TimelineTableColumns.java | 73 + .../core/debug/gui/timeline/TraceObject.java | 151 + .../gui/watch/DebuggerWatchActionContext.java | 43 + .../gui/watch/DebuggerWatchesPlugin.java | 69 + .../gui/watch/DebuggerWatchesProvider.java | 310 ++ .../plugin/core/debug/gui/watch/WatchRow.java | 266 ++ .../mapping/AbstractDebuggerMappingOffer.java | 73 + .../AbstractDebuggerTargetTraceMapper.java | 94 + .../debug/mapping/DebuggerMappingOffer.java | 50 + .../debug/mapping/DebuggerMappingOpinion.java | 63 + .../debug/mapping/DebuggerMemoryMapper.java | 53 + .../debug/mapping/DebuggerRegisterMapper.java | 172 ++ .../mapping/DebuggerTargetTraceMapper.java | 31 + .../mapping/DefaultDebuggerMemoryMapper.java | 74 + .../DefaultDebuggerRegisterMapper.java | 148 + .../LargestSubDebuggerRegisterMapper.java | 186 ++ .../AbstractGdbDebuggerMappingOffer.java | 41 + .../debug/platform/ArmDisassemblyInject.java | 92 + .../DbgengX64DebuggerMappingOpinion.java | 111 + .../platform/DbgengX64DisassemblyInject.java | 126 + .../GdbArmDebuggerMappingOpinion.java | 84 + .../debug/platform/GdbTargetTraceMapper.java | 41 + .../GdbX86DebuggerMappingOpinion.java | 158 + .../JdiDalvikDebuggerMappingOpinion.java | 95 + .../JdiJavaDebuggerMappingOpinion.java | 95 + .../DynamicRegisterChangeListener.java | 26 + .../core/debug/register/RegisterTypeInfo.java | 40 + .../breakpoint/BreakpointActionItem.java | 22 + .../breakpoint/BreakpointActionSet.java | 40 + ...ebuggerLogicalBreakpointServicePlugin.java | 1092 +++++++ .../DeleteBreakpointActionItem.java | 57 + .../DisableBreakpointActionItem.java | 51 + .../EnableBreakpointActionItem.java | 51 + .../breakpoint/LogicalBreakpointInternal.java | 506 +++ .../breakpoint/LoneLogicalBreakpoint.java | 252 ++ .../breakpoint/MappedLogicalBreakpoint.java | 377 +++ .../breakpoint/PlaceBreakpointActionItem.java | 73 + .../model/DebuggerModelServiceInternal.java | 137 + .../model/DebuggerModelServicePlugin.java | 701 +++++ .../DebuggerModelServiceProxyPlugin.java | 477 +++ .../DebuggerSelectMappingOfferDialog.java | 173 ++ .../service/model/DefaultTraceRecorder.java | 2597 ++++++++++++++++ .../DebuggerStaticMappingServicePlugin.java | 1385 +++++++++ .../modules/MapModulesBackgroundCommand.java | 48 + .../modules/MapSectionsBackgroundCommand.java | 48 + .../DebuggerTraceManagerServicePlugin.java | 922 ++++++ .../AbstractMultiToolTraceListener.java | 49 + .../DebuggerWorkflowServicePlugin.java | 370 +++ .../DebuggerWorkflowServiceProxyPlugin.java | 106 + .../MultiToolTraceListenerManager.java | 73 + .../core/debug/utils/BackgroundUtils.java | 86 + .../utils/DomainFolderChangeAdapter.java | 72 + .../plugin/core/debug/utils/FocusUtils.java | 34 + .../core/debug/utils/MiscellaneousUtils.java | 60 + .../debug/utils/ProgramLocationUtils.java | 43 + .../core/debug/utils/ProgramURLUtils.java | 81 + .../debug/utils/TransactionCoalescer.java | 65 + .../workflow/DisassembleAtPcDebuggerBot.java | 319 ++ .../debug/workflow/DisassemblyInject.java | 112 + .../debug/workflow/DisassemblyInjectInfo.java | 42 + .../debug/workflow/MapModulesDebuggerBot.java | 217 ++ .../workflow/MapSectionsDebuggerBot.java | 193 ++ .../workflow/ShowInterpreterDebuggerBot.java | 86 + .../java/ghidra/app/services/DebuggerBot.java | 241 ++ .../ghidra/app/services/DebuggerBotInfo.java | 76 + .../services/DebuggerInterpreterService.java | 31 + .../app/services/DebuggerListingService.java | 39 + .../DebuggerLogicalBreakpointService.java | 264 ++ .../app/services/DebuggerModelService.java | 327 ++ .../DebuggerStaticMappingChangeListener.java | 37 + .../DebuggerStaticMappingService.java | 833 +++++ .../services/DebuggerTraceManagerService.java | 183 ++ .../app/services/DebuggerWorkflowService.java | 37 + .../app/services/LogicalBreakpoint.java | 364 +++ .../LogicalBreakpointsChangeListener.java | 58 + .../ghidra/app/services/TraceRecorder.java | 483 +++ .../app/services/TraceRecorderListener.java | 60 + .../ghidra/pcode/exec/AsyncPcodeExecutor.java | 76 + .../exec/AsyncWrappedPcodeArithmetic.java | 71 + .../exec/AsyncWrappedPcodeExecutorState.java | 25 + .../AsyncWrappedPcodeExecutorStatePiece.java | 72 + .../pcode/exec/TracePcodeExecutorState.java | 16 + .../ghidra/pcode/exec/TracePcodeUtils.java | 52 + .../TraceRecorderAsyncPcodeExecutorState.java | 126 + .../main/resources/defaultTools/Debugger.tool | 799 +++++ .../main/resources/define_info_proc_mappings | 4 + .../src/main/resources/images/add.png | Bin 0 -> 733 bytes .../src/main/resources/images/attach.png | Bin 0 -> 562 bytes .../src/main/resources/images/autoread.png | Bin 0 -> 942 bytes .../src/main/resources/images/blank.png | Bin 0 -> 149 bytes .../resources/images/breakpoint-clear.png | Bin 0 -> 365 bytes .../resources/images/breakpoint-disable.png | Bin 0 -> 295 bytes .../resources/images/breakpoint-enable.png | Bin 0 -> 269 bytes .../resources/images/breakpoint-mixed-de.png | Bin 0 -> 368 bytes .../resources/images/breakpoint-mixed-ed.png | Bin 0 -> 373 bytes .../main/resources/images/breakpoint-set.png | Bin 0 -> 383 bytes .../images/breakpoints-clear-all.png | Bin 0 -> 504 bytes .../images/breakpoints-disable-all.png | Bin 0 -> 420 bytes .../images/breakpoints-enable-all.png | Bin 0 -> 329 bytes .../src/main/resources/images/breakpoints.png | Bin 0 -> 356 bytes .../main/resources/images/closedFolder.png | Bin 0 -> 545 bytes .../src/main/resources/images/connect.png | Bin 0 -> 525 bytes .../src/main/resources/images/console.png | Bin 0 -> 305 bytes .../src/main/resources/images/continue.png | Bin 0 -> 277 bytes .../src/main/resources/images/debugger.png | Bin 0 -> 537 bytes .../src/main/resources/images/debugger32.png | Bin 0 -> 1019 bytes .../src/main/resources/images/delete.png | Bin 0 -> 715 bytes .../src/main/resources/images/detach.png | Bin 0 -> 590 bytes .../src/main/resources/images/disconnect.png | Bin 0 -> 429 bytes .../src/main/resources/images/kill.png | Bin 0 -> 533 bytes .../src/main/resources/images/launch.png | Bin 0 -> 623 bytes .../src/main/resources/images/modules.png | Bin 0 -> 384 bytes .../resources/images/object-populated.png | Bin 0 -> 269 bytes .../main/resources/images/object-running.png | Bin 0 -> 159 bytes .../resources/images/object-unpopulated.png | Bin 0 -> 295 bytes .../src/main/resources/images/process.png | Bin 0 -> 564 bytes .../src/main/resources/images/record.png | Bin 0 -> 389 bytes .../main/resources/images/register-marker.png | Bin 0 -> 237 bytes .../src/main/resources/images/registers.png | Bin 0 -> 225 bytes .../resources/images/select-registers.png | Bin 0 -> 269 bytes .../src/main/resources/images/skipover.png | Bin 0 -> 320 bytes .../src/main/resources/images/stack.png | Bin 0 -> 184 bytes .../src/main/resources/images/stepback.png | Bin 0 -> 298 bytes .../src/main/resources/images/stepinto.png | Bin 0 -> 286 bytes .../src/main/resources/images/stepout.png | Bin 0 -> 323 bytes .../src/main/resources/images/stepover.png | Bin 0 -> 270 bytes .../src/main/resources/images/stop.png | Bin 0 -> 172 bytes .../main/resources/images/sync_enabled.png | Bin 0 -> 446 bytes .../src/main/resources/images/table.png | Bin 0 -> 566 bytes .../src/main/resources/images/text-xml.png | Bin 0 -> 857 bytes .../src/main/resources/images/thread.png | Bin 0 -> 356 bytes .../src/main/resources/images/time.png | Bin 0 -> 793 bytes Ghidra/Debug/Debugger/src/main/svg/attach.svg | 42 + Ghidra/Debug/Debugger/src/main/svg/blank.svg | 30 + .../src/main/svg/breakpoint-clear.svg | 42 + .../src/main/svg/breakpoint-disable.svg | 34 + .../src/main/svg/breakpoint-enable.svg | 36 + .../src/main/svg/breakpoint-mixed-de.svg | 34 + .../src/main/svg/breakpoint-mixed-ed.svg | 42 + .../Debugger/src/main/svg/breakpoint-set.svg | 40 + .../src/main/svg/breakpoints-clear-all.svg | 58 + .../src/main/svg/breakpoints-disable-all.svg | 42 + .../src/main/svg/breakpoints-enable-all.svg | 40 + .../Debugger/src/main/svg/breakpoints.svg | 40 + .../Debug/Debugger/src/main/svg/connect.svg | 38 + .../Debug/Debugger/src/main/svg/console.svg | 38 + .../Debug/Debugger/src/main/svg/continue.svg | 38 + .../Debug/Debugger/src/main/svg/debugger.svg | 38 + Ghidra/Debug/Debugger/src/main/svg/detach.svg | 42 + .../Debugger/src/main/svg/disconnect.svg | 38 + Ghidra/Debug/Debugger/src/main/svg/kill.svg | 45 + Ghidra/Debug/Debugger/src/main/svg/launch.svg | 42 + Ghidra/Debug/Debugger/src/main/svg/memory.svg | 34 + .../Debug/Debugger/src/main/svg/process.svg | 42 + .../Debug/Debugger/src/main/svg/recording.svg | 48 + .../Debugger/src/main/svg/register-marker.svg | 34 + .../Debug/Debugger/src/main/svg/registers.svg | 34 + .../src/main/svg/select-registers.svg | 44 + .../Debug/Debugger/src/main/svg/skipover.svg | 63 + Ghidra/Debug/Debugger/src/main/svg/stack.svg | 51 + .../Debug/Debugger/src/main/svg/stepback.svg | 56 + .../Debug/Debugger/src/main/svg/stepinto.svg | 52 + .../Debug/Debugger/src/main/svg/stepout.svg | 52 + .../Debug/Debugger/src/main/svg/stepover.svg | 55 + Ghidra/Debug/Debugger/src/main/svg/stop.svg | 44 + Ghidra/Debug/Debugger/src/main/svg/thread.svg | 34 + .../core/debug/gui/CheckImagesTest.java | 47 + ...ggerBreakpointMarkerPluginScreenShots.java | 102 + .../DebuggerBreakpointsPluginScreenShots.java | 167 + .../DebuggerListingPluginScreenShots.java | 175 ++ .../DebuggerRegionsPluginScreenShots.java | 79 + .../DebuggerModulesPluginScreenShots.java | 168 + ...ebuggerStaticMappingPluginScreenShots.java | 139 + .../DebuggerObjectsPluginScreenShots.java | 292 ++ .../DebuggerObjectsAccessHelper.java | 23 + .../DebuggerRegistersPluginScreenShots.java | 121 + .../stack/DebuggerStackPluginScreenShots.java | 128 + .../DebuggerTargetsPluginScreenShots.java | 117 + .../DebuggerThreadsPluginScreenShots.java | 110 + .../time/DebuggerTimePluginScreenShots.java | 89 + .../AbstractGhidraHeadedDebuggerGUITest.java | 600 ++++ .../core/debug/gui/DebuggerManualTest.java | 151 + ...DebuggerBreakpointHistoryProviderTest.java | 29 + .../DebuggerBreakpointMarkerPluginTest.java | 705 +++++ .../DebuggerBreakpointsProviderTest.java | 600 ++++ .../DebuggerInterpreterPluginTest.java | 166 + .../listing/DebuggerListingProviderTest.java | 1335 ++++++++ .../memory/DebuggerRegionsProviderTest.java | 260 ++ .../modules/DebuggerModulesProviderTest.java | 701 +++++ .../DebuggerStaticMappingProviderTest.java | 287 ++ .../objects/DebuggerObjectsProviderTest.java | 51 + .../DebuggerRegistersProviderTest.java | 898 ++++++ .../gui/stack/DebuggerStackProviderTest.java | 507 +++ .../target/DebuggerTargetsProviderFriend.java | 36 + .../target/DebuggerTargetsProviderTest.java | 232 ++ .../thread/DebuggerThreadsProviderTest.java | 555 ++++ .../gui/time/DebuggerTimeProviderTest.java | 292 ++ .../LargestSubDebuggerRegisterMapperTest.java | 305 ++ .../debug/opinion/DebuggerOpinionsTest.java | 29 + .../DebuggerLogicalBreakpointServiceTest.java | 1355 ++++++++ .../model/DebuggerModelServiceTest.java | 496 +++ .../model/DefaultTraceRecorderTest.java | 249 ++ .../DebuggerStaticMappingServiceTest.java | 547 ++++ .../DebuggerTraceManagerServiceTest.java | 443 +++ .../service/url/ProjectExperimentsTest.java | 245 ++ .../exec/TraceRecorderAsyncPcodeExecTest.java | 104 + .../Debug/Framework-AsyncComm/Module.manifest | 0 Ghidra/Debug/Framework-AsyncComm/build.gradle | 41 + .../certification.manifest | 19 + .../java/ghidra/async/AsyncClaimQueue.java | 109 + .../ghidra/async/AsyncConfigFieldCodec.java | 52 + .../java/ghidra/async/AsyncDebouncer.java | 123 + .../main/java/ghidra/async/AsyncFence.java | 96 + .../ghidra/async/AsyncHandlerCanExit.java | 67 + .../main/java/ghidra/async/AsyncLazyMap.java | 423 +++ .../java/ghidra/async/AsyncLazyValue.java | 151 + .../src/main/java/ghidra/async/AsyncLock.java | 332 ++ .../java/ghidra/async/AsyncPairingCache.java | 209 ++ .../java/ghidra/async/AsyncPairingQueue.java | 69 + .../src/main/java/ghidra/async/AsyncRace.java | 78 + .../java/ghidra/async/AsyncReference.java | 467 +++ .../main/java/ghidra/async/AsyncTimer.java | 234 ++ .../main/java/ghidra/async/AsyncUtils.java | 898 ++++++ .../ghidra/async/SwingExecutorService.java | 66 + .../src/main/java/ghidra/async/TypeSpec.java | 344 +++ .../java/ghidra/async/loop/AsyncLoop.java | 121 + ...yncLoopFirstActionConsumesAndProduces.java | 36 + .../loop/AsyncLoopFirstActionProduces.java | 34 + .../async/loop/AsyncLoopHandlerForFirst.java | 48 + .../async/loop/AsyncLoopHandlerForSecond.java | 107 + .../async/loop/AsyncLoopOnlyActionRuns.java | 31 + .../loop/AsyncLoopSecondActionConsumes.java | 40 + .../seq/AsyncSequenceActionConsumes.java | 35 + ...syncSequenceActionConsumesAndProduces.java | 38 + .../seq/AsyncSequenceActionProduces.java | 37 + .../async/seq/AsyncSequenceActionRuns.java | 33 + .../seq/AsyncSequenceHandlerForProducer.java | 49 + .../seq/AsyncSequenceHandlerForRunner.java | 64 + .../async/seq/AsyncSequenceWithTemp.java | 307 ++ .../async/seq/AsyncSequenceWithoutTemp.java | 222 ++ .../comm/packet/AbstractPacketCodec.java | 596 ++++ .../comm/packet/AbstractPacketFactory.java | 112 + .../comm/packet/AbstractPacketMarshaller.java | 86 + .../packet/AsynchronousPacketChannel.java | 161 + .../AsynchronousPacketDebugChannel.java | 53 + .../ghidra/comm/packet/DebugByteChannel.java | 102 + .../comm/packet/DefaultPacketFactory.java | 41 + .../main/java/ghidra/comm/packet/Packet.java | 1123 +++++++ .../java/ghidra/comm/packet/PacketCodec.java | 99 + .../ghidra/comm/packet/PacketFactory.java | 176 ++ .../ghidra/comm/packet/PacketMarshaller.java | 80 + .../comm/packet/annot/BitmaskEncoded.java | 35 + .../comm/packet/annot/CountedByField.java | 55 + .../ghidra/comm/packet/annot/EncodeChars.java | 43 + .../comm/packet/annot/OptionalField.java | 43 + .../comm/packet/annot/RepeatedField.java | 72 + .../comm/packet/annot/SizedByField.java | 59 + .../comm/packet/annot/SizedByMethods.java | 69 + .../comm/packet/annot/TypedByField.java | 109 + .../comm/packet/annot/TypedByLookahead.java | 81 + .../ghidra/comm/packet/annot/WithFlag.java | 111 + .../annot/impl/BitmaskEncodedFieldCodec.java | 69 + ...itmaskEncodedFieldCodecWrapperFactory.java | 97 + .../annot/impl/CountedByFieldFieldCodec.java | 66 + .../CountedByFieldInjectedFieldCodec.java | 76 + .../impl/CountedByFieldInjectionFactory.java | 51 + .../impl/CountedByFieldWrapperFactory.java | 69 + .../annot/impl/EncodeCharsFieldCodec.java | 80 + .../annot/impl/EncodeCharsWrapperFactory.java | 61 + .../annot/impl/OptionalFieldFieldCodec.java | 67 + .../impl/OptionalFieldWrapperFactory.java | 50 + .../impl/RepeatedFieldWrapperFactory.java | 114 + .../annot/impl/SizedByFieldFieldCodec.java | 80 + .../impl/SizedByFieldInjectedFieldCodec.java | 82 + .../impl/SizedByFieldInjectionFactory.java | 52 + .../impl/SizedByFieldWrapperFactory.java | 69 + .../annot/impl/SizedByMethodsFieldCodec.java | 77 + .../SizedByMethodsInjectedFieldCodec.java | 83 + .../impl/SizedByMethodsInjectionFactory.java | 56 + .../impl/SizedByMethodsWrapperFactory.java | 80 + .../annot/impl/TypedByFieldFieldCodec.java | 97 + .../impl/TypedByFieldInjectedFieldCodec.java | 98 + .../impl/TypedByFieldInjectionFactory.java | 56 + .../impl/TypedByFieldWrapperFactory.java | 104 + .../impl/TypedByLookaheadFieldCodec.java | 89 + .../impl/TypedByLookaheadWrapperFactory.java | 79 + .../packet/annot/impl/WithFlagFieldCodec.java | 81 + .../impl/WithFlagInjectedFieldCodec.java | 89 + .../annot/impl/WithFlagInjectionFactory.java | 57 + .../annot/impl/WithFlagWrapperFactory.java | 79 + .../binary/AbstractBinaryPacketCodec.java | 91 + .../binary/AbstractByteBufferPacketCodec.java | 317 ++ .../packet/binary/BinaryLEPacketCodec.java | 43 + .../comm/packet/binary/BinaryPacketCodec.java | 43 + .../packet/binary/ByteArrayFieldCodec.java | 55 + .../binary/ByteBufferEnumFieldCodec.java | 61 + .../packet/binary/ByteBufferPacketCodec.java | 85 + .../comm/packet/binary/NullTerminated.java | 56 + .../binary/NullTerminatedWrapperFactory.java | 59 + .../comm/packet/binary/ReverseByteOrder.java | 34 + .../ReverseByteOrderWrapperFactory.java | 66 + .../packet/binary/SequenceTerminated.java | 56 + .../binary/SequenceTerminatedFieldCodec.java | 107 + .../SequenceTerminatedWrapperFactory.java | 61 + .../comm/packet/codecs/ArrayFieldCodec.java | 95 + .../comm/packet/codecs/BooleanFieldCodec.java | 56 + .../comm/packet/codecs/ByteFieldCodec.java | 56 + .../packet/codecs/CharSequenceFieldCodec.java | 57 + .../packet/codecs/CharacterFieldCodec.java | 57 + .../packet/codecs/CollectionFieldCodec.java | 110 + .../comm/packet/codecs/DoubleFieldCodec.java | 56 + .../comm/packet/codecs/ElementCodec.java | 80 + .../comm/packet/codecs/FloatFieldCodec.java | 56 + .../comm/packet/codecs/IntegerFieldCodec.java | 56 + .../comm/packet/codecs/LongFieldCodec.java | 56 + .../packet/codecs/PacketCodecInternal.java | 268 ++ .../comm/packet/codecs/PacketFieldCodec.java | 58 + .../comm/packet/codecs/PrimitiveCodec.java | 297 ++ .../comm/packet/codecs/ShortFieldCodec.java | 56 + .../err/AnnotatedFieldOrderingException.java | 43 + .../packet/err/FieldOrderingException.java | 37 + .../err/InvalidFieldModifiersException.java | 37 + .../packet/err/InvalidFieldNameException.java | 41 + .../err/InvalidMethodNameException.java | 39 + .../packet/err/InvalidPacketException.java | 35 + .../packet/err/PacketAnnotationException.java | 43 + .../err/PacketDeclarationException.java | 38 + .../packet/err/PacketDecodeException.java | 29 + .../packet/err/PacketEncodeException.java | 32 + .../err/PacketFieldDeclarationException.java | 52 + .../PacketFieldValueMismatchException.java | 44 + .../packet/fields/AbstractFieldCodec.java | 115 + .../fields/AbstractFieldCodecFactory.java | 54 + .../packet/fields/AbstractWrapperFactory.java | 27 + .../ghidra/comm/packet/fields/FieldCodec.java | 67 + .../comm/packet/fields/FieldCodecFactory.java | 106 + .../comm/packet/fields/ImplementedBy.java | 31 + .../comm/packet/fields/InjectionFactory.java | 29 + .../comm/packet/fields/PacketField.java | 75 + .../comm/packet/fields/PrimitiveFactory.java | 44 + .../comm/packet/fields/WrapperFactory.java | 55 + .../packet/string/CharArrayFieldCodec.java | 54 + .../comm/packet/string/RegexSeparated.java | 78 + .../string/RegexSeparatedWrapperFactory.java | 102 + .../comm/packet/string/RegexTerminated.java | 73 + .../string/RegexTerminatedFieldCodec.java | 102 + .../string/RegexTerminatedWrapperFactory.java | 61 + .../comm/packet/string/SizeRestricted.java | 91 + .../string/SizeRestrictedFieldCodec.java | 104 + .../string/SizeRestrictedWrapperFactory.java | 55 + .../packet/string/StringEnumFieldCodec.java | 60 + .../comm/packet/string/StringPacketCodec.java | 723 +++++ .../ghidra/comm/packet/string/WithRadix.java | 45 + .../string/WithRadixByteFieldCodec.java | 64 + .../string/WithRadixDoubleFieldCodec.java | 64 + .../string/WithRadixFloatFieldCodec.java | 63 + .../string/WithRadixIntegerFieldCodec.java | 65 + .../string/WithRadixLongFieldCodec.java | 64 + .../string/WithRadixShortFieldCodec.java | 64 + .../string/WithRadixWrapperFactory.java | 87 + .../ghidra/comm/packet/string/WithSign.java | 36 + .../packet/string/WithSignFieldCodec.java | 70 + .../packet/string/WithSignWrapperFactory.java | 47 + .../service/AbstractAsyncClientHandler.java | 49 + .../comm/service/AbstractAsyncServer.java | 141 + .../java/ghidra/comm/util/BitmaskSet.java | 415 +++ .../ghidra/comm/util/BitmaskUniverse.java | 30 + .../ghidra/comm/util/ByteBufferUtils.java | 54 + .../comm/util/pyexport/GeneratePython.java | 367 +++ .../comm/util/pyexport/PythonClass.java | 191 ++ .../comm/util/pyexport/PythonPackage.java | 140 + .../src/main/py/ghidra/__init__.py | 0 .../src/main/py/ghidra/comm/__init__.py | 0 .../main/py/ghidra/comm/packet/__init__.py | 618 ++++ .../src/main/py/ghidra/comm/packet/annot.py | 503 +++ .../src/main/py/ghidra/comm/packet/binary.py | 163 + .../src/main/py/ghidra/comm/packet/string.py | 48 + .../src/main/py/ghidra/comm/util.py | 56 + .../src/main/py/java/__init__.py | 0 .../src/main/py/java/lang.py | 210 ++ .../src/main/py/java/util.py | 55 + .../comm/util/pyexport/__init__.py.template | 19 + .../comm/util/pyexport/setup.py.template | 16 + .../java/ghidra/async/AsyncLaziesTest.java | 109 + .../test/java/ghidra/async/AsyncLockTest.java | 315 ++ .../ghidra/async/AsyncPacketChannelTest.java | 291 ++ .../test/java/ghidra/async/AsyncRaceTest.java | 70 + .../java/ghidra/async/AsyncReferenceTest.java | 162 + .../java/ghidra/async/AsyncTestUtils.java | 35 + .../java/ghidra/async/AsyncTimerTest.java | 155 + .../java/ghidra/async/AsyncUtilsTest.java | 384 +++ .../packet/BinaryPacketDecodingTest.java | 311 ++ .../packet/BinaryPacketEncodingTest.java | 229 ++ .../packet/ByteBufferPacketDecodingTest.java | 305 ++ .../packet/ByteBufferPacketEncodingTest.java | 212 ++ .../comm/tests/packet/PacketCompTest.java | 159 + .../tests/packet/PacketFieldAnnotTest.java | 343 +++ .../comm/tests/packet/PacketTestClasses.java | 723 +++++ .../ghidra/comm/tests/packet/PutzTest.java | 318 ++ .../packet/StringPacketDecodingTest.java | 232 ++ .../packet/StringPacketEncodingTest.java | 170 + .../comm/tests/packet/package-info.java | 23 + .../packet/subpkg/PacketInSubpackage.java | 36 + .../java/ghidra/comm/util/BitmaskSetTest.java | 394 +++ .../java/ghidra/comm/util/CheckPackets.java | 74 + ...ses_TestMessageFullSpecColField_IntList.py | 12 + .../src/test/py/ghidra/comm/test_packet.py | 358 +++ .../Debug/Framework-Debugging/Module.manifest | 0 Ghidra/Debug/Framework-Debugging/build.gradle | 108 + .../certification.manifest | 6 + .../data/ExtensionPoint.manifest | 1 + .../src/expCloneExec/c/expCloneExec.c | 33 + .../src/expCloneExit/c/expCloneExit.c | 34 + .../src/expCloneSpin/c/expCloneSpin.c | 32 + .../src/expCreateProcess/c/expCreateProcess.c | 46 + .../c/expCreateThreadExit.c | 41 + .../c/expCreateThreadSpin.c | 39 + .../src/expFork/c/expFork.c | 30 + .../src/expPrint/c/expPrint.c | 29 + .../src/expSpin/c/expSpin.c | 22 + .../src/expTypes/c/expTypes.c | 111 + .../ghidra/dbg/DebugModelConventions.java | 788 +++++ .../DebuggerAbnormalModelClosedReason.java | 39 + .../ghidra/dbg/DebuggerModelClosedReason.java | 60 + .../java/ghidra/dbg/DebuggerModelFactory.java | 36 + .../ghidra/dbg/DebuggerModelListener.java | 53 + .../dbg/DebuggerNormalModelClosedReason.java | 35 + .../java/ghidra/dbg/DebuggerObjectModel.java | 463 +++ .../ghidra/dbg/DebuggerTargetObjectIface.java | 33 + .../ghidra/dbg/LocalDebuggerModelFactory.java | 44 + .../agent/AbstractDebuggerObjectModel.java | 34 + .../dbg/agent/AbstractTargetObject.java | 198 ++ .../java/ghidra/dbg/agent/AgentWindow.java | 104 + .../dbg/agent/DefaultTargetModelRoot.java | 28 + .../ghidra/dbg/agent/DefaultTargetObject.java | 447 +++ .../dbg/agent/DefaultTargetObjectRef.java | 58 + .../agent/InvalidatableTargetObjectIf.java | 62 + .../dbg/agent/SpiDebuggerObjectModel.java | 138 + .../dbg/attributes/TargetArrayDataType.java | 42 + .../attributes/TargetBitfieldDataType.java | 57 + .../ghidra/dbg/attributes/TargetDataType.java | 24 + .../attributes/TargetNamedDataTypeRef.java | 22 + .../dbg/attributes/TargetObjectRef.java | 542 ++++ .../dbg/attributes/TargetObjectRefList.java | 66 + .../dbg/attributes/TargetPointerDataType.java | 33 + .../attributes/TargetPrimitiveDataType.java | 55 + .../dbg/attributes/TargetStringList.java | 57 + .../dbg/attributes/TypedTargetObjectRef.java | 74 + .../attributes/TypedTargetObjectRefList.java | 30 + .../DebuggerIllegalArgumentException.java | 36 + .../error/DebuggerMemoryAccessException.java | 38 + .../error/DebuggerModelAccessException.java | 44 + .../DebuggerModelNoSuchPathException.java | 26 + .../dbg/error/DebuggerModelTypeException.java | 49 + .../DebuggerRegisterAccessException.java | 37 + .../dbg/error/DebuggerRuntimeException.java | 26 + .../dbg/error/DebuggerUserException.java | 25 + .../java/ghidra/dbg/memory/CachedMemory.java | 237 ++ .../java/ghidra/dbg/memory/MemoryReader.java | 34 + .../java/ghidra/dbg/memory/MemoryWriter.java | 34 + .../dbg/target/TargetAccessConditioned.java | 75 + .../ghidra/dbg/target/TargetAggregate.java | 28 + .../ghidra/dbg/target/TargetAttachable.java | 35 + .../ghidra/dbg/target/TargetAttacher.java | 122 + .../dbg/target/TargetBreakpointContainer.java | 115 + .../dbg/target/TargetBreakpointLocation.java | 85 + .../dbg/target/TargetBreakpointSpec.java | 186 ++ .../java/ghidra/dbg/target/TargetConsole.java | 79 + .../dbg/target/TargetDataTypeMember.java | 58 + .../dbg/target/TargetDataTypeNamespace.java | 56 + .../ghidra/dbg/target/TargetDeletable.java | 34 + .../ghidra/dbg/target/TargetDetachable.java | 39 + .../ghidra/dbg/target/TargetEnvironment.java | 120 + .../ghidra/dbg/target/TargetEventScope.java | 159 + .../dbg/target/TargetExecutionStateful.java | 166 + .../ghidra/dbg/target/TargetFocusScope.java | 86 + .../ghidra/dbg/target/TargetInterpreter.java | 108 + .../dbg/target/TargetInterruptible.java | 47 + .../ghidra/dbg/target/TargetKillable.java | 39 + .../ghidra/dbg/target/TargetLauncher.java | 144 + .../java/ghidra/dbg/target/TargetMemory.java | 135 + .../ghidra/dbg/target/TargetMemoryRegion.java | 72 + .../java/ghidra/dbg/target/TargetMethod.java | 320 ++ .../java/ghidra/dbg/target/TargetModule.java | 64 + .../dbg/target/TargetModuleContainer.java | 58 + .../dbg/target/TargetNamedDataType.java | 108 + .../java/ghidra/dbg/target/TargetObject.java | 717 +++++ .../java/ghidra/dbg/target/TargetProcess.java | 40 + .../ghidra/dbg/target/TargetRegister.java | 80 + .../ghidra/dbg/target/TargetRegisterBank.java | 221 ++ .../dbg/target/TargetRegisterContainer.java | 56 + .../ghidra/dbg/target/TargetResumable.java | 44 + .../java/ghidra/dbg/target/TargetSection.java | 86 + .../java/ghidra/dbg/target/TargetStack.java | 54 + .../ghidra/dbg/target/TargetStackFrame.java | 47 + .../ghidra/dbg/target/TargetSteppable.java | 254 ++ .../java/ghidra/dbg/target/TargetSymbol.java | 146 + .../dbg/target/TargetSymbolNamespace.java | 56 + .../java/ghidra/dbg/target/TargetThread.java | 38 + .../ghidra/dbg/target/TypedTargetObject.java | 29 + .../java/ghidra/dbg/util/CollectionUtils.java | 371 +++ .../ghidra/dbg/util/ConfigurableFactory.java | 164 + .../java/ghidra/dbg/util/ConversionUtils.java | 71 + .../main/java/ghidra/dbg/util/HandlerMap.java | 94 + .../java/ghidra/dbg/util/PathMatcher.java | 54 + .../java/ghidra/dbg/util/PathPattern.java | 103 + .../java/ghidra/dbg/util/PathPredicates.java | 52 + .../main/java/ghidra/dbg/util/PathUtils.java | 563 ++++ .../main/java/ghidra/dbg/util/PrefixMap.java | 65 + .../main/java/ghidra/dbg/util/ShellUtils.java | 141 + .../dbg/util/TargetDataTypeConverter.java | 519 ++++ .../main/java/ghidra/dbg/util/ValueUtils.java | 89 + .../ghidra/dbg/DebugModelConventionsTest.java | 127 + .../agent/DefaultDebuggerObjectModelTest.java | 235 ++ .../ghidra/dbg/memory/CachedMemoryTest.java | 371 +++ .../model/AbstractTestTargetRegisterBank.java | 101 + .../dbg/model/DefaultTestTargetObject.java | 31 + .../dbg/model/TestDebuggerModelBuilder.java | 105 + .../dbg/model/TestDebuggerModelFactory.java | 64 + .../dbg/model/TestDebuggerObjectModel.java | 114 + .../model/TestLocalDebuggerModelFactory.java | 44 + .../dbg/model/TestMimickJavaLauncher.java | 57 + .../dbg/model/TestTargetBreakpoint.java | 85 + .../model/TestTargetBreakpointContainer.java | 68 + .../dbg/model/TestTargetDataTypeMember.java | 40 + .../model/TestTargetDataTypeNamespace.java | 37 + .../dbg/model/TestTargetEnvironment.java | 36 + .../dbg/model/TestTargetInterpreter.java | 103 + .../ghidra/dbg/model/TestTargetMemory.java | 85 + .../dbg/model/TestTargetMemoryRegion.java | 41 + .../ghidra/dbg/model/TestTargetModule.java | 51 + .../dbg/model/TestTargetModuleContainer.java | 36 + .../dbg/model/TestTargetNamedDataType.java | 36 + .../ghidra/dbg/model/TestTargetObject.java | 23 + .../ghidra/dbg/model/TestTargetProcess.java | 67 + .../dbg/model/TestTargetProcessContainer.java | 32 + .../ghidra/dbg/model/TestTargetRegister.java | 74 + .../model/TestTargetRegisterBankInFrame.java | 38 + .../model/TestTargetRegisterBankInThread.java | 40 + .../model/TestTargetRegisterContainer.java | 55 + .../ghidra/dbg/model/TestTargetSection.java | 38 + .../dbg/model/TestTargetSectionContainer.java | 34 + .../ghidra/dbg/model/TestTargetSession.java | 83 + .../ghidra/dbg/model/TestTargetStack.java | 59 + .../dbg/model/TestTargetStackFrame.java | 24 + .../TestTargetStackFrameHasRegisterBank.java | 58 + .../TestTargetStackFrameIsRegisterBank.java | 60 + .../ghidra/dbg/model/TestTargetSymbol.java | 40 + .../dbg/model/TestTargetSymbolNamespace.java | 38 + .../ghidra/dbg/model/TestTargetThread.java | 65 + .../dbg/model/TestTargetThreadContainer.java | 42 + .../dbg/model/TestTargetTypedefDataType.java | 29 + .../dbg/model/TestTargetTypedefDef.java | 24 + .../java/ghidra/dbg/testutil/DummyProc.java | 78 + .../dbg/util/AbstractInvocationListener.java | 36 + .../util/AllTargetObjectListenerAdapter.java | 102 + .../dbg/util/AttributesChangedListener.java | 45 + .../dbg/util/DebuggerModelTestUtils.java | 83 + .../dbg/util/ElementTrackingListener.java | 80 + .../dbg/util/ElementsChangedListener.java | 46 + .../ghidra/dbg/util/InvalidatedListener.java | 43 + .../java/ghidra/dbg/util/PathUtilsTest.java | 79 + .../util/StreamTokenizerExperimentsTest.java | 67 + .../Framework-TraceModeling/Module.manifest | 0 .../Framework-TraceModeling/build.gradle | 16 + .../certification.manifest | 5 + .../data/DomainObjectEventQueues.java | 111 + .../trace/BytesPcodeExecutorStateMixin.java | 26 + .../trace/TraceBytesPcodeExecutorState.java | 156 + .../TraceMemoryStatePcodeArithmetic.java | 50 + ...aceMemoryStatePcodeExecutorStatePiece.java | 143 + .../pcode/exec/trace/TraceSleighUtils.java | 151 + .../java/ghidra/trace/database/DBTrace.java | 715 +++++ .../DBTraceCacheForContainingQueries.java | 192 ++ .../DBTraceCacheForSequenceQueries.java | 193 ++ .../trace/database/DBTraceChangeSet.java | 151 + .../trace/database/DBTraceContentHandler.java | 334 ++ .../ghidra/trace/database/DBTraceManager.java | 29 + .../trace/database/DBTraceUserData.java | 53 + .../ghidra/trace/database/DBTraceUtils.java | 480 +++ .../database/bookmark/DBTraceBookmark.java | 185 ++ .../bookmark/DBTraceBookmarkManager.java | 356 +++ .../DBTraceBookmarkRegisterSpace.java | 46 + .../bookmark/DBTraceBookmarkSpace.java | 145 + .../bookmark/DBTraceBookmarkType.java | 124 + .../breakpoint/DBTraceBreakpoint.java | 373 +++ .../breakpoint/DBTraceBreakpointManager.java | 139 + .../breakpoint/DBTraceBreakpointSpace.java | 142 + .../DBTraceRegisterContextManager.java | 223 ++ .../DBTraceRegisterContextRegisterSpace.java | 49 + .../context/DBTraceRegisterContextSpace.java | 462 +++ .../data/DBTraceDataSettingsAdapter.java | 248 ++ .../data/DBTraceDataSettingsOperations.java | 173 ++ .../database/data/DBTraceDataTypeManager.java | 229 ++ .../language/DBTraceGuestLanguage.java | 250 ++ .../DBTraceGuestLanguageMappedMemory.java | 582 ++++ .../DBTraceGuestLanguageMappedRange.java | 159 + .../language/DBTraceLanguageManager.java | 204 ++ ...bstractBaseDBTraceCodeUnitsMemoryView.java | 288 ++ .../AbstractBaseDBTraceCodeUnitsView.java | 105 + .../AbstractBaseDBTraceDefinedUnitsView.java | 315 ++ .../AbstractComposedDBTraceCodeUnitsView.java | 156 + .../listing/AbstractDBTraceCodeUnit.java | 116 + .../listing/AbstractDBTraceDataComponent.java | 237 ++ .../AbstractSingleDBTraceCodeUnitsView.java | 24 + ...thUndefinedDBTraceCodeUnitsMemoryView.java | 84 + .../database/listing/DBTraceCodeManager.java | 520 ++++ .../listing/DBTraceCodeRegisterSpace.java | 107 + .../database/listing/DBTraceCodeSpace.java | 285 ++ .../listing/DBTraceCodeUnitAdapter.java | 443 +++ .../listing/DBTraceCodeUnitsMemoryView.java | 37 + .../listing/DBTraceCodeUnitsRegisterView.java | 31 + .../listing/DBTraceCodeUnitsView.java | 44 + .../listing/DBTraceCommentAdapter.java | 167 + .../trace/database/listing/DBTraceData.java | 252 ++ .../database/listing/DBTraceDataAdapter.java | 235 ++ .../DBTraceDataArrayElementComponent.java | 36 + .../DBTraceDataCompositeFieldComponent.java | 43 + .../listing/DBTraceDataMemoryView.java | 31 + .../listing/DBTraceDataRegisterView.java | 30 + .../database/listing/DBTraceDataView.java | 41 + .../listing/DBTraceDefinedDataAdapter.java | 241 ++ .../listing/DBTraceDefinedDataMemoryView.java | 58 + .../DBTraceDefinedDataRegisterView.java | 31 + .../listing/DBTraceDefinedDataView.java | 193 ++ .../DBTraceDefinedUnitsMemoryView.java | 42 + .../DBTraceDefinedUnitsRegisterView.java | 31 + .../listing/DBTraceDefinedUnitsView.java | 71 + .../database/listing/DBTraceInstruction.java | 698 +++++ .../DBTraceInstructionsMemoryView.java | 83 + .../DBTraceInstructionsRegisterView.java | 32 + .../listing/DBTraceInstructionsView.java | 383 +++ .../DBTraceUndefinedDataMemoryView.java | 31 + .../DBTraceUndefinedDataRegisterView.java | 31 + .../listing/DBTraceUndefinedDataView.java | 148 + .../listing/UndefinedDBTraceData.java | 258 ++ ...SnapRangePropertyMapOcclusionIterable.java | 144 + .../map/AbstractDBTracePropertyMap.java | 395 +++ .../DBTraceAddressSnapRangePropertyMap.java | 247 ++ ...essSnapRangePropertyMapAddressSetView.java | 274 ++ ...ropertyMapOcclusionIntoFutureIterable.java | 45 + ...ePropertyMapOcclusionIntoPastIterable.java | 45 + ...ressSnapRangePropertyMapRegisterSpace.java | 54 + ...TraceAddressSnapRangePropertyMapSpace.java | 203 ++ ...BTraceAddressSnapRangePropertyMapTree.java | 608 ++++ .../database/memory/DBTraceMemBuffer.java | 65 + .../memory/DBTraceMemoryBlockEntry.java | 208 ++ .../memory/DBTraceMemoryBufferEntry.java | 279 ++ .../database/memory/DBTraceMemoryManager.java | 332 ++ .../database/memory/DBTraceMemoryRegion.java | 328 ++ .../memory/DBTraceMemoryRegisterSpace.java | 68 + .../database/memory/DBTraceMemorySpace.java | 817 +++++ .../memory/DBTraceMemoryStateEntry.java | 70 + .../trace/database/module/DBTraceModule.java | 243 ++ .../database/module/DBTraceModuleManager.java | 255 ++ .../database/module/DBTraceModuleSpace.java | 184 ++ .../trace/database/module/DBTraceSection.java | 162 + .../database/module/DBTraceStaticMapping.java | 246 ++ .../module/DBTraceStaticMappingManager.java | 177 ++ .../AbstractDBTraceProgramViewListing.java | 737 +++++ .../AbstractDBTraceProgramViewMemory.java | 627 ++++ ...actDBTraceProgramViewReferenceManager.java | 410 +++ .../database/program/DBTraceProgramView.java | 1580 ++++++++++ .../DBTraceProgramViewBookmarkManager.java | 377 +++ .../program/DBTraceProgramViewChangeSet.java | 215 ++ .../program/DBTraceProgramViewEquate.java | 118 + .../DBTraceProgramViewEquateTable.java | 163 + .../program/DBTraceProgramViewFragment.java | 247 ++ .../DBTraceProgramViewFunctionManager.java | 338 ++ .../program/DBTraceProgramViewListing.java | 79 + .../program/DBTraceProgramViewMemory.java | 103 + .../DBTraceProgramViewMemoryBlock.java | 323 ++ .../DBTraceProgramViewProgramContext.java | 208 ++ .../DBTraceProgramViewPropertyMapManager.java | 128 + .../DBTraceProgramViewReferenceManager.java | 35 + .../DBTraceProgramViewRegisterListing.java | 76 + .../DBTraceProgramViewRegisterMemory.java | 62 + ...DBTraceProgramViewRegisterMemoryBlock.java | 299 ++ .../program/DBTraceProgramViewRegisters.java | 628 ++++ ...eProgramViewRegistersReferenceManager.java | 43 + .../program/DBTraceProgramViewRootModule.java | 215 ++ .../DBTraceProgramViewSymbolTable.java | 578 ++++ .../DBTraceVariableSnapProgramView.java | 58 + .../DBTraceAddressPropertyManager.java | 279 ++ .../AbstractDBTraceSpaceBasedManager.java | 284 ++ .../space/DBTraceDelegatingManager.java | 245 ++ .../database/space/DBTraceSpaceBased.java | 61 + .../trace/database/space/DBTraceSpaceKey.java | 56 + .../trace/database/stack/DBTraceStack.java | 249 ++ .../database/stack/DBTraceStackFrame.java | 138 + .../database/stack/DBTraceStackManager.java | 146 + .../symbol/AbstractDBTraceSymbol.java | 570 ++++ .../AbstractDBTraceSymbolSingleTypeView.java | 98 + ...BTraceSymbolSingleTypeWithAddressView.java | 53 + ...TraceSymbolSingleTypeWithLocationView.java | 246 ++ .../symbol/AbstractDBTraceVariableSymbol.java | 396 +++ .../database/symbol/DBTraceClassSymbol.java | 40 + .../symbol/DBTraceClassSymbolView.java | 51 + .../trace/database/symbol/DBTraceEquate.java | 154 + .../database/symbol/DBTraceEquateManager.java | 210 ++ .../symbol/DBTraceEquateRegisterSpace.java | 50 + .../database/symbol/DBTraceEquateSpace.java | 221 ++ .../symbol/DBTraceFunctionStackFrame.java | 329 ++ .../symbol/DBTraceFunctionSymbol.java | 1997 ++++++++++++ .../symbol/DBTraceFunctionSymbolView.java | 299 ++ .../symbol/DBTraceGlobalVariableSymbol.java | 66 + .../DBTraceGlobalVariableSymbolView.java | 28 + .../database/symbol/DBTraceLabelSymbol.java | 275 ++ .../symbol/DBTraceLabelSymbolView.java | 64 + .../symbol/DBTraceLocalVariableSymbol.java | 123 + .../DBTraceLocalVariableSymbolView.java | 28 + .../symbol/DBTraceNamespaceSymbol.java | 155 + .../symbol/DBTraceNamespaceSymbolView.java | 60 + .../symbol/DBTraceOffsetReference.java | 36 + .../symbol/DBTraceParameterSymbol.java | 220 ++ .../symbol/DBTraceParameterSymbolView.java | 28 + .../symbol/DBTracePlaceholderSymbol.java | 52 + .../database/symbol/DBTraceReference.java | 210 ++ .../symbol/DBTraceReferenceManager.java | 293 ++ .../symbol/DBTraceReferenceRegisterSpace.java | 49 + .../symbol/DBTraceReferenceSpace.java | 649 ++++ .../symbol/DBTraceShiftedReference.java | 35 + .../DBTraceSnapSelectedReferenceSpace.java | 20 + .../symbol/DBTraceStackReference.java | 25 + .../database/symbol/DBTraceSymbolManager.java | 985 ++++++ ...ceSymbolMultipleTypesNoDuplicatesView.java | 35 + .../DBTraceSymbolMultipleTypesView.java | 76 + ...tipleTypesWithAddressNoDuplicatesView.java | 36 + ...aceSymbolMultipleTypesWithAddressView.java | 71 + ...ceSymbolMultipleTypesWithLocationView.java | 118 + .../trace/database/thread/DBTraceThread.java | 190 ++ .../database/thread/DBTraceThreadManager.java | 157 + .../trace/database/time/DBTraceSnapshot.java | 167 + .../database/time/DBTraceTimeManager.java | 116 + .../java/ghidra/trace/model/AddressSnap.java | 24 + .../trace/model/DefaultAddressSnap.java | 80 + .../trace/model/DefaultTraceLocation.java | 118 + .../ghidra/trace/model/DefaultTraceSnap.java | 88 + .../model/ImmutableTraceAddressSnapRange.java | 131 + .../main/java/ghidra/trace/model/Trace.java | 354 +++ .../trace/model/TraceAddressSnapRange.java | 65 + .../trace/model/TraceAddressSnapSpace.java | 83 + .../ghidra/trace/model/TraceChangeSet.java | 22 + .../model/TraceDomainObjectListener.java | 199 ++ .../ghidra/trace/model/TraceLocation.java | 31 + .../trace/model/TraceOptionsManager.java | 48 + .../java/ghidra/trace/model/TraceSnap.java | 22 + .../ghidra/trace/model/TraceUserData.java | 22 + .../trace/model/bookmark/TraceBookmark.java | 42 + .../model/bookmark/TraceBookmarkManager.java | 64 + .../bookmark/TraceBookmarkOperations.java | 60 + .../bookmark/TraceBookmarkRegisterSpace.java | 42 + .../model/bookmark/TraceBookmarkSpace.java | 25 + .../model/bookmark/TraceBookmarkType.java | 37 + .../model/breakpoint/TraceBreakpoint.java | 207 ++ .../model/breakpoint/TraceBreakpointKind.java | 110 + .../breakpoint/TraceBreakpointManager.java | 129 + .../context/TraceRegisterContextManager.java | 27 + .../TraceRegisterContextOperations.java | 60 + .../TraceRegisterContextRegisterSpace.java | 22 + .../context/TraceRegisterContextSpace.java | 22 + .../model/data/TraceBasedDataTypeManager.java | 81 + .../model/language/TraceGuestLanguage.java | 71 + .../TraceGuestLanguageMappedRange.java | 38 + .../model/language/TraceLanguageManager.java | 31 + .../TraceBaseCodeUnitsRegisterView.java | 113 + .../model/listing/TraceBaseCodeUnitsView.java | 233 ++ .../listing/TraceBaseDefinedRegisterView.java | 48 + .../listing/TraceBaseDefinedUnitsView.java | 48 + .../trace/model/listing/TraceCodeManager.java | 51 + .../model/listing/TraceCodeOperations.java | 66 + .../model/listing/TraceCodeRegisterSpace.java | 40 + .../trace/model/listing/TraceCodeSpace.java | 35 + .../trace/model/listing/TraceCodeUnit.java | 173 ++ .../listing/TraceCodeUnitsRegisterView.java | 20 + .../model/listing/TraceCodeUnitsView.java | 20 + .../ghidra/trace/model/listing/TraceData.java | 36 + .../model/listing/TraceDataRegisterView.java | 20 + .../trace/model/listing/TraceDataView.java | 20 + .../listing/TraceDefinedDataRegisterView.java | 32 + .../model/listing/TraceDefinedDataView.java | 30 + .../TraceDefinedUnitsRegisterView.java | 20 + .../model/listing/TraceDefinedUnitsView.java | 21 + .../trace/model/listing/TraceInstruction.java | 75 + .../TraceInstructionsRegisterView.java | 21 + .../model/listing/TraceInstructionsView.java | 41 + .../TraceUndefinedDataRegisterView.java | 20 + .../model/listing/TraceUndefinedDataView.java | 20 + .../map/TraceAddressSnapRangePropertyMap.java | 30 + ...AddressSnapRangePropertyMapOperations.java | 59 + ...ressSnapRangePropertyMapRegisterSpace.java | 23 + ...TraceAddressSnapRangePropertyMapSpace.java | 23 + .../trace/model/map/TracePropertyMap.java | 32 + .../ghidra/trace/model/map/UnsignedUtils.java | 36 + .../trace/model/memory/TraceMemoryFlag.java | 35 + .../model/memory/TraceMemoryManager.java | 101 + .../model/memory/TraceMemoryOperations.java | 433 +++ .../trace/model/memory/TraceMemoryRegion.java | 323 ++ .../memory/TraceMemoryRegisterSpace.java | 114 + .../trace/model/memory/TraceMemorySpace.java | 50 + .../memory/TraceMemorySpaceInputStream.java | 109 + .../trace/model/memory/TraceMemoryState.java | 31 + .../TraceOverlappedRegionException.java | 33 + .../TraceConflictedMappingException.java | 29 + .../trace/model/modules/TraceModule.java | 240 ++ .../model/modules/TraceModuleManager.java | 110 + .../model/modules/TraceModuleOperations.java | 95 + .../trace/model/modules/TraceModuleSpace.java | 22 + .../trace/model/modules/TraceSection.java | 101 + .../model/modules/TraceStaticMapping.java | 129 + .../modules/TraceStaticMappingManager.java | 108 + .../model/program/TickSpecificTraceView.java | 34 + .../trace/model/program/TraceProgramView.java | 55 + .../TraceProgramViewBookmarkManager.java | 23 + .../program/TraceProgramViewListing.java | 22 + .../model/program/TraceProgramViewMemory.java | 23 + .../TraceProgramViewRegisterListing.java | 22 + .../program/TraceVariableSnapProgramView.java | 32 + .../property/TraceAddressPropertyManager.java | 105 + .../ghidra/trace/model/stack/TraceStack.java | 88 + .../trace/model/stack/TraceStackFrame.java | 32 + .../trace/model/stack/TraceStackManager.java | 27 + .../trace/model/symbol/TraceClassSymbol.java | 22 + .../model/symbol/TraceClassSymbolView.java | 26 + .../trace/model/symbol/TraceEquate.java | 68 + .../model/symbol/TraceEquateManager.java | 61 + .../model/symbol/TraceEquateOperations.java | 40 + .../model/symbol/TraceEquateReference.java | 32 + .../symbol/TraceEquateRegisterSpace.java | 22 + .../trace/model/symbol/TraceEquateSpace.java | 22 + .../model/symbol/TraceFunctionSymbol.java | 81 + .../model/symbol/TraceFunctionSymbolView.java | 49 + .../symbol/TraceGlobalVariableSymbol.java | 21 + .../symbol/TraceGlobalVariableSymbolView.java | 21 + .../trace/model/symbol/TraceLabelSymbol.java | 28 + .../model/symbol/TraceLabelSymbolView.java | 35 + .../symbol/TraceLocalVariableSymbol.java | 22 + .../symbol/TraceLocalVariableSymbolView.java | 21 + .../model/symbol/TraceNamespaceSymbol.java | 47 + .../symbol/TraceNamespaceSymbolView.java | 27 + .../model/symbol/TraceOffsetReference.java | 25 + .../model/symbol/TraceParameterSymbol.java | 22 + .../symbol/TraceParameterSymbolView.java | 21 + .../trace/model/symbol/TraceReference.java | 120 + .../model/symbol/TraceReferenceManager.java | 30 + .../symbol/TraceReferenceOperations.java | 111 + .../symbol/TraceReferenceRegisterSpace.java | 22 + .../model/symbol/TraceReferenceSpace.java | 22 + .../model/symbol/TraceShiftedReference.java | 25 + .../model/symbol/TraceStackReference.java | 30 + .../trace/model/symbol/TraceSymbol.java | 71 + .../model/symbol/TraceSymbolManager.java | 88 + .../symbol/TraceSymbolNoDuplicatesView.java | 34 + .../trace/model/symbol/TraceSymbolView.java | 58 + ...raceSymbolWithAddressNoDuplicatesView.java | 21 + .../symbol/TraceSymbolWithAddressView.java | 43 + .../model/symbol/TraceSymbolWithLifespan.java | 28 + .../symbol/TraceSymbolWithLocationView.java | 67 + .../model/symbol/TraceVariableSymbol.java | 23 + .../trace/model/thread/TraceThread.java | 149 + .../model/thread/TraceThreadManager.java | 115 + .../trace/model/time/TraceSnapshot.java | 109 + .../trace/model/time/TraceTimeManager.java | 59 + .../ghidra/trace/util/ByteArrayUtils.java | 59 + .../trace/util/DataAdapterFromDataType.java | 110 + .../trace/util/DefaultTraceChangeType.java | 118 + .../trace/util/EmptyFunctionIterator.java | 40 + .../trace/util/EnumeratingIterator.java | 61 + .../util/InstructionAdapterFromPrototype.java | 191 ++ .../ghidra/trace/util/MemBufferAdapter.java | 83 + .../ghidra/trace/util/MethodProtector.java | 44 + .../trace/util/OverlappingObjectIterator.java | 159 + .../ghidra/trace/util/TraceAddressSpace.java | 27 + .../ghidra/trace/util/TraceChangeManager.java | 20 + .../ghidra/trace/util/TraceChangeRecord.java | 90 + .../ghidra/trace/util/TraceChangeType.java | 22 + .../ghidra/trace/util/TraceRegisterUtils.java | 163 + .../test/java/experiments/ToArrayTest.java | 82 + .../exec/trace/TraceSleighUtilsTest.java | 236 ++ .../trace/database/ToyDBTraceBuilder.java | 294 ++ .../bookmark/DBTraceBookmarkManagerTest.java | 203 ++ .../DBTraceBreakpointManagerTest.java | 131 + .../DBTraceRegisterContextManagerTest.java | 31 + .../data/DBTraceDataTypeManagerTest.java | 256 ++ .../language/DBTraceLanguageManagerTest.java | 354 +++ .../listing/DBTraceCodeManagerTest.java | 1831 +++++++++++ .../database/listing/DBTraceCodeUnitTest.java | 1840 +++++++++++ ...napRangePropertyMapAddressSetViewTest.java | 523 ++++ ...rtyMapOcclusionIntoFutureIterableTest.java | 283 ++ ...pertyMapOcclusionIntoPastIterableTest.java | 283 ++ ...eAddressSnapRangePropertyMapSpaceTest.java | 418 +++ .../AbstractDBTraceMemoryManagerTest.java | 970 ++++++ .../memory/DBTraceMemoryManagerBETest.java | 25 + .../memory/DBTraceMemoryManagerLETest.java | 25 + .../module/DBTraceModuleManagerTest.java | 378 +++ .../DBTraceStaticMappingManagerTest.java | 200 ++ .../DBTraceDisassemblerIntegrationTest.java | 218 ++ ...DBTraceProgramViewFunctionManagerTest.java | 448 +++ .../DBTraceProgramViewListingTest.java | 809 +++++ .../stack/DBTraceStackManagerTest.java | 292 ++ .../symbol/DBTraceFunctionSymbolTest.java | 1917 ++++++++++++ .../symbol/DBTraceReferenceManagerTest.java | 558 ++++ .../symbol/DBTraceSymbolManagerTest.java | 685 +++++ .../thread/DBTraceThreadManagerTest.java | 70 + .../util/OverlappingObjectIteratorTest.java | 165 + Ghidra/Debug/ProposedUtils/Module.manifest | 0 Ghidra/Debug/ProposedUtils/build.gradle | 24 + .../ProposedUtils/certification.manifest | 5 + .../ExpanderArrowExpansionListener.java | 27 + .../ExpanderArrowExpansionVetoException.java | 20 + .../docking/widgets/ExpanderArrowPanel.java | 155 + .../docking/widgets/HorizontalTabPanel.java | 202 ++ .../docking/widgets/RangeCursorPanel.java | 227 ++ .../widgets/RangeCursorValueListener.java | 20 + .../widgets/table/CellEditorUtils.java | 46 + .../table/CustomToStringCellRenderer.java | 88 + ...aultEnumeratedColumnProgramTableModel.java | 96 + .../DefaultEnumeratedColumnTableModel.java | 298 ++ .../EnumeratedColumnProgramTableModel.java | 24 + .../table/EnumeratedColumnTableModel.java | 43 + .../table/HexBigIntegerTableCellEditor.java | 63 + .../table/HexBigIntegerTableCellRenderer.java | 49 + .../table/IconButtonTableCellEditor.java | 62 + .../table/IconButtonTableCellRenderer.java | 45 + .../RowWrappedEnumeratedColumnTableModel.java | 75 + .../widgets/timeline/TimelinePanel.java | 832 +++++ .../timeline/TimelineViewRangeListener.java | 22 + .../tree/AnyChangeTreeModelListener.java | 43 + .../tree/SearchableByObjectGTreeNode.java | 46 + .../generic/AbstractUnionedCollection.java | 95 + .../java/generic/CatenatedCollection.java | 50 + .../java/generic/ComparableTupleRecord.java | 51 + .../src/main/java/generic/ID.java | 47 + .../src/main/java/generic/NestedIterator.java | 86 + .../src/main/java/generic/TupleRecord.java | 51 + .../src/main/java/generic/Unique.java | 64 + .../generic/depends/DependentService.java | 28 + .../depends/DependentServiceConstructor.java | 57 + .../depends/DependentServiceResolver.java | 153 + .../err/ServiceConstructionException.java | 29 + .../err/UnsatisfiedFieldsException.java | 32 + .../err/UnsatisfiedParameterException.java | 33 + .../table/DataTypeTableCellEditor.java | 235 ++ .../framework/data/DBDomainObjectSupport.java | 91 + .../framework/data/OpenedDomainFile.java | 57 + .../ghidra/framework/options/AutoOptions.java | 223 ++ .../options/AutoOptionsListener.java | 335 ++ .../annotation/AutoOptionConsumed.java | 26 + .../options/annotation/AutoOptionDefined.java | 37 + .../options/annotation/HelpInfo.java | 26 + .../framework/plugintool/AutoConfigState.java | 493 +++ .../framework/plugintool/AutoService.java | 88 + .../framework/plugintool/PluginToolUtils.java | 97 + .../annotation/AutoConfigStateField.java | 33 + .../annotation/AutoServiceConsumed.java | 24 + .../annotation/AutoServiceProvided.java | 24 + .../plugintool/util/AutoServiceListener.java | 227 ++ .../util/datastruct/DynamicSortedTreeSet.java | 325 ++ .../util/datastruct/SemisparseByteArray.java | 261 ++ .../ghidra/graph/algo/TopologicalSorter.java | 141 + .../java/ghidra/lifecycle/Experimental.java | 30 + .../main/java/ghidra/lifecycle/Internal.java | 37 + .../java/ghidra/lifecycle/Unfinished.java | 56 + .../AbstractLongOffsetPcodeExecutorState.java | 28 + ...ractLongOffsetPcodeExecutorStatePiece.java | 98 + ...ctOffsetTransformedPcodeExecutorState.java | 52 + .../pcode/exec/AddressOfPcodeArithmetic.java | 46 + .../exec/AddressOfPcodeExecutorState.java | 58 + .../exec/AnnotatedSleighUseropLibrary.java | 132 + .../pcode/exec/BigIntegerPcodeArithmetic.java | 46 + .../pcode/exec/BytesPcodeArithmetic.java | 86 + .../pcode/exec/PairedPcodeArithmetic.java | 71 + .../pcode/exec/PairedPcodeExecutorState.java | 44 + .../exec/PairedPcodeExecutorStatePiece.java | 54 + .../ghidra/pcode/exec/PcodeArithmetic.java | 35 + .../java/ghidra/pcode/exec/PcodeExecutor.java | 204 ++ .../ghidra/pcode/exec/PcodeExecutorState.java | 25 + .../pcode/exec/PcodeExecutorStatePiece.java | 48 + .../java/ghidra/pcode/exec/PcodeFrame.java | 65 + .../ghidra/pcode/exec/SleighExpression.java | 59 + .../pcode/exec/SleighLinkException.java | 22 + .../java/ghidra/pcode/exec/SleighProgram.java | 66 + .../pcode/exec/SleighProgramCompiler.java | 140 + .../pcode/exec/SleighUseropLibrary.java | 78 + .../model/address/CachedAddressSetView.java | 343 +++ .../ghidra/util/AbstractAddressSetView.java | 195 ++ .../ghidra/util/AbstractPeekableIterator.java | 56 + .../ghidra/util/AddressIteratorAdapter.java | 114 + .../ghidra/util/AddressRangeComparators.java | 35 + .../ghidra/util/AddressRangeIterators.java | 101 + .../java/ghidra/util/AnnotatedSaveable.java | 281 ++ .../main/java/ghidra/util/ComparatorMath.java | 38 + .../ghidra/util/DifferenceAddressSetView.java | 100 + .../main/java/ghidra/util/GhidraLockHold.java | 35 + .../util/IntersectionAddressSetView.java | 128 + .../java/ghidra/util/LanguageUtilities.java | 41 + .../src/main/java/ghidra/util/LockHold.java | 38 + .../ghidra/util/MergeSortingIterator.java | 155 + .../ghidra/util/PairingIteratorMerger.java | 77 + .../java/ghidra/util/PeekableIterators.java | 31 + .../SymmetricDifferenceAddressSetView.java | 107 + .../src/main/java/ghidra/util/TimedMsg.java | 40 + .../TwoWayBreakdownAddressRangeIterator.java | 203 ++ .../java/ghidra/util/UIManagerWrapper.java | 60 + .../util/UnionAddressRangeIterator.java | 103 + .../java/ghidra/util/UnionAddressSetView.java | 113 + .../AbstractDirectedLongKeyIterator.java | 33 + .../AbstractDirectedRecordIterator.java | 33 + .../database/BackwardLongKeyIterator.java | 36 + .../util/database/BackwardRecordIterator.java | 37 + .../util/database/DBAnnotatedObject.java | 181 ++ .../database/DBAnnotatedObjectFactory.java | 22 + .../util/database/DBBufferInputStream.java | 122 + .../util/database/DBBufferOutputStream.java | 74 + .../database/DBCachedDomainObjectAdapter.java | 163 + .../util/database/DBCachedObjectIndex.java | 320 ++ .../util/database/DBCachedObjectStore.java | 1069 +++++++ .../database/DBCachedObjectStoreEntrySet.java | 211 ++ .../DBCachedObjectStoreEntrySubSet.java | 163 + .../database/DBCachedObjectStoreFactory.java | 644 ++++ ...edObjectStoreFoundKeysValueCollection.java | 165 + .../database/DBCachedObjectStoreKeySet.java | 205 ++ .../DBCachedObjectStoreKeySubSet.java | 154 + .../util/database/DBCachedObjectStoreMap.java | 250 ++ .../database/DBCachedObjectStoreSubMap.java | 209 ++ .../DBCachedObjectStoreValueCollection.java | 104 + ...DBCachedObjectStoreValueSubCollection.java | 92 + .../ghidra/util/database/DBObjectColumn.java | 41 + .../java/ghidra/util/database/DBOpenMode.java | 35 + .../ghidra/util/database/DBTransaction.java | 56 + .../util/database/DirectedIterator.java | 78 + .../database/DirectedLongKeyIterator.java | 34 + .../util/database/DirectedRecordIterator.java | 130 + .../util/database/ForwardLongKeyIterator.java | 36 + .../util/database/ForwardRecordIterator.java | 37 + .../ghidra/util/database/SchemaBuilder.java | 62 + .../util/database/UndoableTransaction.java | 118 + .../database/annot/DBAnnotatedColumn.java | 24 + .../util/database/annot/DBAnnotatedField.java | 39 + .../util/database/annot/DBAnnotatedIndex.java | 25 + .../database/annot/DBAnnotatedObjectInfo.java | 24 + .../database/err/NoDefaultCodecException.java | 31 + .../spatial/AbstractConstraintsTree.java | 949 ++++++ .../AbstractConstraintsTreeSpatialMap.java | 441 +++ .../spatial/AbstractRStarConstraintsTree.java | 551 ++++ .../util/database/spatial/BoundedShape.java | 22 + .../util/database/spatial/BoundingShape.java | 45 + .../database/spatial/DBTreeDataRecord.java | 133 + .../database/spatial/DBTreeNodeRecord.java | 89 + .../util/database/spatial/DBTreeRecord.java | 46 + .../ghidra/util/database/spatial/Query.java | 77 + .../util/database/spatial/SpatialMap.java | 209 ++ .../spatial/rect/Abstract2DRStarTree.java | 87 + .../rect/AbstractRectangle2DQuery.java | 207 ++ .../spatial/rect/EuclideanSpace2D.java | 48 + .../spatial/rect/ImmutablePoint2D.java | 48 + .../spatial/rect/ImmutableRectangle2D.java | 80 + .../util/database/spatial/rect/Point2D.java | 30 + .../database/spatial/rect/Rectangle2D.java | 202 ++ .../spatial/rect/Rectangle2DDirection.java | 63 + .../datastruct/CollectionChangeListener.java | 32 + .../DefaultObservableCollection.java | 254 ++ .../ghidra/util/datastruct/ListenerMap.java | 227 ++ .../ghidra/util/datastruct/ListenerSet.java | 93 + .../util/datastruct/ObservableCollection.java | 35 + .../layout/ProportionalHorizontalLayout.java | 124 + .../java/utilities/util/ProxyUtilities.java | 119 + .../utilities/util/SuppressableCallback.java | 217 ++ .../widgets/timeline/TimelinePanelTest.java | 209 ++ .../test/java/generic/NestedIteratorTest.java | 131 + .../depends/DependentServiceResolverTest.java | 270 ++ .../framework/options/AutoOptionsTest.java | 535 ++++ .../framework/plugintool/AutoServiceTest.java | 164 + .../datastruct/DynamicSortedTreeSetTest.java | 203 ++ .../datastruct/SemisparseByteArrayTest.java | 106 + .../ghidra/util/AnnotatedSaveableTest.java | 321 ++ .../util/DifferenceAddressSetViewTest.java | 346 +++ .../util/IntersectionAddressSetViewTest.java | 342 +++ .../ghidra/util/MergeSortingIteratorTest.java | 103 + ...SymmetricDifferenceAddressSetViewTest.java | 360 +++ ...oWayBreakdownAddressRangeIteratorTest.java | 341 ++ .../ghidra/util/UnionAddressSetViewTest.java | 362 +++ .../DBCachedObjectStoreFactoryTest.java | 791 +++++ .../database/DBCachedObjectStoreTest.java | 2731 +++++++++++++++++ .../database/spatial/RStarTreeMapTest.java | 1135 +++++++ .../util/datastruct/ListenerMapTest.java | 113 + .../util/datastruct/ListenerSetTest.java | 100 + .../datastruct/ObservableCollectionTest.java | 514 ++++ .../util/deeper/ProxyUtilitiesTest.java | 92 + ...AbstractSymbolTreePluginExternalsTest.java | 43 +- .../Framework/SoftwareModeling/Sleigh.launch | 27 - certification.local.manifest | 1 + gradle/root/eclipse.gradle | 8 + gradle/support/ip.gradle | 3 +- settings.gradle | 1 + 2705 files changed, 305722 insertions(+), 53 deletions(-) create mode 100644 DebuggerDevGuide.md create mode 100644 Ghidra/Debug/AnnotationValidator/Module.manifest create mode 100644 Ghidra/Debug/AnnotationValidator/build.gradle create mode 100644 Ghidra/Debug/AnnotationValidator/certification.manifest create mode 100644 Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/AbstractDBAnnotationValidator.java create mode 100644 Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/AccessSpec.java create mode 100644 Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedColumnValidator.java create mode 100644 Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedFieldValidator.java create mode 100644 Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedObjectProcessor.java create mode 100644 Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedObjectValidator.java create mode 100644 Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/ValidationContext.java create mode 100644 Ghidra/Debug/AnnotationValidator/src/main/resources/META-INF/services/javax.annotation.processing.Processor create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/Module.manifest create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/build.gradle create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/certification.manifest create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/cpp/javaprovider.cpp create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/def/javaprovider.def create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/headers/afxres.h create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/headers/resource.h create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/rc/javaprovider.rc create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/DbgEngInJvmDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/COMUtilsExtra.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DbgEng.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugAdvanced.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugBreakpoint.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClient.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClientReentrant.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControlReentrant.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugDataSpaces.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugEventCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugEventInformation.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugExceptionRecord64.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugInputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugModule.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugModuleInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugOutputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugProcessId.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugProcessInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugRegisters.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugRunningProcess.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugServerId.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSessionId.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugStackInformation.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolEntry.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolId.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolName.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbols.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSystemObjects.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugThreadId.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugThreadInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugValue.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/err/DbgEngException.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/err/DbgEngRuntimeException.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/ext/JavaProvider.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/util/DebugEventCallbacksAdapter.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngGadpServer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngLocalDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/AbstractClientThreadExecutor.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/DbgEngClientThreadExecutor.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/DbgEngGadpServerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/DbgEngUtil.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/DebugRunningProcessImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl5.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl6.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl7.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl5.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl6.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl7.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/event/WrapCallbackIDebugEventCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/event/WrapCallbackIDebugEventCallbacksWide.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugInputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugOutputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugOutputCallbacksWide.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugModuleImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl5.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/DbgEngNative.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/Kernel32Extra.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/ToolhelpUtil.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/UnknownWithUtils.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/WinNTExtra.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient5.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient6.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient7.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient5.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient6.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient7.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl5.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl6.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl7.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl5.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl6.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl7.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventCallbacksWide.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventContextCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventCallbacksWide.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventContextCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventCallbacksWide.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventContextCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/MarkerEventCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventCallbacksWide.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventContextCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugInputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacks2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacksWide.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugInputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacks2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacksWide.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugInputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacks2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacksWide.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/MarkerInputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/MarkerOutputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugInputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacks2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacksWide.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/IDebugRegisters.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/IDebugRegisters2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/WrapIDebugRegisters.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/WrapIDebugRegisters2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbolGroup.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbolGroup2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols5.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols5.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects3.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects4.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/javaprovider/JavaProviderNative.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgCause.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventHandler.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgManager.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgMemoryOperations.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModule.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModuleMemory.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModuleSection.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgProcess.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgReason.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgSession.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStackFrame.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStackFrameOperations.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgState.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStateListener.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgThread.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointDisp.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointInsertions.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointLocation.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointType.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/AbstractDbgCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAddProcessCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAddSessionCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAttachCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAttachKernelCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgCommandError.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgConsoleExecCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgContinueCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDeleteBreakpointsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDetachCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDisableBreakpointsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgEnableBreakpointsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgEvaluateCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgFileExecAndSymbolsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgInsertBreakpointCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgKillCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgLaunchProcessCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListAvailableProcessesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListBreakpointsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListKernelMemoryRegionsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListMappingsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListMemoryRegionsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListModulesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListProcessesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListRegisterDescriptionsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListSessionsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListThreadsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgOpenDumpCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgPendingCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgProcessSelectCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadBusDataCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadControlCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadDebuggerDataCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadIoCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadPhysicalMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadRegistersCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRemoveProcessCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRemoveSessionCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRunCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSessionSelectCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgStackListFramesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgStepCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgThreadSelectCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteBusDataCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteControlCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteIoCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWritePhysicalMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteRegistersCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/AbstractDbgCompletedCommandEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/AbstractDbgEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointCreatedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointDeletedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointModifiedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandDoneEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandErrorEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandRunningEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgConsoleOutputEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgExceptionEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgModuleLoadedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgModuleUnloadedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessCreatedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessExitedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessSelectedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgRunningEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSessionSelectedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStateChangedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStoppedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSystemsEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadCreatedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadExitedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadSelectedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugEventCallbacksAdapter.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugOutputCallbacks.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgMinimalSymbol.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgModuleImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgModuleMemoryImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgProcessImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgRegister.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgRegisterSet.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgSectionImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgSessionImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgStackFrameImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgThreadImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgEndSteppingRangeReason.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgExitNormallyReason.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgExitedReason.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgSignalReceivedReason.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/AbstractDbgModel.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelSelectableObject.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAccessConditioned.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAttachable.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAttacher.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetBptHelper.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetDeletable.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetDetachable.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetEnvironment.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetEventScope.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetExecutionStateful.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetFocusScope.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterpreter.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterruptible.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetKillable.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetLauncher.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetMethod.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetResumable.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetSteppable.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetAvailable.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetAvailableContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointLocation.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointSpec.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetConnector.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetConnectorContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetDebugContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEnvironmentEx.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetMemoryContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetMemoryRegion.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModule.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleSection.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleSectionContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetObject.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetProcess.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetProcessContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegister.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegisterBank.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegisterContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRoot.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSession.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionAttributes.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionAttributesMachine.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetStack.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetStackFrame.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSymbol.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSymbolContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetTTD.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetThread.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetThreadContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelDefaultTargetModelRoot.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImplUtils.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetAvailableContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetAvailableImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetBreakpointContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetBreakpointSpecImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetConnectorContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetDebugContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetKernelConnectorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetMemoryContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetMemoryRegionImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleSectionContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleSectionImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetObjectImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessAttachConnectorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessLaunchConnectorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRegisterContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRegisterImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRootImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionAttributesImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionAttributesMachineImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetStackFrameImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetStackImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSymbolContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSymbolImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetThreadContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetThreadImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetTraceOrDumpConnectorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/sctldbg/cpp/sctldbg.cpp create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/DummyProc.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTest.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTestOld.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/AbstractDbgManagerTest.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/SpawnedDbgManagerTest.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/AbstractModelForDbgTest.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/GadpForDbgTest.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/ModelForDbgTest.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/testutil/DummyProc.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/Module.manifest create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/build.gradle create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/certification.manifest create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/ghidra_scripts/PopulateTraceLocal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/ghidra_scripts/PopulateTraceRemote.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/Module.manifest create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/certification.manifest create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/cpp/javaprovider.cpp create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/def/javaprovider.def create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/headers/afxres.h create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/headers/resource.h create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/rc/javaprovider.rc create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/DbgModelInJvmDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/COMUtilsExtra.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/DbgModel.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/UnknownEx.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/bridge/HostDataModelAccess.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/ComparableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/Concept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DataModelConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DynamicConceptProviderConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DynamicKeyProviderConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/EquatableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/IndexableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/IterableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/PreferredRuntimeTypeConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/StringDisplayableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/DataModelManager1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/DataModelManager2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelNameBinder.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScript.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptClient.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptHostContext.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptManager.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptProvider.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptProviderEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptTemplate.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptTemplateEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebug1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebug2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpoint.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugClient.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugStack.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrame.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHost.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostBase.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostBaseClass.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostConstant.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostContext.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostData.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostErrorSink.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostEvaluator1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostEvaluator2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostExtensability.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostField.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostMemory1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostMemory2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModule1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModule2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModuleSignature.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostPublic.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostScriptHost.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostStatus.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbol1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbol2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbolEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbols.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostType1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostType2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostTypeSignature.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/err/DbgModelException.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/err/DbgModelRuntimeException.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/KeyEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/KeyStore.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelIterator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelKeyReference1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelKeyReference2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelMethod.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelObject.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelPropertyAccessor.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/RawEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelGadpServer.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelLocalDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/DbgModelClientThreadExecutor.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/DbgModelGadpServerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/WrappedDbgModel.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/DbgModelUtil.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/DebugRunningProcessImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/UnknownExImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/UnknownExInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HDMAUtil.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HostDataModelAccessImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HostDataModelAccessInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/ComparableConceptImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/ComparableConceptInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DataModelConceptImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DataModelConceptInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicConceptProviderConceptImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicConceptProviderConceptInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicKeyProviderConceptImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicKeyProviderConceptInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/EquatableConceptImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/EquatableConceptInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IndexableConceptImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IndexableConceptInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IterableConceptImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IterableConceptInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/PreferredRuntimeTypeConceptImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/PreferredRuntimeTypeConceptInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/StringDisplayableConceptImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/StringDisplayableConceptInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelNameBinderImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelNameBinderInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptClientImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptClientInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptHostContextImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptHostContextInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptManagerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptManagerInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderEnumeratorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderEnumeratorInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateEnumeratorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateEnumeratorInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumeratorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumeratorInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugClientImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugClientInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrameImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrameInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumeratorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumeratorInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostBaseClassImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostBaseClassInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostConstantImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostConstantInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostContextImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostContextInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostDataImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostDataInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostErrorSinkImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostErrorSinkInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostExtensabilityImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostExtensabilityInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostFieldImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostFieldInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleSignatureImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleSignatureInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostPublicImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostPublicInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostScriptHostImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostScriptHostInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostStatusImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostStatusInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolEnumeratorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolEnumeratorInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolsImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolsInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeSignatureImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeSignatureInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/X_DebugHostBaseClassImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/X_DebugHostBaseClassInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyEnumeratorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyEnumeratorInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyStoreImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyStoreInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelIteratorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelIteratorInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceImpl1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceImpl2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelMethodImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelMethodInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelObjectImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelObjectInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelPropertyAccessorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelPropertyAccessorInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/RawEnumeratorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/RawEnumeratorInternal.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgApplyMethodsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgGetRegisterMapCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgListAttributesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgListElementsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/DbgModelNative.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/IDebugClientEx.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/IUnknownEx.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/UnknownWithUtils.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/WrapIUnknownEx.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/bridge/IHostDataModelAccess.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/bridge/WrapIHostDataModelAccess.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IComparableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDataModelConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDynamicConceptProviderConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDynamicKeyProviderConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IEquatableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IIndexableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IIterableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IPreferredRuntimeTypeConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IStringDisplayableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIComparableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDataModelConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDynamicConceptProviderConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDynamicKeyProviderConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIEquatableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIIndexableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIIterableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIPreferredRuntimeTypeConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIStringDisplayableConcept.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/IDataModelManager1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/IDataModelManager2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/WrapIDataModelManager1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/WrapIDataModelManager2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelNameBinder.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScript.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptClient.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptHostContext.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptManager.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptProvider.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptProviderEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptTemplate.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptTemplateEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelNameBinder.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScript.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptClient.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptHostContext.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptManager.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptProvider.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptProviderEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptTemplate.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptTemplateEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebug.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebug2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugBreakpoint.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugBreakpointEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugClient.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugStack.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugStackFrame.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugVariableSetEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebug.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebug2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugBreakpoint.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugBreakpointEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugClient.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugStack.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugStackFrame.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugVariableSetEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHost.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostBaseClass.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostConstant.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostContext.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostData.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostErrorSink.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostEvaluator1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostEvaluator2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostExtensability.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostField.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostMemory1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostMemory2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModule1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModule2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModuleSignature.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostPublic.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostScriptHost.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostStatus.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbol1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbol2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbolEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbols.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostType1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostType2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostTypeSignature.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHost.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostBaseClass.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostConstant.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostContext.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostData.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostErrorSink.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostEvaluator1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostEvaluator2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostExtensability.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostField.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostMemory1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostMemory2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModule1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModule2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModuleSignature.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostPublic.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostScriptHost.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostStatus.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbol1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbol2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbolEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbols.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostType1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostType2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostTypeSignature.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/X_IDebugHostBaseClass.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/X_WrapIDebugHostBaseClass.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IKeyEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IKeyStore.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelIterator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelKeyReference.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelKeyReference2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelMethod.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelObject.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelPropertyAccessor.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IRawEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIKeyEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIKeyStore.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelIterator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelKeyReference1.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelKeyReference2.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelMethod.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelObject.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelPropertyAccessor.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIRawEnumerator.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/javaprovider/JavaProviderNative.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/manager/DbgManager2Impl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2DefaultTargetModelRoot.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2Impl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetAvailableContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetAvailableImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetObjectImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetRootImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetSystemMarkerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DelegateDbgModel2TargetObject.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/sctldbg/cpp/sctldbg.cpp create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/test/java/agent/dbgmodel/DummyProc.java create mode 100644 Ghidra/Debug/Debugger-agent-dbgmodel/src/test/java/agent/dbgmodel/dbgmodel/DbgModelTest.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/Module.manifest create mode 100644 Ghidra/Debug/Debugger-agent-gdb/build.gradle create mode 100644 Ghidra/Debug/Debugger-agent-gdb/certification.manifest create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbInJvmDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/FdInputStream.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/FdOutputStream.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/Pty.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtyEndpoint.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtyMaster.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtySessionLeader.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtySlave.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/Util.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbGadpServer.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbLocalDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/impl/GdbGadpServerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbCause.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbConsoleOutputListener.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbEventsListener.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbEventsListenerAdapter.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbInferior.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbInferiorThreadGroup.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbLibraryId.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbManager.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbMemoryOperations.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbModule.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbModuleSection.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbProcessThreadGroup.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbRegister.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbRegisterSet.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStackFrame.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStackFrameOperations.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbState.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStateListener.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbTable.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbTargetOutputListener.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbThread.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointDisp.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInsertions.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointLocation.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointType.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbCompletedCommandEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithFields.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithStateChange.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithString.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbLibraryEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbOutputEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbThreadEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbThreadGroupEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointCreatedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointDeletedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointModifiedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandConnectedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandDoneEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandEchoEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandErrorEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandExitEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandRunningEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbConsoleOutputEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbDebugOutputEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbLibraryLoadedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbLibraryUnloadedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbMemoryChangedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbParamChangedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbRunningEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbStoppedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbTargetOutputEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadCreatedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadExitedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupAddedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupExitedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupRemovedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupStartedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadSelectedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbFrameInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbInferiorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbMemoryMapping.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbMinimalSymbol.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbModuleImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbModuleSectionImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbPendingCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbStackFrameImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadAndFrameId.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadId.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbAddInferiorCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbAttachCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbClaimStopped.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbCommandError.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbConsoleExecCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbContinueCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDeleteBreakpointsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDetachCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDisableBreakpointsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEnableBreakpointsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEvaluateCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbFileExecAndSymbolsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbGetThreadInfoCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbGetVarCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInferiorSelectCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInfoOsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInsertBreakpointCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInterruptCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbKillCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListAvailableProcessesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListBreakpointsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListInferiorsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListRegisterNamesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListThreadsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadRegistersCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbRemoveInferiorCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbRunCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbSetInferiorTtyCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbSetVarCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbStackListFramesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbStepCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbThreadSelectCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbWriteMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbWriteRegistersCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/parsing/GdbCValueParser.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/parsing/GdbMiParser.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/parsing/GdbParsingUtils.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbBreakpointHitReason.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbEndSteppingRangeReason.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbExitNormallyReason.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbExitedReason.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbReason.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbSignalReceivedReason.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImplUtils.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelSelectableObject.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetAttachable.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetAvailableContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointLocation.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointSpec.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetEnvironment.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferior.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferiorContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetMemoryRegion.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetModule.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetModuleContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetProcessMemory.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetRegister.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetRegisterContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSection.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSectionContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSession.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStack.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrame.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrameRegister.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrameRegisterContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSymbol.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSymbolContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThread.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThreadContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/resources/session.py create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/sh/execjar.sh create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/ffi/linux/PtyTest.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/GdbTableTest.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/AbstractGdbManagerTest.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/GdbCValueParserTest.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/JoinedGdbManagerTest.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/SpawnedGdbManagerTest.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/parsing/GdbMiParserTest.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/AbstractModelForGdbTest.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/EventSequenceListener.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/GadpForGdbTest.java create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/ModelForGdbTest.java create mode 100644 Ghidra/Debug/Debugger-gadp/Module.manifest create mode 100644 Ghidra/Debug/Debugger-gadp/build.gradle create mode 100644 Ghidra/Debug/Debugger-gadp/certification.manifest create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/GadpRegistry.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/GadpVersion.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/DelegateGadpClientTargetObject.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClient.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAccessConditioned.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAggregate.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAttachable.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAttacher.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointContainer.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointLocation.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointSpec.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetConsole.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDataTypeMember.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDataTypeNamespace.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDeletable.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDetachable.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetEnvironment.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetEventScope.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetExecutionStateful.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetFocusScope.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetInterpreter.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetInterruptible.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetKillable.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetLauncher.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMemory.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMemoryRegion.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMethod.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetModule.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetModuleContainer.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetNamedDataType.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetObject.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetObjectStub.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetProcess.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegister.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegisterBank.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegisterContainer.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetResumable.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSection.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetStack.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetStackFrame.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSteppable.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSymbol.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSymbolNamespace.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetThread.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpTcpDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/annot/GadpAttributeChangeCallback.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/annot/GadpEventHandler.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpErrorException.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpException.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpIllegalStateException.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpMessageException.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpRuntimeException.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpLocalDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpServer.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/GadpClientHandler.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannel.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/GadpValueUtils.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/ProtobufOneofByTypeHelper.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/main/proto/gadp.proto create mode 100644 Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/GadpClientServerTest.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/client/GadpClientTest.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/client/GadpClientTestHelper.java create mode 100644 Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannelTest.java create mode 100644 Ghidra/Debug/Debugger-jpda/Module.manifest create mode 100644 Ghidra/Debug/Debugger-jpda/build.gradle create mode 100644 Ghidra/Debug/Debugger-jpda/certification.manifest create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/JdiDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiCause.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiConsoleOutputListener.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventHandler.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventsListener.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventsListenerAdapter.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiManager.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiMemoryMapping.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiReason.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiStateListener.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiTargetOutputListener.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiThreadInfo.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiVMThreadGroup.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/breakpoint/JdiBreakpointInfo.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/breakpoint/JdiBreakpointType.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/DebugStatus.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/JdiManagerImpl.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelImpl.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetAttributesContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetBreakpointContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetBreakpointSpec.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetClassContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConnector.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConnectorContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConstantPool.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetElementsContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetField.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetFieldContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocalVariable.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocalVariableContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocation.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocationContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetMethod.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetMethodContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetModule.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetModuleContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectImpl.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectReference.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectReferenceContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetProcess.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetReferenceType.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRegister.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRegisterContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRoot.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetSection.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetSectionContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetStack.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetStackFrame.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThread.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThreadContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThreadGroupContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetType.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetTypeContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetVM.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetVMContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValue.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValueContainer.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValueMap.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelSelectableObject.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetAccessConditioned.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetAttacher.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetConsole.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetDeletable.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetDetachable.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetEnvironment.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetEventScope.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetExecutionStateful.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetFocusScope.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetInterruptible.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetKillable.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetLauncher.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetMethod.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetResumable.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetSteppable.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface2/JdiModelTargetAttachable.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface2/JdiModelTargetObject.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/test/java/ghidra/dbg/jdi/JdiExperimentsTest.java create mode 100644 Ghidra/Debug/Debugger-jpda/src/test/java/ghidra/dbg/jdi/model/JdiModelTest.java create mode 100644 Ghidra/Debug/Debugger-sctl/Module.manifest create mode 100644 Ghidra/Debug/Debugger-sctl/build.gradle create mode 100644 Ghidra/Debug/Debugger-sctl/certification.manifest create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlClient.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlExtension.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlMemoryProtection.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetAttachable.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetAttachableContainer.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetBreakpoint.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetBreakpointContainer.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetCompositeDataType.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetCompositeField.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetDataTypeMember.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetDataTypeNamespace.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetEnumConstant.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetEnumDataType.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetFunctionDataType.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetFunctionParameter.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetMemory.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetMemoryRegion.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetModule.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetModuleContainer.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetNamedDataType.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetObject.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetObjectsContainer.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetProcess.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetProcessContainer.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetRegisterDescription.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetRegisters.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSection.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSectionContainer.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSession.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetStructDataType.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSymbol.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSymbolNamespace.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetThread.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetThreadContainer.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetTypedefDataType.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetTypedefDef.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetUnionDataType.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTcpDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTrace.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/depr/DebuggerAddressMapper.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/depr/DefaultDebuggerAddressMapper.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectPingResponse.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectVersionRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectVersionResponse.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlPartialWriteException.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlProtocolSequenceException.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/HasOptionalPlatform.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/HasOptionalProcessID.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SctlDialect.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SctlNullDialect.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SelSctlNullDialectPacket.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/err/SctlError.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/err/SctlRuntimeException.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlNotify.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSelSctlPacket.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlMarshaller.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlPacket.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlVersionInfo.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractByLongFieldsSctlContext.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlBinary.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlContext.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlObjectEntry.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlProcessEntry.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlProcessList.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlRegion.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlSection.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlStatus.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlThreadEntry.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlTrapSpec.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlRegisterDefinition.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlString.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlSymbol.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlEventNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlForkCloneNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlForkNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlListsLibrariesEventNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlOnlyCtxEventNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlSnapNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlCloneNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlEventNotify.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlExecNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlExitNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlLoadNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlSignalNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlSyscallNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlTrapNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlUnloadNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlAttachReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlLaunchReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlSnapshotReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlClearTrapReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlContinueReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlDetachReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateLocalsReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateSegmentsReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateSymbolsReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateTypesReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlErrorReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlGetContextReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlKillReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupAddressReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupProgramCounterReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupSourceReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupSymbolReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupTypeReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlNamesReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlPingReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlProcessListReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlReadReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlSetContextReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlSetTrapReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStatusReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStepReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStopReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlTraceReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlUnwindOneFrameReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlVersionReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlWriteReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlAttachRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlClearTrapRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlContinueRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlDetachRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateLocalsRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateSegmentsRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateSymbolsRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateTypesRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlGetContextRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlKillRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLaunchRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupAddressRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupProgramCounterRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupSourceRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupSymbolRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupTypeRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlNamesRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlPingRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlProcessListRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlReadRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSetContextRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSetTrapRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSnapshotRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStatusRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStepRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStopRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlTraceRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlUnwindOneFrameRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlVersionRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlWriteRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Attrval.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Cbase.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Evkind.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Lkind.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Mkind.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Rkind.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Rulekind.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Stype.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Tkind.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Vkind.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlAggregateTypeDefinition.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTaggedTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTypeDefinition.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAggregateField.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlArrayTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAtom.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAttributeKeyVal.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAttributes.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBaseTypeDefinition.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBaseTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBitfieldTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumConstTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumConstant.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumTypeDefinition.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlFunctionParameter.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlFunctionTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlPointerTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStringAtom.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStructTypeDefinition.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStructTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlSymbolAtom.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlTypedefTypeDefinition.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlTypedefTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUIntAtom.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUndefinedTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUnionTypeDefinition.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUnionTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SelSctlTypeDefinition.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SelSctlTypeName.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012AttachReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012ForkNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012LaunchReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012SnapNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012SnapshotReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/SelSctl2012Packet.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/SctlX86Context.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxBinary.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxProcessEntry.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxProcessList.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxRegion.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxStatus.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxThreadEntry.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxTrapSpec.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxX86Dialect.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/win/Sctl2012WindowsX86Context.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/win/Sctl2012WindowsX86Dialect.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SctlExecuteReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SctlExecuteRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SelSctl2012ExtPacket.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/x86/linux/Sctl2012ExtLinuxX86Dialect.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/x86/win/Sctl2012ExtWindowsX86Dialect.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018AttachReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018ForkNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018LaunchReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018SnapNotification.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018SnapshotReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlChooseContextReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlChooseContextRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlEnumerateContextReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlEnumerateContextRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlFocusReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlFocusRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetAttributesReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetAttributesRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetElementsReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetElementsRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepIntoReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepIntoRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOutReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOutRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOverReply.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOverRequest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SelSctl2018Packet.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018AnyAnyDialect.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Binary.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Context.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ObjectEntry.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ProcessEntry.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ProcessList.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Region.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Section.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Status.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ThreadEntry.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018TrapSpec.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/AbstractSctlClientHandler.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/AbstractSctlServer.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/SctlSyntheticClient.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlClientTest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlOnReferenceTest.java create mode 100644 Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlPacketsTest.java create mode 100644 Ghidra/Debug/Debugger/Module.manifest create mode 100644 Ghidra/Debug/Debugger/build.gradle create mode 100644 Ghidra/Debug/Debugger/certification.manifest create mode 100644 Ghidra/Debug/Debugger/data/ExtensionPoint.manifest create mode 100644 Ghidra/Debug/Debugger/ghidra_scripts/PopulateDemoTrace.java create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/shared/Frontpage.css create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/shared/arrow.gif create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/shared/close16.gif create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/shared/menu16.gif create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/shared/note-red.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/shared/note.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/shared/note.yellow.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/shared/redo.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/shared/tip.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/shared/undo.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/shared/warning.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/Debugger.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/GettingStarted.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/Troubleshooting.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBots/DebuggerBots.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerBreakpointMarkerPlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerPlaceBreakpointDialog.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-clear.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-disable.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-enable.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-ed.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-disable-all.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-enable-all.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerGoToDialog.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerModuleImportDialog.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModuleMapProposalDialog.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModulesPlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerSectionMapProposalDialog.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/DebuggerBreakpointDialog.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/DebuggerMethodInvocationDialog_ForLaunch.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/DebuggerObjectsPlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/attach.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/blank.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/breakpoint-set.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/console.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/continue.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/debugger.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/detach.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/display_as_graph.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/display_as_table.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/display_as_tree.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/display_as_xml.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/display_filtered_graph.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/display_filtered_table.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/display_filtered_tree.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/display_filtered_xml.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/export_as_facts.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/export_as_xml.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/import_from_facts.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/import_from_xml.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/kill.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/launch.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/record.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/reload.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/stepinto.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/stepout.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/stepover.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/stop.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegionsPlugin/DebuggerRegionsPlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegionsPlugin/images/DebuggerRegionsPlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/images/DebuggerAvailableRegistersDialog.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/images/DebuggerRegistersPlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/images/select-registers.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStackPlugin/DebuggerStackPlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStackPlugin/images/DebuggerStackPlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStaticMappingPlugin/images/DebuggerStaticMappingPlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/DebuggerTargetsPlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/DebuggerConnectDialog.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/DebuggerTargetsPlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/connect.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/disconnect.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/DebuggerThreadsPlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/continue.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/stepback.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/stepinto.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTimePlugin/images/DebuggerTimePlugin.png create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/AbstractDebuggerPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerPluginPackage.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/ModelActivatedPluginEvent.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/ModelObjectFocusedPluginEvent.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceActivatedPluginEvent.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceClosedPluginEvent.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceLocationPluginEvent.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceOpenedPluginEvent.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceRecorderAdvancedPluginEvent.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceSelectionPluginEvent.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewAsciiExporter.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewBinaryExporter.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewHtmlExporter.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewIntelHexExporter.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewXmlExporter.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerSnapActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/BreakpointLocationRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellRenderer.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointHistoryPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointHistoryProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocationsActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerLogicalBreakpointsActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerPlaceBreakpointDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/interpreters/AbstractDebuggerWrappedConsoleConnection.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/interpreters/DebuggerInterpreterPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/interpreters/DebuggerWrappedConsoleConnection.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/interpreters/DebuggerWrappedInterpreterConnection.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/CursorBackgroundColorModel.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerGoToDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingAutoReadMemoryAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingTrackLocationAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerModuleImportDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerTrackedRegisterListingBackgroundColorModel.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/MemoryStateListingBackgroundColorModel.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/MultiBlendedListingBackgroundColorModel.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/RegionRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/AbstractDebuggerMapProposalDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerBlockChooserDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleMapProposalDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerSectionActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerSectionMapProposalDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/ModuleRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/SectionRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/StaticMappingRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/ObjectContainer.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/ObjectContainerListener.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/ObjectUpdateService.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/ObjectUpdatedEvent.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/DisplayAsAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/DisplayAsGraphAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/DisplayAsTableAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/DisplayAsTreeAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/DisplayAsXMLAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/DisplayFilteredAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/DisplayFilteredGraphAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/DisplayFilteredTableAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/DisplayFilteredTreeAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/DisplayFilteredXMLAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/DisplayMethodsAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/ExportAsFactsAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/ExportAsXMLAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/ImportExportAsAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/ImportFromFactsAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/ImportFromXMLAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/actions/OpenTraceAction.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/AttachableProcessesTableColumns.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/DebuggerAttachDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/DebuggerAttachDialogOld.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/DebuggerBreakpointDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/DebuggerMethodInvocationDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/DummyTargetObject.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectAttributeColumn.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectAttributeRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectElementColumn.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectElementRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectEnumeratedColumnTableModel.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectNode.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectPane.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectTable.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectTree.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectTreeCellRenderer.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/AvailableRegisterRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerAvailableRegistersActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerAvailableRegistersDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegisterActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/StackFrameRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/target/DebuggerConnectDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/target/DebuggerConnectionsNode.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/target/DebuggerModelActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/target/DebuggerModelNode.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/target/DebuggerTargetsPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/target/DebuggerTargetsProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/thread/DebuggerThreadActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/thread/DebuggerThreadsPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/thread/DebuggerThreadsProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/thread/DebuggerThreadsTimelinePanel.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/thread/ThreadRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/thread/ThreadState.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/thread/ThreadTableColumns.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimePlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimeProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/SnapshotRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/timeline/DebuggerTimelineActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/timeline/DebuggerTimelinePanel.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/timeline/DebuggerTimelinePlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/timeline/DebuggerTimelineProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/timeline/TimelineRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/timeline/TimelineState.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/timeline/TimelineTableColumns.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/timeline/TraceObject.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchActionContext.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProvider.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/WatchRow.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerMappingOffer.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerTargetTraceMapper.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerMappingOffer.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerMappingOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerMemoryMapper.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerRegisterMapper.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerTargetTraceMapper.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerMemoryMapper.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerRegisterMapper.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/LargestSubDebuggerRegisterMapper.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/AbstractGdbDebuggerMappingOffer.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/ArmDisassemblyInject.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/DbgengX64DebuggerMappingOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/DbgengX64DisassemblyInject.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/GdbArmDebuggerMappingOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/GdbTargetTraceMapper.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/GdbX86DebuggerMappingOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/JdiDalvikDebuggerMappingOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/JdiJavaDebuggerMappingOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/register/DynamicRegisterChangeListener.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/register/RegisterTypeInfo.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/BreakpointActionItem.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/BreakpointActionSet.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServicePlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/DeleteBreakpointActionItem.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/DisableBreakpointActionItem.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/EnableBreakpointActionItem.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LogicalBreakpointInternal.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LoneLogicalBreakpoint.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/MappedLogicalBreakpoint.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/PlaceBreakpointActionItem.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServiceInternal.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServicePlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServiceProxyPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerSelectMappingOfferDialog.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DefaultTraceRecorder.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServicePlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/MapModulesBackgroundCommand.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/MapSectionsBackgroundCommand.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/workflow/AbstractMultiToolTraceListener.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/workflow/DebuggerWorkflowServicePlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/workflow/DebuggerWorkflowServiceProxyPlugin.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/workflow/MultiToolTraceListenerManager.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/BackgroundUtils.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/DomainFolderChangeAdapter.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/FocusUtils.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/MiscellaneousUtils.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/ProgramLocationUtils.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/ProgramURLUtils.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/TransactionCoalescer.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassembleAtPcDebuggerBot.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassemblyInject.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/DisassemblyInjectInfo.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/MapModulesDebuggerBot.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/MapSectionsDebuggerBot.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/workflow/ShowInterpreterDebuggerBot.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerBot.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerBotInfo.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerInterpreterService.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerListingService.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerLogicalBreakpointService.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerModelService.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerStaticMappingChangeListener.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerStaticMappingService.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerTraceManagerService.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerWorkflowService.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/LogicalBreakpoint.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/LogicalBreakpointsChangeListener.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/TraceRecorder.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/TraceRecorderListener.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/AsyncPcodeExecutor.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/AsyncWrappedPcodeArithmetic.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/AsyncWrappedPcodeExecutorState.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/AsyncWrappedPcodeExecutorStatePiece.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/TracePcodeExecutorState.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/TracePcodeUtils.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/TraceRecorderAsyncPcodeExecutorState.java create mode 100644 Ghidra/Debug/Debugger/src/main/resources/defaultTools/Debugger.tool create mode 100644 Ghidra/Debug/Debugger/src/main/resources/define_info_proc_mappings create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/add.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/attach.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/autoread.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/blank.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-clear.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-disable.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-enable.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-de.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-ed.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-set.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-clear-all.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-disable-all.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-enable-all.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoints.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/closedFolder.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/connect.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/console.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/continue.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/debugger.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/debugger32.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/delete.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/detach.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/disconnect.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/kill.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/launch.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/modules.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/object-populated.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/object-running.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/object-unpopulated.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/process.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/record.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/register-marker.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/registers.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/select-registers.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/skipover.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/stack.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/stepback.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/stepinto.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/stepout.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/stepover.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/stop.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/sync_enabled.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/table.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/text-xml.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/thread.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/time.png create mode 100644 Ghidra/Debug/Debugger/src/main/svg/attach.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/blank.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoint-clear.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-de.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ed.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoint-set.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoints-clear-all.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoints-disable-all.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoints-enable-all.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoints.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/connect.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/console.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/continue.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/debugger.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/detach.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/disconnect.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/kill.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/launch.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/memory.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/process.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/recording.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/register-marker.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/registers.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/select-registers.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/skipover.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/stack.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/stepback.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/stepinto.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/stepout.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/stepover.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/stop.svg create mode 100644 Ghidra/Debug/Debugger/src/main/svg/thread.svg create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/CheckImagesTest.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsPluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesPluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingPluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsPluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/objects/components/DebuggerObjectsAccessHelper.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersPluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackPluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/target/DebuggerTargetsPluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/thread/DebuggerThreadsPluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimePluginScreenShots.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/DebuggerManualTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointHistoryProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/interpreters/DebuggerInterpreterPluginTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/target/DebuggerTargetsProviderFriend.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/target/DebuggerTargetsProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/thread/DebuggerThreadsProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimeProviderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/LargestSubDebuggerRegisterMapperTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/opinion/DebuggerOpinionsTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServiceTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/model/DefaultTraceRecorderTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServiceTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServiceTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/url/ProjectExperimentsTest.java create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/pcode/exec/TraceRecorderAsyncPcodeExecTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/Module.manifest create mode 100644 Ghidra/Debug/Framework-AsyncComm/build.gradle create mode 100644 Ghidra/Debug/Framework-AsyncComm/certification.manifest create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncClaimQueue.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncConfigFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncDebouncer.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncFence.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncHandlerCanExit.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncLazyMap.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncLazyValue.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncLock.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncPairingCache.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncPairingQueue.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncRace.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncReference.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncTimer.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncUtils.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/SwingExecutorService.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/TypeSpec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoop.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopFirstActionConsumesAndProduces.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopFirstActionProduces.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopHandlerForFirst.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopHandlerForSecond.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopOnlyActionRuns.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopSecondActionConsumes.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionConsumes.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionConsumesAndProduces.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionProduces.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionRuns.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceHandlerForProducer.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceHandlerForRunner.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceWithTemp.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceWithoutTemp.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/AbstractPacketCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/AbstractPacketFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/AbstractPacketMarshaller.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/AsynchronousPacketChannel.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/AsynchronousPacketDebugChannel.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/DebugByteChannel.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/DefaultPacketFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/Packet.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/PacketCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/PacketFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/PacketMarshaller.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/BitmaskEncoded.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/CountedByField.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/EncodeChars.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/OptionalField.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/RepeatedField.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/SizedByField.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/SizedByMethods.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/TypedByField.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/TypedByLookahead.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/WithFlag.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/BitmaskEncodedFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/BitmaskEncodedFieldCodecWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/CountedByFieldFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/CountedByFieldInjectedFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/CountedByFieldInjectionFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/CountedByFieldWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/EncodeCharsFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/EncodeCharsWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/OptionalFieldFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/OptionalFieldWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/RepeatedFieldWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/SizedByFieldFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/SizedByFieldInjectedFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/SizedByFieldInjectionFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/SizedByFieldWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/SizedByMethodsFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/SizedByMethodsInjectedFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/SizedByMethodsInjectionFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/SizedByMethodsWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/TypedByFieldFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/TypedByFieldInjectedFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/TypedByFieldInjectionFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/TypedByFieldWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/TypedByLookaheadFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/TypedByLookaheadWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/WithFlagFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/WithFlagInjectedFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/WithFlagInjectionFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/annot/impl/WithFlagWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/AbstractBinaryPacketCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/AbstractByteBufferPacketCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/BinaryLEPacketCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/BinaryPacketCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/ByteArrayFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/ByteBufferEnumFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/ByteBufferPacketCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/NullTerminated.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/NullTerminatedWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/ReverseByteOrder.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/ReverseByteOrderWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/SequenceTerminated.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/SequenceTerminatedFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/binary/SequenceTerminatedWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/ArrayFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/BooleanFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/ByteFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/CharSequenceFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/CharacterFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/CollectionFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/DoubleFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/ElementCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/FloatFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/IntegerFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/LongFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/PacketCodecInternal.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/PacketFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/PrimitiveCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/codecs/ShortFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/AnnotatedFieldOrderingException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/FieldOrderingException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/InvalidFieldModifiersException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/InvalidFieldNameException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/InvalidMethodNameException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/InvalidPacketException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/PacketAnnotationException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/PacketDeclarationException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/PacketDecodeException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/PacketEncodeException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/PacketFieldDeclarationException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/err/PacketFieldValueMismatchException.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/fields/AbstractFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/fields/AbstractFieldCodecFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/fields/AbstractWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/fields/FieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/fields/FieldCodecFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/fields/ImplementedBy.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/fields/InjectionFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/fields/PacketField.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/fields/PrimitiveFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/fields/WrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/CharArrayFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/RegexSeparated.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/RegexSeparatedWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/RegexTerminated.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/RegexTerminatedFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/RegexTerminatedWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/SizeRestricted.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/SizeRestrictedFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/SizeRestrictedWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/StringEnumFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/StringPacketCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/WithRadix.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/WithRadixByteFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/WithRadixDoubleFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/WithRadixFloatFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/WithRadixIntegerFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/WithRadixLongFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/WithRadixShortFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/WithRadixWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/WithSign.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/WithSignFieldCodec.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/string/WithSignWrapperFactory.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/service/AbstractAsyncClientHandler.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/service/AbstractAsyncServer.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/BitmaskSet.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/BitmaskUniverse.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/ByteBufferUtils.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/pyexport/GeneratePython.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/pyexport/PythonClass.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/pyexport/PythonPackage.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/py/ghidra/__init__.py create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/py/ghidra/comm/__init__.py create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/py/ghidra/comm/packet/__init__.py create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/py/ghidra/comm/packet/annot.py create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/py/ghidra/comm/packet/binary.py create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/py/ghidra/comm/packet/string.py create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/py/ghidra/comm/util.py create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/py/java/__init__.py create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/py/java/lang.py create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/py/java/util.py create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/resources/ghidra/comm/util/pyexport/__init__.py.template create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/main/resources/ghidra/comm/util/pyexport/setup.py.template create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncLaziesTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncLockTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncPacketChannelTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncRaceTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncReferenceTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncTestUtils.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncTimerTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncUtilsTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/BinaryPacketDecodingTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/BinaryPacketEncodingTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/ByteBufferPacketDecodingTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/ByteBufferPacketEncodingTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/PacketCompTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/PacketFieldAnnotTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/PacketTestClasses.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/PutzTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/StringPacketDecodingTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/StringPacketEncodingTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/package-info.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/tests/packet/subpkg/PacketInSubpackage.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/util/BitmaskSetTest.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/util/CheckPackets.java create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/py/classes/ghidra/comm/tests/packet/_class_PacketTestClasses_TestMessageFullSpecColField_IntList.py create mode 100644 Ghidra/Debug/Framework-AsyncComm/src/test/py/ghidra/comm/test_packet.py create mode 100644 Ghidra/Debug/Framework-Debugging/Module.manifest create mode 100644 Ghidra/Debug/Framework-Debugging/build.gradle create mode 100644 Ghidra/Debug/Framework-Debugging/certification.manifest create mode 100644 Ghidra/Debug/Framework-Debugging/data/ExtensionPoint.manifest create mode 100644 Ghidra/Debug/Framework-Debugging/src/expCloneExec/c/expCloneExec.c create mode 100644 Ghidra/Debug/Framework-Debugging/src/expCloneExit/c/expCloneExit.c create mode 100644 Ghidra/Debug/Framework-Debugging/src/expCloneSpin/c/expCloneSpin.c create mode 100644 Ghidra/Debug/Framework-Debugging/src/expCreateProcess/c/expCreateProcess.c create mode 100644 Ghidra/Debug/Framework-Debugging/src/expCreateThreadExit/c/expCreateThreadExit.c create mode 100644 Ghidra/Debug/Framework-Debugging/src/expCreateThreadSpin/c/expCreateThreadSpin.c create mode 100644 Ghidra/Debug/Framework-Debugging/src/expFork/c/expFork.c create mode 100644 Ghidra/Debug/Framework-Debugging/src/expPrint/c/expPrint.c create mode 100644 Ghidra/Debug/Framework-Debugging/src/expSpin/c/expSpin.c create mode 100644 Ghidra/Debug/Framework-Debugging/src/expTypes/c/expTypes.c create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/DebugModelConventions.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/DebuggerAbnormalModelClosedReason.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/DebuggerModelClosedReason.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/DebuggerModelFactory.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/DebuggerModelListener.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/DebuggerNormalModelClosedReason.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/DebuggerObjectModel.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/DebuggerTargetObjectIface.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/LocalDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/agent/AbstractDebuggerObjectModel.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/agent/AbstractTargetObject.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/agent/AgentWindow.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/agent/DefaultTargetModelRoot.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/agent/DefaultTargetObject.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/agent/DefaultTargetObjectRef.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/agent/InvalidatableTargetObjectIf.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/agent/SpiDebuggerObjectModel.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/attributes/TargetArrayDataType.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/attributes/TargetBitfieldDataType.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/attributes/TargetDataType.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/attributes/TargetNamedDataTypeRef.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/attributes/TargetObjectRef.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/attributes/TargetObjectRefList.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/attributes/TargetPointerDataType.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/attributes/TargetPrimitiveDataType.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/attributes/TargetStringList.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/attributes/TypedTargetObjectRef.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/attributes/TypedTargetObjectRefList.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/error/DebuggerIllegalArgumentException.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/error/DebuggerMemoryAccessException.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/error/DebuggerModelAccessException.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/error/DebuggerModelNoSuchPathException.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/error/DebuggerModelTypeException.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/error/DebuggerRegisterAccessException.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/error/DebuggerRuntimeException.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/error/DebuggerUserException.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/memory/CachedMemory.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/memory/MemoryReader.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/memory/MemoryWriter.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetAccessConditioned.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetAggregate.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetAttachable.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetAttacher.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetBreakpointContainer.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetBreakpointLocation.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetBreakpointSpec.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetConsole.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetDataTypeMember.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetDataTypeNamespace.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetDeletable.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetDetachable.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetEnvironment.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetEventScope.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetExecutionStateful.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetFocusScope.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetInterpreter.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetInterruptible.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetKillable.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetLauncher.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetMemory.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetMemoryRegion.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetMethod.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetModule.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetModuleContainer.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetNamedDataType.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetObject.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetProcess.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetRegister.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetRegisterBank.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetRegisterContainer.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetResumable.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetSection.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetStack.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetStackFrame.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetSteppable.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetSymbol.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetSymbolNamespace.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetThread.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TypedTargetObject.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/CollectionUtils.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/ConfigurableFactory.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/ConversionUtils.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/HandlerMap.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/PathMatcher.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/PathPattern.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/PathPredicates.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/PathUtils.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/PrefixMap.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/ShellUtils.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/TargetDataTypeConverter.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/ValueUtils.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/DebugModelConventionsTest.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/agent/DefaultDebuggerObjectModelTest.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/memory/CachedMemoryTest.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/AbstractTestTargetRegisterBank.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/DefaultTestTargetObject.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestDebuggerModelBuilder.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestDebuggerObjectModel.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestLocalDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestMimickJavaLauncher.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetBreakpoint.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetBreakpointContainer.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetDataTypeMember.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetDataTypeNamespace.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetEnvironment.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetInterpreter.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetMemory.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetMemoryRegion.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetModule.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetModuleContainer.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetNamedDataType.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetObject.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetProcess.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetProcessContainer.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetRegister.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetRegisterBankInFrame.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetRegisterBankInThread.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetRegisterContainer.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetSection.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetSectionContainer.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetSession.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetStack.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetStackFrame.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetStackFrameHasRegisterBank.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetStackFrameIsRegisterBank.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetSymbol.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetSymbolNamespace.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetThread.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetThreadContainer.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetTypedefDataType.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetTypedefDef.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/testutil/DummyProc.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/util/AbstractInvocationListener.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/util/AllTargetObjectListenerAdapter.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/util/AttributesChangedListener.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/util/DebuggerModelTestUtils.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/util/ElementTrackingListener.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/util/ElementsChangedListener.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/util/InvalidatedListener.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/util/PathUtilsTest.java create mode 100644 Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/util/StreamTokenizerExperimentsTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/Module.manifest create mode 100644 Ghidra/Debug/Framework-TraceModeling/build.gradle create mode 100644 Ghidra/Debug/Framework-TraceModeling/certification.manifest create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/framework/data/DomainObjectEventQueues.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/BytesPcodeExecutorStateMixin.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceBytesPcodeExecutorState.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceMemoryStatePcodeArithmetic.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceMemoryStatePcodeExecutorStatePiece.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceSleighUtils.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceCacheForContainingQueries.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceCacheForSequenceQueries.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceChangeSet.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceContentHandler.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceUserData.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTraceUtils.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/bookmark/DBTraceBookmark.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/bookmark/DBTraceBookmarkManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/bookmark/DBTraceBookmarkRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/bookmark/DBTraceBookmarkSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/bookmark/DBTraceBookmarkType.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/breakpoint/DBTraceBreakpoint.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/breakpoint/DBTraceBreakpointManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/breakpoint/DBTraceBreakpointSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/context/DBTraceRegisterContextManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/context/DBTraceRegisterContextRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/context/DBTraceRegisterContextSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataSettingsAdapter.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataSettingsOperations.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguage.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguageMappedMemory.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceGuestLanguageMappedRange.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/language/DBTraceLanguageManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractBaseDBTraceCodeUnitsMemoryView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractBaseDBTraceCodeUnitsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractBaseDBTraceDefinedUnitsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractComposedDBTraceCodeUnitsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceCodeUnit.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceDataComponent.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractSingleDBTraceCodeUnitsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractWithUndefinedDBTraceCodeUnitsMemoryView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeUnitAdapter.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeUnitsMemoryView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeUnitsRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCodeUnitsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceCommentAdapter.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceData.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDataAdapter.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDataArrayElementComponent.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDataCompositeFieldComponent.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDataMemoryView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDataRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDataView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataAdapter.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataMemoryView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedUnitsMemoryView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedUnitsRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedUnitsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstruction.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsMemoryView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceInstructionsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceUndefinedDataMemoryView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceUndefinedDataRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceUndefinedDataView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/AbstractDBTraceAddressSnapRangePropertyMapOcclusionIterable.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/AbstractDBTracePropertyMap.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/DBTraceAddressSnapRangePropertyMap.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/DBTraceAddressSnapRangePropertyMapAddressSetView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/DBTraceAddressSnapRangePropertyMapOcclusionIntoFutureIterable.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/DBTraceAddressSnapRangePropertyMapOcclusionIntoPastIterable.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/DBTraceAddressSnapRangePropertyMapRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/DBTraceAddressSnapRangePropertyMapSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/DBTraceAddressSnapRangePropertyMapTree.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemBuffer.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemoryBlockEntry.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemoryBufferEntry.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemoryManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemoryRegion.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemoryRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemoryStateEntry.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/module/DBTraceModule.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/module/DBTraceModuleManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/module/DBTraceModuleSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/module/DBTraceSection.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/module/DBTraceStaticMapping.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/module/DBTraceStaticMappingManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewListing.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewMemory.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/AbstractDBTraceProgramViewReferenceManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewBookmarkManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewChangeSet.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewEquate.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewEquateTable.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewFragment.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewListing.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemory.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemoryBlock.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewProgramContext.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewPropertyMapManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewReferenceManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRegisterListing.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRegisterMemory.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRegisterMemoryBlock.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRegisters.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRegistersReferenceManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRootModule.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewSymbolTable.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceVariableSnapProgramView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/property/DBTraceAddressPropertyManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/space/AbstractDBTraceSpaceBasedManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/space/DBTraceDelegatingManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/space/DBTraceSpaceBased.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/space/DBTraceSpaceKey.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceStack.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceStackFrame.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceStackManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/AbstractDBTraceSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/AbstractDBTraceSymbolSingleTypeView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/AbstractDBTraceSymbolSingleTypeWithAddressView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/AbstractDBTraceSymbolSingleTypeWithLocationView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/AbstractDBTraceVariableSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceClassSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceClassSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceEquate.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceEquateManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceEquateRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceEquateSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceFunctionStackFrame.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceFunctionSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceFunctionSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceGlobalVariableSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceGlobalVariableSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceLabelSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceLabelSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceLocalVariableSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceLocalVariableSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceNamespaceSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceNamespaceSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceOffsetReference.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceParameterSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceParameterSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTracePlaceholderSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceReference.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceReferenceManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceReferenceRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceReferenceSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceShiftedReference.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceSnapSelectedReferenceSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceStackReference.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceSymbolManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceSymbolMultipleTypesNoDuplicatesView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceSymbolMultipleTypesView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceSymbolMultipleTypesWithAddressNoDuplicatesView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceSymbolMultipleTypesWithAddressView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceSymbolMultipleTypesWithLocationView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/thread/DBTraceThread.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/thread/DBTraceThreadManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/time/DBTraceSnapshot.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/time/DBTraceTimeManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/AddressSnap.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/DefaultAddressSnap.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/DefaultTraceLocation.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/DefaultTraceSnap.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/ImmutableTraceAddressSnapRange.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/Trace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceAddressSnapRange.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceAddressSnapSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceChangeSet.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceDomainObjectListener.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceLocation.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceOptionsManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceSnap.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/TraceUserData.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/bookmark/TraceBookmark.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/bookmark/TraceBookmarkManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/bookmark/TraceBookmarkOperations.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/bookmark/TraceBookmarkRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/bookmark/TraceBookmarkSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/bookmark/TraceBookmarkType.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/breakpoint/TraceBreakpoint.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/breakpoint/TraceBreakpointKind.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/breakpoint/TraceBreakpointManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/context/TraceRegisterContextManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/context/TraceRegisterContextOperations.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/context/TraceRegisterContextRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/context/TraceRegisterContextSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/data/TraceBasedDataTypeManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceGuestLanguage.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceGuestLanguageMappedRange.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/language/TraceLanguageManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceBaseCodeUnitsRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceBaseCodeUnitsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceBaseDefinedRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceBaseDefinedUnitsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceCodeManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceCodeOperations.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceCodeRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceCodeSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceCodeUnit.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceCodeUnitsRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceCodeUnitsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceData.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceDataRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceDataView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceDefinedDataRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceDefinedDataView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceDefinedUnitsRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceDefinedUnitsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceInstruction.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceInstructionsRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceInstructionsView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceUndefinedDataRegisterView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/listing/TraceUndefinedDataView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/map/TraceAddressSnapRangePropertyMap.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/map/TraceAddressSnapRangePropertyMapOperations.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/map/TraceAddressSnapRangePropertyMapRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/map/TraceAddressSnapRangePropertyMapSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/map/TracePropertyMap.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/map/UnsignedUtils.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryFlag.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryOperations.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryRegion.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemorySpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemorySpaceInputStream.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryState.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceOverlappedRegionException.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/modules/TraceConflictedMappingException.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/modules/TraceModule.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/modules/TraceModuleManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/modules/TraceModuleOperations.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/modules/TraceModuleSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/modules/TraceSection.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/modules/TraceStaticMapping.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/modules/TraceStaticMappingManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/program/TickSpecificTraceView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/program/TraceProgramView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/program/TraceProgramViewBookmarkManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/program/TraceProgramViewListing.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/program/TraceProgramViewMemory.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/program/TraceProgramViewRegisterListing.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/program/TraceVariableSnapProgramView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/property/TraceAddressPropertyManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/stack/TraceStack.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/stack/TraceStackFrame.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/stack/TraceStackManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceClassSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceClassSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceEquate.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceEquateManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceEquateOperations.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceEquateReference.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceEquateRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceEquateSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceFunctionSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceFunctionSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceGlobalVariableSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceGlobalVariableSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceLabelSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceLabelSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceLocalVariableSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceLocalVariableSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceNamespaceSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceNamespaceSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceOffsetReference.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceParameterSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceParameterSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceReference.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceReferenceManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceReferenceOperations.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceReferenceRegisterSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceReferenceSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceShiftedReference.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceStackReference.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceSymbolManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceSymbolNoDuplicatesView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceSymbolView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceSymbolWithAddressNoDuplicatesView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceSymbolWithAddressView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceSymbolWithLifespan.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceSymbolWithLocationView.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceVariableSymbol.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/thread/TraceThread.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/thread/TraceThreadManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/TraceSnapshot.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/TraceTimeManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/ByteArrayUtils.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/DataAdapterFromDataType.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/DefaultTraceChangeType.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/EmptyFunctionIterator.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/EnumeratingIterator.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/InstructionAdapterFromPrototype.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/MemBufferAdapter.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/MethodProtector.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/OverlappingObjectIterator.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceAddressSpace.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceChangeManager.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceChangeRecord.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceChangeType.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceRegisterUtils.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/experiments/ToArrayTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TraceSleighUtilsTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/bookmark/DBTraceBookmarkManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/breakpoint/DBTraceBreakpointManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/context/DBTraceRegisterContextManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/data/DBTraceDataTypeManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/language/DBTraceLanguageManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeUnitTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/map/DBTraceAddressSnapRangePropertyMapAddressSetViewTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/map/DBTraceAddressSnapRangePropertyMapOcclusionIntoFutureIterableTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/map/DBTraceAddressSnapRangePropertyMapOcclusionIntoPastIterableTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/map/DBTraceAddressSnapRangePropertyMapSpaceTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/memory/AbstractDBTraceMemoryManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/memory/DBTraceMemoryManagerBETest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/memory/DBTraceMemoryManagerLETest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/module/DBTraceModuleManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/module/DBTraceStaticMappingManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceDisassemblerIntegrationTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceProgramViewListingTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/stack/DBTraceStackManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/symbol/DBTraceFunctionSymbolTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/symbol/DBTraceReferenceManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/symbol/DBTraceSymbolManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/thread/DBTraceThreadManagerTest.java create mode 100644 Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/util/OverlappingObjectIteratorTest.java create mode 100644 Ghidra/Debug/ProposedUtils/Module.manifest create mode 100644 Ghidra/Debug/ProposedUtils/build.gradle create mode 100644 Ghidra/Debug/ProposedUtils/certification.manifest create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/ExpanderArrowExpansionListener.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/ExpanderArrowExpansionVetoException.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/ExpanderArrowPanel.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/HorizontalTabPanel.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/RangeCursorPanel.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/RangeCursorValueListener.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/CellEditorUtils.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/CustomToStringCellRenderer.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/DefaultEnumeratedColumnProgramTableModel.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/DefaultEnumeratedColumnTableModel.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedColumnProgramTableModel.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/EnumeratedColumnTableModel.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/HexBigIntegerTableCellEditor.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/HexBigIntegerTableCellRenderer.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/IconButtonTableCellEditor.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/IconButtonTableCellRenderer.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/table/RowWrappedEnumeratedColumnTableModel.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/timeline/TimelinePanel.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/timeline/TimelineViewRangeListener.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/tree/AnyChangeTreeModelListener.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/docking/widgets/tree/SearchableByObjectGTreeNode.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/AbstractUnionedCollection.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/CatenatedCollection.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/ComparableTupleRecord.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/ID.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/NestedIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/TupleRecord.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/Unique.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/depends/DependentService.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/depends/DependentServiceConstructor.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/depends/DependentServiceResolver.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/depends/err/ServiceConstructionException.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/depends/err/UnsatisfiedFieldsException.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/generic/depends/err/UnsatisfiedParameterException.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/base/widgets/table/DataTypeTableCellEditor.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/data/DBDomainObjectSupport.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/data/OpenedDomainFile.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/options/AutoOptions.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/options/AutoOptionsListener.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/options/annotation/AutoOptionConsumed.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/options/annotation/AutoOptionDefined.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/options/annotation/HelpInfo.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/plugintool/AutoConfigState.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/plugintool/AutoService.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/plugintool/PluginToolUtils.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/plugintool/annotation/AutoConfigStateField.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/plugintool/annotation/AutoServiceConsumed.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/plugintool/annotation/AutoServiceProvided.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/plugintool/util/AutoServiceListener.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/generic/util/datastruct/DynamicSortedTreeSet.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/generic/util/datastruct/SemisparseByteArray.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/graph/algo/TopologicalSorter.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/lifecycle/Experimental.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/lifecycle/Internal.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/lifecycle/Unfinished.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/AbstractLongOffsetPcodeExecutorState.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/AbstractLongOffsetPcodeExecutorStatePiece.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/AbstractOffsetTransformedPcodeExecutorState.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/AddressOfPcodeArithmetic.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/AddressOfPcodeExecutorState.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/AnnotatedSleighUseropLibrary.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BigIntegerPcodeArithmetic.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BytesPcodeArithmetic.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PairedPcodeArithmetic.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PairedPcodeExecutorState.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PairedPcodeExecutorStatePiece.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeArithmetic.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeExecutor.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeExecutorState.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeExecutorStatePiece.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeFrame.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/SleighExpression.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/SleighLinkException.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/SleighProgram.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/SleighProgramCompiler.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/SleighUseropLibrary.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/program/model/address/CachedAddressSetView.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/AbstractAddressSetView.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/AbstractPeekableIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/AddressIteratorAdapter.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/AddressRangeComparators.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/AddressRangeIterators.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/AnnotatedSaveable.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/ComparatorMath.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/DifferenceAddressSetView.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/GhidraLockHold.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/IntersectionAddressSetView.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/LanguageUtilities.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/LockHold.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/MergeSortingIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/PairingIteratorMerger.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/PeekableIterators.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/SymmetricDifferenceAddressSetView.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/TimedMsg.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/TwoWayBreakdownAddressRangeIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/UIManagerWrapper.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/UnionAddressRangeIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/UnionAddressSetView.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/AbstractDirectedLongKeyIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/AbstractDirectedRecordIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/BackwardLongKeyIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/BackwardRecordIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBAnnotatedObject.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBAnnotatedObjectFactory.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBBufferInputStream.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBBufferOutputStream.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedDomainObjectAdapter.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectIndex.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStore.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreEntrySet.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreEntrySubSet.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreFactory.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreFoundKeysValueCollection.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreKeySet.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreKeySubSet.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreMap.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreSubMap.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreValueCollection.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreValueSubCollection.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBObjectColumn.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBOpenMode.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBTransaction.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DirectedIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DirectedLongKeyIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DirectedRecordIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/ForwardLongKeyIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/ForwardRecordIterator.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/SchemaBuilder.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/UndoableTransaction.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/annot/DBAnnotatedColumn.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/annot/DBAnnotatedField.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/annot/DBAnnotatedIndex.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/annot/DBAnnotatedObjectInfo.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/err/NoDefaultCodecException.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/AbstractConstraintsTree.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/AbstractConstraintsTreeSpatialMap.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/AbstractRStarConstraintsTree.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/BoundedShape.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/BoundingShape.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/DBTreeDataRecord.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/DBTreeNodeRecord.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/DBTreeRecord.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/Query.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/SpatialMap.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/rect/Abstract2DRStarTree.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/rect/AbstractRectangle2DQuery.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/rect/EuclideanSpace2D.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/rect/ImmutablePoint2D.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/rect/ImmutableRectangle2D.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/rect/Point2D.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/rect/Rectangle2D.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/spatial/rect/Rectangle2DDirection.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/datastruct/CollectionChangeListener.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/datastruct/DefaultObservableCollection.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/datastruct/ListenerMap.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/datastruct/ListenerSet.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/datastruct/ObservableCollection.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/layout/ProportionalHorizontalLayout.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/utilities/util/ProxyUtilities.java create mode 100644 Ghidra/Debug/ProposedUtils/src/main/java/utilities/util/SuppressableCallback.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/docking/widgets/timeline/TimelinePanelTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/generic/NestedIteratorTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/generic/depends/DependentServiceResolverTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/framework/options/AutoOptionsTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/framework/plugintool/AutoServiceTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/generic/util/datastruct/DynamicSortedTreeSetTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/generic/util/datastruct/SemisparseByteArrayTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/AnnotatedSaveableTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/DifferenceAddressSetViewTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/IntersectionAddressSetViewTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/MergeSortingIteratorTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/SymmetricDifferenceAddressSetViewTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/TwoWayBreakdownAddressRangeIteratorTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/UnionAddressSetViewTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/database/DBCachedObjectStoreFactoryTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/database/DBCachedObjectStoreTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/database/spatial/RStarTreeMapTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/datastruct/ListenerMapTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/datastruct/ListenerSetTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/ghidra/util/datastruct/ObservableCollectionTest.java create mode 100644 Ghidra/Debug/ProposedUtils/src/test/java/utilities/util/deeper/ProxyUtilitiesTest.java delete mode 100644 Ghidra/Framework/SoftwareModeling/Sleigh.launch diff --git a/.gitignore b/.gitignore index a2896ae8a3..aeb07d8f84 100644 --- a/.gitignore +++ b/.gitignore @@ -84,4 +84,6 @@ gradle/wrapper gradlew gradlew.* - +# Ignore logs and core dumps +*.log +core.* diff --git a/DebuggerDevGuide.md b/DebuggerDevGuide.md new file mode 100644 index 0000000000..b3731a5d66 --- /dev/null +++ b/DebuggerDevGuide.md @@ -0,0 +1,107 @@ +# Debugger Developer's Guide + +## Catalog of Dependencies + +In addition to Ghidra's normal dependencies, you may want the following: + + * WinDbg for Windows x64 + * GDB 8.0 or later for Linux amd64/x86_64 + +The others (e.g., JNA) are handled by Gradle via Maven Central. + +## Architecture Overview + +There are several Eclipse projects each fitting into a larger architectural picture. +These all currently reside in the `Ghidra/Debug` directory, but will likely be re-factored into the `Framework` and `Feature` directories later. +Each project is listed "bottom up" with a brief description and status. + + * ProposedUtils - a collection of utilities proposed to be moved to other respective projects + * AnnotationValidator - an experimental annotation processor for database access objects + * Framework-TraceModeling - a database schema and set of interfaces for storing machine state over time + * Framework-AsyncComm - a collection of utilities for asynchronous communication (packet formats and completable-future conveniences). + * Framework-Debugging - specifies interfaces for debugger models and provides implementation conveniences. + * Debugger - the collection of Ghidra plugins and services comprising the Debugger UI. + * Debugger-agent-dbgeng - the connector for WinDbg (via dbgeng.dll) on Windows x64. + * Debugger-agent-dbgmodel - an experimental connector for WinDbg Preview (with TTD, via dbgmodel.dll) on Windows x64. + * Debugger-agent-dbgmodel-traceloader - an experimental "importer" for WinDbg trace files. + * Debugger-agent-gdb - the connector for GDB (8.0 or later recommended) on UNIX. + * Debugger-gadp - the connector for our custom wire protocol the Ghidra Asynchronous Debugging Protocol. + * Debugger-jpda - an in-development connector for Java and Dalvik debugging via JDI (i.e., JDWP). + * Debugger-sctl - a deprecated connector for the SCTL stub (cqctworld.org). + +The Trace Modeling schema records machine state and markup over time. +It rests on the same database as Programs, allowing trace recordings to be stored in a Ghidra project and shared via a server, if desired. +Trace "recording" is a de facto requirement for displaying information in Ghidra's UI. +However, only the machine state actually observed by the user (or perhaps a script) is recorded. +For most use cases, the Trace is small and ephemeral, serving only to mediate between the UI components and the target's model. +It supports many of the same markup (e.g., disassembly, data types) as Programs, in addition to tracking active threads, loaded modules, breakpoints, etc. + +Every model (or "adapter" or "connector" or "agent") implements the API specified in Framework-Debugging. As a general rule in Ghidra, no component is allowed to access a native API and reside in the same JVM as the Ghidra UI. +This allows us to contain crashes, preventing data loss. +To accommodate this requirement -- given that debugging native applications is almost certainly going to require access to native APIs -- we've developed the Ghidra Asynchronous Debugging Protocol. +This protocol is tightly coupled to Framework-Debugging, essentially exposing its methods via RMI. +The protocol is built using Google's Protobuf library, providing a potential path for agent implementations in alternative languages. +GADP provides both a server and a client implementation. +The server can accept any model which adheres to the specification and expose it via TCP; the client does the converse. +When a model is instantiated in this way, it is called an "agent," because it is executing in its own JVM. +The other connectors, which do not use native APIs, may reside in Ghidra's JVM and typically implement alternative wire protocols, e.g., JDWP and SCTL. +In both cases, the implementations inherit from the same interfaces. + +The Debugger services maintain a collection of active connections and inspect each model for potential targets. +When a target is found, the service inspects the target environment and attempts to find a suitable opinion. +Such an opinion, if found, instructs Ghidra how to map the objects, addresses, registers, etc. from the target namespace into Ghidra's. +The target is then handed to a Trace Recorder which begins collecting information needed to populate the UI, e.g., the program counter, stack pointer, and the bytes of memory they refer to. + +## Developing a new connector + +So Ghidra does not yet support your favorite debugger? +It is tempting, exciting, but also daunting to develop your own connector. +Please finish reading this guide, and look carefully at the ones we have so far, and perhaps ask to see if we are already developing one. +Of course, in time you might also search the internet to see if others are developing one. +There are quite a few caveats and gotchas, the most notable being that this interface is still in quite a bit of flux. +When things go wrong, it could be because of, without limitation: 1) a bug on your part, 2) a bug on our part, 3) a design flaw in the interfaces, or 4) a bug in the debugger/API your adapting. +We are still in the process of writing up this documentation. +In the meantime, we recommend using the GDB and dbgeng.dll agents as examples. + +You'll also need to provide launcher(s) so that Ghidra knows how to configure and start your connector. +Please provide launchers for your model in both configurations: as a connector in Ghidra's JVM, and as a GADP agent. +If your model requires native API access, you should only permit launching it as a GADP agent, unless you give ample warning in the launcher's description. +Look at the existing launchers for examples. +There are many model implementation requirements that cannot be expressed in Java interfaces. +Failing to adhere to those requirements may cause different behaviors with and without GADP. +Testing with GADP tends to reveal those implementation errors, but also obscures the source of client method calls behind network messages. + +## Adding a new platform + +If an existing connector exists for a suitable debugger on the desired platform, then adding it may be very simple. +For example, both the x86 and ARM platforms are supported by GDB, so even though we're currently focused on x86 support, we've provided the opinions needed for Ghidra to debug ARM platforms via GDB. +These opinions are kept in the "Debugger" project, not their respective "agent" projects. +We imagine there are a number of platforms that could be supported almost out of the box, except that we haven't written the necessary opinions, yet. +Take a look at the existing ones for examples. + +In general, to write a new opinion, you need to know: 1) What the platform is called (including variant names) by the debugger, 2) What the processor language is called by Ghidra, 3) If applicable, the mapping of target address spaces into Ghidra's address spaces, 4) If applicable, the mapping of target register names to those in Ghidra's processor language. +In most cases (3) and (4) are already implemented by default mappers, so you can use those same mappers in your opinion. +Once you have the opinion written, you can try debugging and recording a target. +If Ghidra finds your opinion applicable to that target, it will attempt to record, and then you can work out the kinds from there. +Again, we have a bit of documentation to do regarding common pitfalls. + +## Emulation + +It may be tempting to write a "connector" for emulation, but we recommend against it. +We are exploring the inclusion of emulation as an integral feature of the UI. +Namely for interpolation between machines states recorded in a trace, and extrapolation into future machine states. +In other words, a connector for emulation is likely to be deprecated by our future work. + +## Contributing + +Whether submitting help tickets and pull requests, please tag those related to the debugger with "Debugger" so that we can triage them more quickly. + +To set up your environment, in addition to the usual Gradle tasks, process the Protobuf specification for GADP: + +```bash +gradle generateProto +``` + +If you already have an environment set up in Eclipse, please re-run `gradle prepDev eclipse` and import the new projects. +The Protobuf plugin for Gradle does not seem to export the generated source directory to the Eclipse project. +To remedy this, add `build/generated/source/proto/main/java` to the build path, and configure it to output to `bin/main`. \ No newline at end of file diff --git a/DevGuide.md b/DevGuide.md index f2d873ca80..73689108a9 100644 --- a/DevGuide.md +++ b/DevGuide.md @@ -21,6 +21,7 @@ - [Building Supporting Data](#building-supporting-data) * [Building Data Type Archives](#building-data-type-archives) * [Building FID Databases](#building-fid-databases) +- [Hacking on the Debugger](#hacking-on-the-debugger) ## Catalog of Dependencies @@ -375,4 +376,11 @@ Fill out the options appropriately and click OK. If you'd like some details of our fine tuning, take a look at `~/git/ghidra/Ghidra/Features/FunctionID/data/building_fid.txt`. +### Hacking on the Debugger + +The Debugger consists of multiple modules comprising its own collection of utilities, frameworks, and features. +There is plenty of new ground to be broken. +Before getting too deep into it, please see our dedicated [Debugger Developer's Guide][DbgGuide]. + [ghidra-data]: https://github.com/NationalSecurityAgency/ghidra-data +[DbgGuide]: DebuggerDevGuide.md \ No newline at end of file diff --git a/Ghidra/Debug/AnnotationValidator/Module.manifest b/Ghidra/Debug/AnnotationValidator/Module.manifest new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Ghidra/Debug/AnnotationValidator/build.gradle b/Ghidra/Debug/AnnotationValidator/build.gradle new file mode 100644 index 0000000000..b14c8e2449 --- /dev/null +++ b/Ghidra/Debug/AnnotationValidator/build.gradle @@ -0,0 +1,45 @@ +apply from: "${rootProject.projectDir}/gradle/javaProject.gradle" +apply from: "${rootProject.projectDir}/gradle/jacocoProject.gradle" +apply from: "${rootProject.projectDir}/gradle/javaTestProject.gradle" +apply from: "${rootProject.projectDir}/gradle/distributableGhidraModule.gradle" + +apply plugin: 'eclipse' +eclipse.project.name = 'Debug AnnotationValidator' + +dependencies { + compile project(':ProposedUtils') +} + +// no-dep jar for experiments in loading annotation processor into Eclipse +def boolean filterJar(File jarfile) { + if (jarfile.name.contains("gradle-api")) { + return false + } else if (jarfile.name.contains("groovy-all")) { + return false + } else if (jarfile.name.contains("gradle-installation-beacon")) { + return false + } + return true +} + +task configureNodepJar { + doLast { + configurations.runtime.files.forEach { + if (filterJar(it)) { + nodepJar.from(zipTree(it)) { + exclude("META-INF/**") + } + } + } + } +} + +task nodepJar(type: Jar) { + inputs.file(file(jar.archivePath)) + dependsOn(configureNodepJar) + dependsOn(jar) + + appendix = 'nodep' + + from(zipTree(jar.archivePath)) +} diff --git a/Ghidra/Debug/AnnotationValidator/certification.manifest b/Ghidra/Debug/AnnotationValidator/certification.manifest new file mode 100644 index 0000000000..7fad3499ec --- /dev/null +++ b/Ghidra/Debug/AnnotationValidator/certification.manifest @@ -0,0 +1,6 @@ +##VERSION: 2.0 +.classpath||NONE||reviewed||END| +.project||NONE||reviewed||END| +Module.manifest||GHIDRA||||END| +build.gradle||GHIDRA||||END| +src/main/resources/META-INF/services/javax.annotation.processing.Processor||GHIDRA||||END| diff --git a/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/AbstractDBAnnotationValidator.java b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/AbstractDBAnnotationValidator.java new file mode 100644 index 0000000000..8cdf8a422c --- /dev/null +++ b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/AbstractDBAnnotationValidator.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.database.annotproc; + +import java.lang.annotation.Annotation; + +import javax.lang.model.element.*; +import javax.tools.Diagnostic.Kind; + +public class AbstractDBAnnotationValidator { + protected final ValidationContext ctx; + + public AbstractDBAnnotationValidator(ValidationContext ctx) { + this.ctx = ctx; + } + + protected void checkEnclosingType(Class annotType, VariableElement field, + TypeElement type) { + if (type.getKind() != ElementKind.CLASS) { + ctx.messager.printMessage(Kind.ERROR, String.format( + "@%s can only be applied to fields in a class", annotType.getSimpleName()), field); + } + else if (!ctx.isSubclass(type, ctx.DB_ANNOTATED_OBJECT_ELEM)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("@%s can only be applied within a subclass of %s", + annotType.getSimpleName(), ctx.DB_ANNOTATED_OBJECT_ELEM), + field); + } + } +} diff --git a/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/AccessSpec.java b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/AccessSpec.java new file mode 100644 index 0000000000..8d07f21386 --- /dev/null +++ b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/AccessSpec.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.database.annotproc; + +import java.util.Set; + +import javax.lang.model.element.Modifier; + +public enum AccessSpec { + PRIVATE(0), PACKAGE(1), PROTECTED(2), PUBLIC(3); + + private final int level; + + private AccessSpec(int level) { + this.level = level; + } + + /** + * Checks if the second permits the same or more access than the first + * + * @param first the first + * @param second the second + * @return true if the second is the same or more permissive + */ + public static boolean isSameOrMorePermissive(AccessSpec first, AccessSpec second) { + // TODO: I'm not sure protected actually includes package... + // It might be more diamond shaped + return first.level <= second.level; + } + + /** + * Get the access specifier derived from the given modifiers + * + * @param modifiers the element's modifiers + * @return the elements access specification + */ + public static AccessSpec get(Set modifiers) { + if (modifiers.contains(Modifier.PRIVATE)) { + return PRIVATE; + } + if (modifiers.contains(Modifier.PROTECTED)) { + return PROTECTED; + } + if (modifiers.contains(Modifier.PUBLIC)) { + return PUBLIC; + } + return PACKAGE; + } +} diff --git a/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedColumnValidator.java b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedColumnValidator.java new file mode 100644 index 0000000000..a88c9e68af --- /dev/null +++ b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedColumnValidator.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.database.annotproc; + +import java.util.Set; + +import javax.lang.model.element.*; +import javax.tools.Diagnostic.Kind; + +import ghidra.util.database.annot.DBAnnotatedColumn; + +public class DBAnnotatedColumnValidator extends AbstractDBAnnotationValidator { + final VariableElement column; + + public DBAnnotatedColumnValidator(ValidationContext ctx, VariableElement column) { + super(ctx); + this.column = column; + } + + public void validate() { + if (!ctx.hasType(column, ctx.DB_OBJECT_COLUMN_ELEM)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("@%s can only be applied to fields of type %s", + DBAnnotatedColumn.class.getSimpleName(), ctx.DB_OBJECT_COLUMN_ELEM), + column); + } + Set mods = column.getModifiers(); + if (mods.contains(Modifier.FINAL)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("@%s cannot be applied to a final field", + DBAnnotatedColumn.class.getSimpleName()), + column); + } + if (!mods.contains(Modifier.STATIC)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("@%s must be applied to a static field", + DBAnnotatedColumn.class.getSimpleName()), + column); + } + TypeElement type = (TypeElement) column.getEnclosingElement(); + checkEnclosingType(DBAnnotatedColumn.class, column, type); + } +} diff --git a/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedFieldValidator.java b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedFieldValidator.java new file mode 100644 index 0000000000..46decb1d75 --- /dev/null +++ b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedFieldValidator.java @@ -0,0 +1,214 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.database.annotproc; + +import java.util.*; + +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.tools.Diagnostic.Kind; + +import db.DBHandle; +import ghidra.util.database.DBCachedDomainObjectAdapter; +import ghidra.util.database.DBOpenMode; +import ghidra.util.database.annot.DBAnnotatedField; +import ghidra.util.task.TaskMonitor; + +public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator { + final VariableElement field; + final Map javaToDBTypeMap; + final static String FACTORY_NAME = "ghidra.util.database.DBCachedObjectStoreFactory"; + final static String BOOLEAN_CODEC_NAME = FACTORY_NAME + ".BooleanDBFieldCodec"; + final static String BYTE_CODEC_NAME = FACTORY_NAME + ".ByteDBFieldCodec"; + final static String SHORT_CODEC_NAME = FACTORY_NAME + ".ShortDBFieldCodec"; + final static String INT_CODEC_NAME = FACTORY_NAME + ".IntDBFieldCodec"; + final static String LONG_CODEC_NAME = FACTORY_NAME + ".LongDBFieldCodec"; + final static String STRING_CODEC_NAME = FACTORY_NAME + ".StringDBFieldCodec"; + final static String BYTE_ARRAY_CODEC_NAME = FACTORY_NAME + ".ByteArrayDBFieldCodec"; + final static String LONG_ARRAY_CODEC_NAME = FACTORY_NAME + ".LongArrayDBFieldCodec"; + final static String ENUM_CODEC_NAME = FACTORY_NAME + ".EnumDBByteFieldCodec"; + + final TypeElement ENUM_CODEC_ELEM; + + public DBAnnotatedFieldValidator(ValidationContext ctx, VariableElement field) { + super(ctx); + this.field = field; + + Map typeMap = new LinkedHashMap<>(); + putPrimitiveTypeCodec(typeMap, TypeKind.BOOLEAN, BOOLEAN_CODEC_NAME); + putPrimitiveTypeCodec(typeMap, TypeKind.BYTE, BYTE_CODEC_NAME); + putPrimitiveTypeCodec(typeMap, TypeKind.SHORT, SHORT_CODEC_NAME); + putPrimitiveTypeCodec(typeMap, TypeKind.INT, INT_CODEC_NAME); + putPrimitiveTypeCodec(typeMap, TypeKind.LONG, LONG_CODEC_NAME); + putTypeCodec(typeMap, String.class, STRING_CODEC_NAME); + putPrimitiveArrayTypeCodec(typeMap, TypeKind.BYTE, BYTE_ARRAY_CODEC_NAME); + putPrimitiveArrayTypeCodec(typeMap, TypeKind.LONG, LONG_ARRAY_CODEC_NAME); + // NOTE: Enum requires subtype check + + javaToDBTypeMap = Map.copyOf(typeMap); + + ENUM_CODEC_ELEM = ctx.elementUtils.getTypeElement(ENUM_CODEC_NAME); + } + + protected void putPrimitiveTypeCodec(Map map, TypeKind kind, + String codecName) { + PrimitiveType primitive = ctx.typeUtils.getPrimitiveType(kind); + TypeMirror boxed = ctx.typeUtils.boxedClass(primitive).asType(); + TypeElement codec = ctx.elementUtils.getTypeElement(codecName); + map.put(primitive, codec); + map.put(boxed, codec); + } + + protected void putTypeCodec(Map map, Class cls, String codecName) { + TypeMirror type = ctx.elementUtils.getTypeElement(cls.getCanonicalName()).asType(); + TypeElement codec = ctx.elementUtils.getTypeElement(codecName); + map.put(type, codec); + } + + protected void putPrimitiveArrayTypeCodec(Map map, TypeKind kind, + String codecName) { + PrimitiveType primitive = ctx.typeUtils.getPrimitiveType(kind); + ArrayType array = ctx.typeUtils.getArrayType(primitive); + TypeElement codec = ctx.elementUtils.getTypeElement(codecName); + map.put(array, codec); + } + + public void validate() { + Set mods = field.getModifiers(); + if (mods.contains(Modifier.FINAL)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("@%s cannot be applied to a final field", + DBAnnotatedField.class.getSimpleName()), + field); + } + if (mods.contains(Modifier.STATIC)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("@%s cannot be applied to a static field", + DBAnnotatedField.class.getSimpleName()), + field); + } + TypeElement type = (TypeElement) field.getEnclosingElement(); + checkEnclosingType(DBAnnotatedField.class, field, type); + checkCodecTypes(type); + } + + protected TypeElement getDefaultCodecType(TypeMirror javaType) { + if (ctx.isEnumType(javaType)) { + return ENUM_CODEC_ELEM; + } + return javaToDBTypeMap.get(javaType); + } + + protected TypeElement getCodecTypeElement() { + DBAnnotatedField annotation = field.getAnnotation(DBAnnotatedField.class); + TypeElement codecElem; + try { + codecElem = ctx.elementUtils.getTypeElement(annotation.codec().getCanonicalName()); + } + catch (MirroredTypeException e) { + codecElem = (TypeElement) ((DeclaredType) e.getTypeMirror()).asElement(); + } + if (codecElem == ctx.DEFAULT_CODEC_ELEM) { + return getDefaultCodecType(field.asType()); + } + return codecElem; + } + + class A extends DBCachedDomainObjectAdapter { + + protected A(DBHandle dbh, DBOpenMode openMode, TaskMonitor monitor, String name, + int timeInterval, int bufSize, Object consumer) { + super(dbh, openMode, monitor, name, timeInterval, bufSize, consumer); + // TODO Auto-generated constructor stub + } + + @Override + public boolean isChangeable() { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getDescription() { + // TODO Auto-generated method stub + return null; + } + } + + protected void checkCodecTypes(TypeElement objectType) { + + //experiment(new Blargh(null, null)); + + TypeElement codecType = getCodecTypeElement(); + if (codecType == null) { + ctx.messager.printMessage(Kind.ERROR, + String.format("Could not select default codec for %s. @%s.codec must be specified.", + field.asType(), DBAnnotatedField.class.getSimpleName()), + field); + return; + } + + // REQUIREMENTS: + // 1) ValueType matches the field's type exactly + // Cannot be super or extends because it's read/write + // 2) ObjectType is super of the containing object + // Need to ensure extra interfaces (intersection) are considered + // 3) FieldType is non-abstract + // 4) The codec has an appropriate constructor + + for (Element enc : codecType.getEnclosedElements()) { + if (enc.getKind() == ElementKind.CONSTRUCTOR) { + ExecutableElement exe = (ExecutableElement) enc; + ExecutableType exeType = (ExecutableType) exe.asType(); + //throw new RuntimeException(); + } + } + + Map args = ctx.getArguments(codecType, ctx.DB_FIELD_CODEC_ELEM); + + // 1) + TypeMirror argVT = args.get("VT"); + if (!ctx.hasType(field, argVT)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("Codec %s can only be used with fields of type %s", codecType, argVT), + field); + } + + // 2) (INCOMPLETE) + TypeMirror argOT = args.get("OT"); + if (!ctx.isCapturable(objectType.asType(), argOT)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("Codec %s requires the containing object to conform to %s", codecType, + ctx.format(argOT)), + field); + } + + // 3) + TypeMirror argFT = args.get("FT"); + if (argFT.getKind() != TypeKind.DECLARED) { + ctx.messager.printMessage(Kind.ERROR, + String.format("Codec %s must have a non-abstract class for its field type, not %s", + codecType, argFT), + codecType); + } + else if (((DeclaredType) argFT).asElement().getModifiers().contains(Modifier.ABSTRACT)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("Codec %s must have a non-abstract class for its field type, not %s", + codecType, argFT), + codecType); + } + } +} diff --git a/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedObjectProcessor.java b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedObjectProcessor.java new file mode 100644 index 0000000000..314bb1ad3b --- /dev/null +++ b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedObjectProcessor.java @@ -0,0 +1,96 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.database.annotproc; + +import java.lang.annotation.Annotation; +import java.util.*; +import java.util.stream.Collectors; + +import javax.annotation.processing.*; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.*; + +import ghidra.util.database.DBAnnotatedObject; +import ghidra.util.database.annot.*; + +/** + * A compile-time annotation processor for {@link DBAnnotatedObject}-related annotations. + * + * Currently just performs compile-time checks. It does not generate any code, but perhaps one day, + * it will. + */ +//@AutoService(Processor.class) // TODO: Evaluate Google's auto-service as a dependency +public class DBAnnotatedObjectProcessor extends AbstractProcessor { + static final Set> SUPPORTED_ANNOTATIONS = + Set.of(DBAnnotatedColumn.class, DBAnnotatedField.class, DBAnnotatedObjectInfo.class); + + private ValidationContext ctx; + + @Override + public synchronized void init(ProcessingEnvironment env) { + //System.err.println("HERE4"); + super.init(env); + ctx = new ValidationContext(env); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + Map types = new LinkedHashMap<>(); + for (Element element : roundEnv.getElementsAnnotatedWith(DBAnnotatedObjectInfo.class)) { + TypeElement type = (TypeElement) element; // Required by annotation Target + types.put(type, new DBAnnotatedObjectValidator(ctx, type)); + } + for (Element field : roundEnv.getElementsAnnotatedWith(DBAnnotatedField.class)) { + VariableElement varField = (VariableElement) field; // Required by annotation Target + // Fields can only be members of types, right? + TypeElement type = (TypeElement) field.getEnclosingElement(); + DBAnnotatedObjectValidator validator = + types.computeIfAbsent(type, t -> new DBAnnotatedObjectValidator(ctx, type)); + validator.addAnnotatedField(varField); + } + for (Element column : roundEnv.getElementsAnnotatedWith(DBAnnotatedColumn.class)) { + VariableElement varColumn = (VariableElement) column; // Required by annotation Target + // Fields can only be members of types, right? + TypeElement type = (TypeElement) column.getEnclosingElement(); + DBAnnotatedObjectValidator validator = + types.computeIfAbsent(type, t -> new DBAnnotatedObjectValidator(ctx, type)); + validator.addAnnotatedColumn(varColumn); + } + + for (DBAnnotatedObjectValidator ov : types.values()) { + ov.validate(); + } + return true; + } + + @Override + public Iterable getCompletions(Element element, + AnnotationMirror annotation, ExecutableElement member, String userText) { + // TODO Auto-generated method stub + return super.getCompletions(element, annotation, member, userText); + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + @Override + public Set getSupportedAnnotationTypes() { + return SUPPORTED_ANNOTATIONS.stream().map(Class::getCanonicalName).collect( + Collectors.toSet()); + } +} diff --git a/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedObjectValidator.java b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedObjectValidator.java new file mode 100644 index 0000000000..6de69c6af6 --- /dev/null +++ b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedObjectValidator.java @@ -0,0 +1,137 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.database.annotproc; + +import java.util.*; + +import javax.lang.model.element.*; +import javax.tools.Diagnostic.Kind; + +import ghidra.util.database.annot.*; + +public class DBAnnotatedObjectValidator { + private final ValidationContext ctx; + private final TypeElement type; + private final Map fieldsByName = new LinkedHashMap<>(); + private final Map columnsByName = new LinkedHashMap<>(); + + public DBAnnotatedObjectValidator(ValidationContext ctx, TypeElement type) { + this.ctx = ctx; + this.type = type; + } + + public void addAnnotatedField(VariableElement field) { + DBAnnotatedField annotation = field.getAnnotation(DBAnnotatedField.class); + assert annotation != null; + fieldsByName.put(annotation.column(), new DBAnnotatedFieldValidator(ctx, field)); + } + + public void addAnnotatedColumn(VariableElement column) { + DBAnnotatedColumn annotation = column.getAnnotation(DBAnnotatedColumn.class); + assert annotation != null; + columnsByName.put(annotation.value(), new DBAnnotatedColumnValidator(ctx, column)); + } + + public void validate() { + DBAnnotatedObjectInfo annotation = type.getAnnotation(DBAnnotatedObjectInfo.class); + if (annotation != null && type.getKind() != ElementKind.CLASS) { + ctx.messager.printMessage(Kind.ERROR, + String.format("@%s cannot be applied to an interface", + DBAnnotatedObjectInfo.class.getSimpleName()), + type); + } + else if (annotation != null && type.getModifiers().contains(Modifier.ABSTRACT)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("@%s cannot be applied to an abstract class", + DBAnnotatedObjectInfo.class.getSimpleName()), + type); + } + if (annotation != null && !ctx.isSubclass(type, ctx.DB_ANNOTATED_OBJECT_ELEM)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("@%s can only be applied to subclasses of %s", "DBAnnotatedObject", + DBAnnotatedObjectInfo.class.getSimpleName())); + } + if (annotation == null && !type.getModifiers().contains(Modifier.ABSTRACT)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("Non-abstract subclasses of %s must have @%s annotation", + "DBAnnotatedObject", DBAnnotatedObjectInfo.class.getSimpleName()), + type); + } + if (annotation != null && annotation.version() < 0) { + ctx.messager.printMessage(Kind.ERROR, String.format("@%s.version cannot be negative", + DBAnnotatedObjectInfo.class.getSimpleName()), type); + } + + validateFields(); + validateColumns(); + + checkMissing(); + } + + protected void validateFields() { + for (DBAnnotatedFieldValidator fv : fieldsByName.values()) { + fv.validate(); + } + } + + protected void validateColumns() { + for (DBAnnotatedColumnValidator cv : columnsByName.values()) { + cv.validate(); + } + } + + protected void checkMissing() { + Set names = new LinkedHashSet<>(); + names.addAll(fieldsByName.keySet()); + names.addAll(columnsByName.keySet()); + for (String n : names) { + DBAnnotatedFieldValidator fv = fieldsByName.get(n); + DBAnnotatedColumnValidator cv = columnsByName.get(n); + if (fv == null && cv != null && !type.getModifiers().contains(Modifier.ABSTRACT)) { + ctx.messager.printMessage(Kind.ERROR, + String.format("@%s is missing corresponding @%s of the same column name: %s", + DBAnnotatedColumn.class.getSimpleName(), + DBAnnotatedField.class.getSimpleName(), n), + cv.column); + } + if (fv != null && cv == null && !type.getModifiers().contains(Modifier.ABSTRACT)) { + ctx.messager.printMessage(Kind.WARNING, + String.format("@%s is missing corresponding @%s of the same column name: %s", + DBAnnotatedField.class.getSimpleName(), + DBAnnotatedColumn.class.getSimpleName(), n), + fv.field); + } + if (fv != null && cv != null) { + checkAccess(fv.field, cv.column, n); + } + } + + } + + protected void checkAccess(VariableElement field, VariableElement column, String name) { + AccessSpec fieldSpec = AccessSpec.get(field.getModifiers()); + AccessSpec columnSpec = AccessSpec.get(column.getModifiers()); + if (!AccessSpec.isSameOrMorePermissive(fieldSpec, columnSpec)) { + ctx.messager.printMessage(Kind.WARNING, + String.format( + "field with @%s should have same or greater access than field with" + + " corresponding @%s for column name: %s", + DBAnnotatedColumn.class.getSimpleName(), DBAnnotatedField.class.getSimpleName(), + name), + column); + } + } +} diff --git a/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/ValidationContext.java b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/ValidationContext.java new file mode 100644 index 0000000000..292d810a7d --- /dev/null +++ b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/ValidationContext.java @@ -0,0 +1,326 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.database.annotproc; + +import java.util.*; + +import javax.annotation.processing.Messager; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; + +import ghidra.util.database.annot.DBAnnotatedField; + +public class ValidationContext { + final Types typeUtils; + final Elements elementUtils; + final Messager messager; + + final TypeElement LIST_ELEM; + final TypeElement DB_ANNOTATED_OBJECT_ELEM; + final TypeElement DB_OBJECT_COLUMN_ELEM; + final TypeElement DB_FIELD_CODEC_ELEM; + final TypeElement DEFAULT_CODEC_ELEM; + final TypeElement ENUM_ELEM; + + public ValidationContext(ProcessingEnvironment env) { + typeUtils = env.getTypeUtils(); + elementUtils = env.getElementUtils(); + messager = env.getMessager(); + + LIST_ELEM = elementUtils.getTypeElement(List.class.getCanonicalName()); + DB_ANNOTATED_OBJECT_ELEM = + elementUtils.getTypeElement("ghidra.util.database.DBAnnotatedObject"); + DB_OBJECT_COLUMN_ELEM = elementUtils.getTypeElement("ghidra.util.database.DBObjectColumn"); + DB_FIELD_CODEC_ELEM = elementUtils.getTypeElement( + "ghidra.util.database.DBCachedObjectStoreFactory.DBFieldCodec"); + DEFAULT_CODEC_ELEM = elementUtils.getTypeElement( + DBAnnotatedField.class.getCanonicalName() + ".DefaultCodec"); + ENUM_ELEM = elementUtils.getTypeElement(Enum.class.getCanonicalName()); + } + + public boolean isSubclass(TypeElement t1, TypeElement t2) { + return typeUtils.isSubtype(typeUtils.erasure(t1.asType()), typeUtils.erasure(t2.asType())); + } + + public boolean hasType(VariableElement field, TypeElement type) { + return hasType(field, type.asType()); + } + + public boolean hasType(VariableElement field, TypeMirror type) { + TypeMirror fieldType = field.asType(); + try { + PrimitiveType unboxed = typeUtils.unboxedType(type); + if (typeUtils.isSameType(fieldType, unboxed)) { + return true; + } + } + catch (IllegalArgumentException e) { + // Eh, I guess it's not unboxable + } + + if (fieldType.getKind() == TypeKind.DECLARED) { + DeclaredType declType = (DeclaredType) fieldType; + if (isSubclass((TypeElement) declType.asElement(), ENUM_ELEM)) { + Map enumArgs = getArguments(declType, ENUM_ELEM); + TypeMirror argE = enumArgs.get("E"); + if (typeUtils.isSameType(declType, argE)) { + return true; + } + } + } + + return typeUtils.isAssignable(fieldType, type); +// return typeUtils.isSameType(fieldType, type); + } + + public boolean isCapturable(TypeMirror t1, TypeMirror t2) { + // TODO: This only works for typevar at top level... + // TODO: Need to figure out how to check for capture and check + if (t2.getKind() == TypeKind.TYPEVAR) { + TypeVariable v2 = (TypeVariable) t2; + if (!typeUtils.isSubtype(t1, v2.getUpperBound())) { + return false; + } + if (!typeUtils.isSubtype(v2.getLowerBound(), t1)) { + return false; + } + return true; + } + return typeUtils.isSubtype(t1, t2); + } + + public boolean isEnumType(TypeMirror t) { + if (t.getKind() != TypeKind.DECLARED) { + return false; + } + DeclaredType enumType = typeUtils.getDeclaredType(ENUM_ELEM, t); + return typeUtils.isSubtype(t, enumType); + } + + protected DeclaredType findSupertype(Set types, TypeElement superType) { + Set next; + while (!types.isEmpty()) { + next = new HashSet<>(); + for (DeclaredType t : types) { + List supers = typeUtils.directSupertypes(t); + for (TypeMirror s : supers) { + DeclaredType ds = (DeclaredType) s; + if (superType == ds.asElement()) { + return ds; + } + next.add(ds); + } + } + types = next; + } + return null; + } + + public DeclaredType findSupertype(DeclaredType type, TypeElement superElem) { + return findSupertype(Set.of(type), superElem); + } + + public DeclaredType findSupertype(TypeElement elem, TypeElement superElem) { + return findSupertype((DeclaredType) elem.asType(), superElem); + } + + protected Map toArgsMap(TypeElement superElem, DeclaredType superType) { + List typeParameters = superElem.getTypeParameters(); + List typeArguments = superType.getTypeArguments(); + assert typeParameters.size() == typeArguments.size(); + Map result = new HashMap<>(); + for (int i = 0; i < typeParameters.size(); i++) { + result.put(typeParameters.get(i).getSimpleName().toString(), typeArguments.get(i)); + } + return result; + } + + public Map getArguments(DeclaredType type, TypeElement superElem) { + return toArgsMap(superElem, findSupertype(type, superElem)); + } + + public Map getArguments(TypeElement elem, TypeElement superElem) { + return toArgsMap(superElem, findSupertype(elem, superElem)); + } + + public String format(TypeMirror type) { + FormatVisitor vis = new FormatVisitor(); + type.accept(vis, null); + return vis.buf.toString(); + } +} + +class FormatVisitor implements TypeVisitor { + StringBuffer buf = new StringBuffer(); + + @Override + public Void visit(TypeMirror t, Void p) { + switch (t.getKind()) { + case ARRAY: + return visitArray((ArrayType) t, p); + case BOOLEAN: + case BYTE: + case CHAR: + case DOUBLE: + case FLOAT: + case INT: + case LONG: + case SHORT: + case VOID: + return visitPrimitive((PrimitiveType) t, p); + case DECLARED: + return visitDeclared((DeclaredType) t, p); + case ERROR: + return visitError((ErrorType) t, p); + case EXECUTABLE: + return visitExecutable((ExecutableType) t, p); + case INTERSECTION: + return visitIntersection((IntersectionType) t, p); + case NONE: + return visitNoType((NoType) t, p); + case NULL: + return visitNull((NullType) t, p); + case TYPEVAR: + return visitTypeVariable((TypeVariable) t, p); + case UNION: + return visitUnion((UnionType) t, p); + case WILDCARD: + return visitWildcard((WildcardType) t, p); + default: + return visitUnknown(t, p); + } + } + + @Override + public Void visitPrimitive(PrimitiveType t, Void p) { + buf.append(t.toString()); + return null; + } + + @Override + public Void visitNull(NullType t, Void p) { + buf.append(t.toString()); + return null; + } + + @Override + public Void visitArray(ArrayType t, Void p) { + visit(t.getComponentType()); + buf.append("[]"); + return null; + } + + @Override + public Void visitDeclared(DeclaredType t, Void p) { + buf.append(t.asElement().toString()); + Iterator it = t.getTypeArguments().iterator(); + if (it.hasNext()) { + buf.append("<"); + visit(it.next()); + while (it.hasNext()) { + buf.append(", "); + visit(it.next()); + } + buf.append(">"); + } + return null; + } + + @Override + public Void visitError(ErrorType t, Void p) { + buf.append(t.toString()); + return null; + } + + @Override + public Void visitTypeVariable(TypeVariable t, Void p) { + buf.append(t.toString()); + TypeMirror lower = t.getLowerBound(); + if (lower.getKind() != TypeKind.NULL) { + buf.append(" super "); + visit(lower); + } + TypeMirror upper = t.getUpperBound(); + if (!upper.toString().equals("java.lang.Object")) { + buf.append(" extends "); + visit(upper); + } + return null; + } + + @Override + public Void visitWildcard(WildcardType t, Void p) { + buf.append("?"); + TypeMirror sup = t.getSuperBound(); + if (sup != null) { + buf.append(" super "); + visit(sup); + } + TypeMirror ext = t.getExtendsBound(); + if (ext != null) { + buf.append(" extends "); + visit(ext); + } + return null; + } + + @Override + public Void visitExecutable(ExecutableType t, Void p) { + buf.append(t.toString()); + return null; + } + + @Override + public Void visitNoType(NoType t, Void p) { + buf.append(t.toString()); + return null; + } + + @Override + public Void visitUnknown(TypeMirror t, Void p) { + buf.append(t.toString()); + return null; + } + + @Override + public Void visitUnion(UnionType t, Void p) { + Iterator it = t.getAlternatives().iterator(); + if (it.hasNext()) { + visit(it.next()); + while (it.hasNext()) { + buf.append(" | "); + visit(it.next()); + } + } + return null; + } + + @Override + public Void visitIntersection(IntersectionType t, Void p) { + Iterator it = t.getBounds().iterator(); + if (it.hasNext()) { + visit(it.next()); + while (it.hasNext()) { + buf.append(" & "); + visit(it.next()); + } + } + return null; + } +} diff --git a/Ghidra/Debug/AnnotationValidator/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/Ghidra/Debug/AnnotationValidator/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000000..811b3341ac --- /dev/null +++ b/Ghidra/Debug/AnnotationValidator/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +ghidra.util.database.annotproc.DBAnnotatedObjectProcessor diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/Module.manifest b/Ghidra/Debug/Debugger-agent-dbgeng/Module.manifest new file mode 100644 index 0000000000..8f0afe45a1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/Module.manifest @@ -0,0 +1,2 @@ +MODULE FILE LICENSE: lib/jna-5.4.0.jar Apache License 2.0 +MODULE FILE LICENSE: lib/jna-platform-5.4.0.jar Apache License 2.0 diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/build.gradle b/Ghidra/Debug/Debugger-agent-dbgeng/build.gradle new file mode 100644 index 0000000000..17fe849429 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/build.gradle @@ -0,0 +1,66 @@ +apply from: "$rootProject.projectDir/gradle/javaProject.gradle" +apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle" +apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" +apply from: "$rootProject.projectDir/gradle/nativeProject.gradle" +apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" +apply plugin: 'eclipse' + +eclipse.project.name = 'Debug Debugger-agent-dbgeng' + +dependencies { + compile project(":Framework-AsyncComm") + compile project(":Framework-Debugging") + compile project(":Debugger-gadp") + compile "net.java.dev.jna:jna:5.4.0" + compile "net.java.dev.jna:jna-platform:5.4.0" + + //testCompile project(":Base") + testCompile project(path: ":Debugger-gadp", configuration: 'testArtifacts') +} + +def boolean filterJar(File jarfile) { + if (jarfile.name.contains("gradle-api")) { + return false + } else if (jarfile.name.contains("groovy-all")) { + return false + } else if (jarfile.name.contains("gradle-installation-beacon")) { + return false + } + return true +} + +jar { + manifest { + attributes['Main-Class'] = 'agent.dbgeng.gadp.DbgEngGadpServer' + } +} + +task configureNodepJar { + doLast { + configurations.runtime.files.forEach { + if (filterJar(it)) { + nodepJar.from(zipTree(it)) + } + } + } +} + +task nodepJar(type: Jar) { + inputs.file(file(jar.archivePath)) + dependsOn(configureNodepJar) + dependsOn(jar) + + appendix = 'nodep' + manifest { + attributes['Main-Class'] = 'agent.dbgeng.gadp.DbgEngGadpServer' + } + + from(zipTree(jar.archivePath)) +} + +test { + jvmArgs('-Xrs') // TODO: Is this needed, or left over from trial-and-error + if ("win64".equals(getCurrentPlatformName())) { + dependsOn(":Framework-Debugging:testSpecimenWin64") + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/certification.manifest b/Ghidra/Debug/Debugger-agent-dbgeng/certification.manifest new file mode 100644 index 0000000000..fc6bcc9c98 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/certification.manifest @@ -0,0 +1,8 @@ +##VERSION: 2.0 +##MODULE IP: Apache License 2.0 +.classpath||NONE||reviewed||END| +.project||NONE||reviewed||END| +Module.manifest||GHIDRA||||END| +build.gradle||GHIDRA||||END| +src/javaprovider/def/javaprovider.def||GHIDRA||||END| +src/javaprovider/rc/javaprovider.rc||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/cpp/javaprovider.cpp b/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/cpp/javaprovider.cpp new file mode 100644 index 0000000000..2bb02ec96b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/cpp/javaprovider.cpp @@ -0,0 +1,224 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define INITGUID + +#include +#include + +#include "resource.h" + +#define CHECK_RESULT(x, y) do { \ + HRESULT hr = (x); \ + if (hr != S_OK) { \ + fprintf(stderr, "HRESULT of %s = %x\n", ##x, hr); \ + return y; \ + } \ +} while (0) + +class EXT_CLASS : public ExtExtension { +public: + virtual HRESULT Initialize(); + virtual void Uninitialize(); + + //virtual void OnSessionAccessible(ULONG64 Argument); + + EXT_COMMAND_METHOD(java_add_cp); + EXT_COMMAND_METHOD(java_set); + EXT_COMMAND_METHOD(java_get); + EXT_COMMAND_METHOD(java_run); + + void run_command(PCSTR name); +}; + +EXT_DECLARE_GLOBALS(); + +JavaVM* jvm = NULL; +JNIEnv* env = NULL; +jclass clsCommands = NULL; + +char JDK_JVM_DLL_PATH[] = "\\jre\\bin\\server\\jvm.dll"; +char JRE_JVM_DLL_PATH[] = "\\bin\\server\\jvm.dll"; + +typedef jint (_cdecl *CreateJavaVMFunc)(JavaVM**, void**, void*); + +HRESULT EXT_CLASS::Initialize() { + HRESULT result = ExtExtension::Initialize(); + if (result != S_OK) { + return result; + } + + char* env_java_home = getenv("JAVA_HOME"); + if (env_java_home == NULL) { + fprintf(stderr, "JAVA_HOME is not set\n"); + fflush(stderr); + return E_FAIL; + } + char* java_home = strdup(env_java_home); + size_t home_len = strlen(java_home); + if (java_home[home_len - 1] == '\\') { + java_home[home_len - 1] = '\0'; + } + size_t full_len = home_len + sizeof(JDK_JVM_DLL_PATH); + char* full_path = new char[full_len]; + HMODULE jvmDll = NULL; + // Try the JRE path first; + strcpy_s(full_path, full_len, java_home); + strcat_s(full_path, full_len, JRE_JVM_DLL_PATH); + fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path); + fflush(stderr); + jvmDll = LoadLibraryA(full_path); + if (jvmDll == NULL) { + // OK, then try the JDK path + strcpy_s(full_path, full_len, java_home); + strcat_s(full_path, full_len, JDK_JVM_DLL_PATH); + fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path); + fflush(stderr); + jvmDll = LoadLibraryA(full_path); + } + free(full_path); + free(java_home); + if (jvmDll == NULL) { + fprintf(stderr, "Could not find the jvm.dll\n"); + fflush(stderr); + return E_FAIL; + } + fprintf(stderr, "Found it!\n"); + fflush(stderr); + + JavaVMOption options[2]; + JavaVMInitArgs vm_args = { 0 }; + vm_args.version = JNI_VERSION_1_8; + vm_args.nOptions = sizeof(options)/sizeof(options[0]); + vm_args.options = options; + options[0].optionString = "-Xrs"; + options[1].optionString = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"; + vm_args.ignoreUnrecognized = false; + CreateJavaVMFunc create_jvm = NULL; + //create_jvm = JNI_CreateJavaVM; + create_jvm = (CreateJavaVMFunc) GetProcAddress(jvmDll, "JNI_CreateJavaVM"); + jint jni_result = create_jvm(&jvm, (void**)&env, &vm_args); + + if (jni_result != JNI_OK) { + jvm = NULL; + fprintf(stderr, "Could not initialize JVM: %d: ", jni_result); + switch (jni_result) { + case JNI_ERR: + fprintf(stderr, "unknown error"); + break; + case JNI_EDETACHED: + fprintf(stderr, "thread detached from the VM"); + break; + case JNI_EVERSION: + fprintf(stderr, "JNI version error"); + break; + case JNI_ENOMEM: + fprintf(stderr, "not enough memory"); + break; + case JNI_EEXIST: + fprintf(stderr, "VM already created"); + break; + case JNI_EINVAL: + fprintf(stderr, "invalid arguments"); + break; + } + fprintf(stderr, "\n"); + fflush(stderr); + return E_FAIL; + } + + HMODULE hJavaProviderModule = GetModuleHandle(TEXT("javaprovider")); + HRSRC resCommandsClassfile = FindResource(hJavaProviderModule, MAKEINTRESOURCE(IDR_CLASSFILE1), TEXT("Classfile")); + HGLOBAL gblCommandsClassfile = LoadResource(hJavaProviderModule, resCommandsClassfile); + LPVOID lpCommandsClassfile = LockResource(gblCommandsClassfile); + DWORD szCommandsClassfile = SizeofResource(hJavaProviderModule, resCommandsClassfile); + + clsCommands = env->DefineClass( + "javaprovider/Commands", NULL, (jbyte*) lpCommandsClassfile, szCommandsClassfile + ); + if (clsCommands == NULL) { + fprintf(stderr, "Could not define Commands class\n"); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return E_FAIL; + } + } + + return S_OK; +} + +void EXT_CLASS::Uninitialize() { + if (jvm != NULL) { + jvm->DestroyJavaVM(); + } + ExtExtension::Uninitialize(); +} + +void EXT_CLASS::run_command(PCSTR name) { + // TODO: Throw an exception during load, then! + if (jvm == NULL) { + Out("javaprovider extension did not load properly.\n"); + return; + } + if (clsCommands == NULL) { + Out("javaprovider extension did not load properly.\n"); + return; + } + + PCSTR args = GetRawArgStr(); + + jmethodID mthCommand = env->GetStaticMethodID(clsCommands, name, "(Ljava/lang/String;)V"); + if (mthCommand == NULL) { + Out("INTERNAL ERROR: No such command: %s\n", name); + return; + } + + jstring argsStr = env->NewStringUTF(args); + if (argsStr == NULL) { + Out("Could not create Java string for arguments.\n"); + return; + } + + env->CallStaticVoidMethod(clsCommands, mthCommand, argsStr); + env->DeleteLocalRef(argsStr); + if (env->ExceptionCheck()) { + Out("Exception during javaprovider command:\n"); + env->ExceptionDescribe(); // TODO: Send this to output callbacks, not console. + env->ExceptionClear(); + } +} + +EXT_COMMAND(java_add_cp, "Add an element to the class path", "{{custom}}") { + run_command("java_add_cp"); +} + +EXT_COMMAND(java_set, "Set a Java system property", "{{custom}}") { + run_command("java_set"); +} + +EXT_COMMAND(java_get, "Get a Java system property", "{{custom}}") { + run_command("java_get"); +} + +EXT_COMMAND(java_run, "Execute the named java class", "{{custom}}") { + run_command("java_run"); +} + +#define JNA extern "C" __declspec(dllexport) + +JNA HRESULT createClient(PDEBUG_CLIENT* client) { + return g_ExtInstance.m_Client->CreateClient(client); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/def/javaprovider.def b/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/def/javaprovider.def new file mode 100644 index 0000000000..197aee0bbd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/def/javaprovider.def @@ -0,0 +1,13 @@ +EXPORTS + +; For ExtCpp + DebugExtensionInitialize + DebugExtensionUninitialize + DebugExtensionNotify + help + +; My Commands + java_add_cp + java_set + java_get + java_run diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/headers/afxres.h b/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/headers/afxres.h new file mode 100644 index 0000000000..853b8950c1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/headers/afxres.h @@ -0,0 +1,16 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/headers/resource.h b/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/headers/resource.h new file mode 100644 index 0000000000..39e5f2e8e5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/headers/resource.h @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by javaprovider.rc +// +#define IDR_CLASSFILE1 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/rc/javaprovider.rc b/Ghidra/Debug/Debugger-agent-dbgeng/src/javaprovider/rc/javaprovider.rc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/DbgEngInJvmDebuggerModelFactory.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/DbgEngInJvmDebuggerModelFactory.java new file mode 100644 index 0000000000..bc7b058fb5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/DbgEngInJvmDebuggerModelFactory.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.impl.DbgModelImpl; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.LocalDebuggerModelFactory; +import ghidra.dbg.util.ConfigurableFactory.FactoryDescription; +import ghidra.util.classfinder.ExtensionPointProperties; + +/** + * Note this is in the testing source because it's not meant to be shipped in the release.... That + * may change if it proves stable, though, no? + */ +@FactoryDescription( // + brief = "IN-VM MS dbgeng local debugger", // + htmlDetails = "Launch a dbgeng session in this same JVM" // +) +@ExtensionPointProperties(priority = 80) +public class DbgEngInJvmDebuggerModelFactory implements LocalDebuggerModelFactory { + + @Override + public CompletableFuture build() { + DbgModelImpl model = new DbgModelImpl(); + return model.startDbgEng(new String[] {}).thenApply(__ -> model); + } + + @Override + public boolean isCompatible() { + return System.getProperty("os.name").toLowerCase().contains("windows"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/COMUtilsExtra.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/COMUtilsExtra.java new file mode 100644 index 0000000000..e194eb3bb4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/COMUtilsExtra.java @@ -0,0 +1,72 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMException; +import com.sun.jna.platform.win32.COM.COMUtils; + +/** + * Utilities for interacting with Microsoft COM objects beyond those provided by {@link COMUtils}. + * + * See the MSDN for details on the meanings of the return codes for the function or method of + * interest. + */ +public interface COMUtilsExtra { + + public static HRESULT E_UNEXPECTED = new HRESULT(0x8000FFFF); + public static HRESULT E_BOUNDS = new HRESULT(0x8000000B); + public static HRESULT E_NOTIMPLEMENTED = new HRESULT(0x80004001); + public static HRESULT E_NOINTERFACE = new HRESULT(0x80004002); + public static HRESULT E_COM_EXC = new HRESULT(0x80004003); + public static HRESULT E_FAIL = new HRESULT(0x80004005); + public static HRESULT E_CANTCALLOUT_INASYNCCALL = new HRESULT(0x80010004); + public static HRESULT E_INTERNALEXCEPTION = new HRESULT(0x80040205); + public static HRESULT E_ACCESS_DENIED = new HRESULT(0x80070005); + public static HRESULT E_CANNOT_READ = new HRESULT(0x8007001E); + public static HRESULT E_INVALID_PARAM = new HRESULT(0x80070057); + public static HRESULT E_SCOPE_NOT_FOUND = new HRESULT(0x8007013E); + + /** + * Check if the given exception represents an {@code E_NOINTERFACE} result + * + * @param e the exception + * @return true if {@code E_NOINTERFACE} + */ + static boolean isE_NOINTERFACE(COMException e) { + return E_NOINTERFACE.equals(e.getHresult()); + } + + /** + * Check if the given exception represents an {@code E_UNEXPECTED} result + * + * @param e the exception + * @return true if {@code E_UNEXPECTED} + */ + static boolean isE_UNEXPECTED(COMException e) { + return E_UNEXPECTED.equals(e.getHresult()); + } + + /** + * Check if the given exception represents an {@code E_INTERNALEXCEPTION} result + * + * @param e the exception + * @return true if {@code E_INTERNALEXCEPTION} + */ + static boolean isE_INTERNALEXCEPTION(COMException e) { + return E_INTERNALEXCEPTION.equals(e.getHresult()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DbgEng.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DbgEng.java new file mode 100644 index 0000000000..bc90b1b3d1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DbgEng.java @@ -0,0 +1,163 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import java.lang.ref.Cleaner; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Kernel32Util; +import com.sun.jna.platform.win32.WinDef.DWORD; +import com.sun.jna.platform.win32.WinNT.HANDLE; +import com.sun.jna.platform.win32.COM.IUnknown; + +import agent.dbgeng.impl.dbgeng.client.DebugClientInternal; +import agent.dbgeng.jna.dbgeng.DbgEngNative; +import ghidra.comm.util.BitmaskSet; +import ghidra.util.Msg; + +/** + * A wrapper for Microsoft's {@code dbgeng.dll} that presents a Java-friendly interface. + * + * This is the "root interface" from which all other interfaces to {@code dbgeng.dll} are generated. + * Not every method listed in the documentation, nor every method present in the header, is + * implemented. Only those that were necessary to implement the SCTL adapter. However, the class and + * interface hierarchy was designed so that adding the remaining methods should be fairly + * straightforward. This wrapper attempts to obtain the most capable COM interfaces for the debug + * client that it knows. Again, newer interfaces should be fairly straightforward to add. + * + * Methods that are "obviously" wrappers for a COM method are left undocumented, unless there is + * some nuance to how it has been wrapped. In many cases, a parameter which is an integer in the COM + * method may be presented as an {@code enum} or {@link BitmaskSet} by the wrapper. Consult the MSDN + * for the meaning of the various values and bit flags. + * + * Each wrapper interface is implemented by several COM interface wrappers: one for each known COM + * interface version. The wrapper is optimistic, in that it declares wrapper methods even for COM + * methods that are only available in later versions. The implementations limited to earlier COM + * interfaces should either emulate the operation, or throw an + * {@link UnsupportedOperationException}. Where a newer method is provided by a newer interface, a + * wrapper implementation should prefer the latest. For example, one series of interfaces introduces + * {@code *Wide} variants of existing methods. Since Java also uses a UTF-16-like string encoding + * internally, JNA permits wide strings to be passed by reference. Thus, the wide variant is always + * preferred. + * + * Pay careful attention to the threading requirements imposed by {@code dbgeng.dll} these can be + * found in the MSDN. As a general rule of thumb, if the method is reentrant (i.e., it can be called + * from any thread), it is declared in the {@code *Reentrant} variant of the wrapper interface. + * There are few of these. Unless the documentation explicitly lists the method as reentrant, do not + * declare it there. Many methods appear to execute successfully from the wrong thread, but cause + * latent issues. A practice to prevent accidental use of non-reentrant methods outside of the + * client's owning thread is to ensure that only the owning thread can see the full interface. All + * other threads should only have access to the reentrant interface. + * + * If you implement methods that introduce a new callback class, use the existing callback type + * hierarchies as a model. There are many classes to implement. Furthermore, be sure to keep a + * reference to any active callback instances within the wrapper that uses them. The JNA has no way + * of knowing whether or not the instance is still being used by the external C/C++ library. If you + * do not store a reference, the JVM will think it's garbage and free it, even though COM is still + * using it. Drop the reference only when you are certain nothing external has a reference to it. + */ +public class DbgEng { + private static final Cleaner CLEANER = Cleaner.create(); + + private static class ReleaseCOMObject implements Runnable { + private final IUnknown obj; + + ReleaseCOMObject(IUnknown obj) { + this.obj = obj; + } + + @Override + public void run() { + Msg.debug(this, "Releasing COM object: " + obj); + obj.Release(); + } + } + + private static class ReleaseHANDLE implements Runnable { + private final HANDLE handle; + + public ReleaseHANDLE(HANDLE handle) { + this.handle = handle; + } + + @Override + public void run() { + Kernel32Util.closeHandle(handle); + } + } + + public static class OpaqueCleanable { + @SuppressWarnings("unused") // A reference to control GC + private final Object state; + @SuppressWarnings("unused") // A reference to control GC + private final Cleaner.Cleanable cleanable; + + public OpaqueCleanable(Object state, Cleaner.Cleanable cleanable) { + this.state = state; + this.cleanable = cleanable; + } + } + + public static OpaqueCleanable releaseWhenPhantom(Object owner, IUnknown obj) { + ReleaseCOMObject state = new ReleaseCOMObject(obj); + return new OpaqueCleanable(state, CLEANER.register(owner, state)); + } + + public static OpaqueCleanable releaseWhenPhantom(Object owner, HANDLE handle) { + ReleaseHANDLE state = new ReleaseHANDLE(handle); + return new OpaqueCleanable(state, CLEANER.register(owner, state)); + } + + /** + * Connect to a debug session. + * + * See {@code DebugConnect} or {@code DebugConnectWide} on the MSDN. + * + * @param remoteOptions the options, like those given to {@code -remote} + * @return a new client connected as specified + */ + public static DebugClient debugConnect(String remoteOptions) { + WString options = new WString(remoteOptions); + return DebugClientInternal.tryPreferredInterfaces((refiid, + ppClient) -> DbgEngNative.INSTANCE.DebugConnectWide(options, refiid, ppClient)); + } + + /** + * Create a debug client. + * + * Typically, this client is connected to the "local server". See {@code DebugCreate} on the + * MSDN. + * + * @return a new client + */ + public static DebugClient debugCreate() { + return DebugClientInternal.tryPreferredInterfaces(DbgEngNative.INSTANCE::DebugCreate); + } + + /** + * Create a debug client with the given options. + * + * See {@code DebugCreateEx} on the MSDN. + * + * @param options the options + * @return a new client + */ + public static DebugClient debugCreate(int options) { + DWORD dwOpts = new DWORD(options); + return DebugClientInternal.tryPreferredInterfaces( + (refiid, ppClient) -> DbgEngNative.INSTANCE.DebugCreateEx(refiid, dwOpts, ppClient)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugAdvanced.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugAdvanced.java new file mode 100644 index 0000000000..af4291f895 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugAdvanced.java @@ -0,0 +1,83 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * A wrapper for {@code IDebugAdvanced} and its newer variants. + */ +public interface DebugAdvanced { + public static class DebugThreadBasicInformation { + public final Integer exitStatus; + public final Integer priorityClass; + public final Integer priority; + public final Long createTime; + public final Long exitTime; + public final Long kernelTime; + public final Long userTime; + public final Long startOffset; + public final Long affinity; + + public DebugThreadBasicInformation(Integer exitStatus, Integer priorityClass, + Integer priority, Long createTime, Long exitTime, Long kernelTime, Long userTime, + Long startOffset, Long affinity) { + this.exitStatus = exitStatus; + this.priorityClass = priorityClass; + this.priority = priority; + this.createTime = createTime; + this.exitTime = exitTime; + this.kernelTime = kernelTime; + this.userTime = userTime; + this.startOffset = startOffset; + this.affinity = affinity; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(""); + return sb.toString(); + } + } + + DebugThreadBasicInformation getThreadBasicInformation(DebugThreadId tid); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugBreakpoint.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugBreakpoint.java new file mode 100644 index 0000000000..a2e17b418e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugBreakpoint.java @@ -0,0 +1,122 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.Machine; +import ghidra.comm.util.BitmaskSet; +import ghidra.comm.util.BitmaskUniverse; + +/** + * A wrapper for {@code IDebugBreakpoint} and its newer variants. + */ +public interface DebugBreakpoint { + public static enum BreakType { + CODE, DATA, TIME, INLINE; + } + + public static class BreakFullType { + public final BreakType breakType; + public final Machine procType; // TODO: Guessing the values are from WinNT + + public BreakFullType(BreakType breakType, Machine procType) { + this.breakType = breakType; + this.procType = procType; + } + } + + public static enum BreakFlags implements BitmaskUniverse { + GO_ONLY(1 << 0), // + DEFERRED(1 << 1), // + ENABLED(1 << 2), // + ADDER_ONLY(1 << 3), // + ONE_SHOT(1 << 4), // + ; + + private BreakFlags(int mask) { + this.mask = mask; + } + + private final int mask; + + @Override + public long getMask() { + return mask; + } + } + + public static enum BreakAccess implements BitmaskUniverse { + READ(1 << 0), // + WRITE(1 << 1), // + EXECUTE(1 << 2), // + IO(1 << 3), // + ; + + private BreakAccess(int mask) { + this.mask = mask; + } + + private final int mask; + + @Override + public long getMask() { + return mask; + } + } + + public static class BreakDataParameters { + public int size; + public BitmaskSet access; + + public BreakDataParameters(int size, BitmaskSet access) { + this.size = size; + this.access = access; + } + } + + void remove(); + + int getId(); + + BreakFullType getType(); + + DebugClient getAdder(); + + BitmaskSet getFlags(); + + void addFlags(BitmaskSet flags); + + void addFlags(BreakFlags... flags); + + void removeFlags(BitmaskSet flags); + + void removeFlags(BreakFlags... flags); + + void setFlags(BitmaskSet flags); + + void setFlags(BreakFlags... flags); + + long getOffset(); + + void setOffset(long offset); + + BreakDataParameters getDataParameters(); + + void setDataParameters(BreakDataParameters params); + + void setDataParameters(int size, BitmaskSet access); + + void setDataParameters(int size, BreakAccess... access); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClient.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClient.java new file mode 100644 index 0000000000..9f16050120 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClient.java @@ -0,0 +1,398 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import java.util.List; + +import com.sun.jna.platform.win32.WinBase; + +import agent.dbgeng.dbgeng.DebugRunningProcess.Description; +import agent.dbgeng.dbgeng.DebugRunningProcess.Description.ProcessDescriptionFlags; +import ghidra.comm.util.BitmaskSet; +import ghidra.comm.util.BitmaskUniverse; + +/** + * A wrapper for {@code IDebugClient} and its newer variants. + */ +public interface DebugClient extends DebugClientReentrant { + public static enum ExecutionState { + RUNNING, STOPPED; + } + + public static enum DebugStatus { + NO_CHANGE(false, null, 13), // + GO(true, ExecutionState.RUNNING, 10), // + GO_HANDLED(true, ExecutionState.RUNNING, 9), // + GO_NOT_HANDLED(true, ExecutionState.RUNNING, 8), // + STEP_OVER(true, ExecutionState.RUNNING, 7), // + STEP_INTO(true, ExecutionState.RUNNING, 5), // + BREAK(false, ExecutionState.STOPPED, 0), // + NO_DEBUGGEE(true, null, 1), // shouldWait is true to handle process creation + STEP_BRANCH(true, null, 6), // + IGNORE_EVENT(false, null, 11), // + RESTART_REQUESTED(true, null, 12), // + REVERSE_GO(true, null, 0xff), // + REVERSE_STEP_BRANCH(true, null, 0xff), // + REVERSE_STEP_OVER(true, null, 0xff), // + REVERSE_STEP_INTO(true, null, 0xff), // + OUT_OF_SYNC(false, null, 2), // + WAIT_INPUT(false, null, 3), // + TIMEOUT(false, null, 4), // + ; + + public static final long MASK = 0xaf; + public static final long INSIDE_WAIT = 0x100000000L; + public static final long WAIT_TIMEOUT = 0x200000000L; + + DebugStatus(boolean shouldWait, ExecutionState threadState, int precedence) { + this.shouldWait = shouldWait; + this.threadState = threadState; + this.precedence = precedence; + } + + public final boolean shouldWait; + public final ExecutionState threadState; + public final int precedence; // 0 is highest + + public static DebugStatus fromArgument(long argument) { + return values()[(int) (argument & MASK)]; + } + + public static boolean isInsideWait(long argument) { + return (argument & INSIDE_WAIT) != 0; + } + + public static boolean isWaitTimeout(long argument) { + return (argument & WAIT_TIMEOUT) != 0; + } + } + + public static enum SessionStatus { + ACTIVE, // + END_SESSION_ACTIVE_TERMINATE,// + END_SESSION_ACTIVE_DETACH, // + END_SESSION_PASSIVE, // + END, // + REBOOT, // + HIBERNATE, // + FAILURE, // + ; + } + + public static enum ChangeDebuggeeState implements BitmaskUniverse { + ALL(0xffffffff), // + REGISTERS(1 << 0), // + DATA(1 << 1), // + REFRESH(1 << 2), // + ; + + private ChangeDebuggeeState(int mask) { + this.mask = mask; + } + + private final int mask; + + @Override + public long getMask() { + return mask; + } + } + + public static enum ChangeEngineState implements BitmaskUniverse { + ALL(0xffffffff), // + CURRENT_THREAD(1 << 0), // + EFFECTIVE_PROCESSOR(1 << 1), // + BREAKPOINTS(1 << 2), // + CODE_LEVEL(1 << 3), // + EXECUTION_STATUS(1 << 4), // + ENGINE_OPTIONS(1 << 5), // + LOG_FILE(1 << 6), // + RADIX(1 << 7), // + EVENT_FILTERS(1 << 8), // + PROCESS_OPTIONS(1 << 9), // + EXTENSIONS(1 << 10), // + SYSTEMS(1 << 11), // + ASSEMBLY_OPTIONS(1 << 12), // + EXPRESSION_SYNTAX(1 << 13), // + TEXT_REPLACEMENTS(1 << 14), // + ; + + private ChangeEngineState(int mask) { + this.mask = mask; + } + + private final int mask; + + @Override + public long getMask() { + return mask; + } + } + + public static enum ChangeSymbolState implements BitmaskUniverse { + ALL(0xffffffff), // + LOADS(1 << 0), // + UNLOADS(1 << 1), // + SCOPE(1 << 2), // + PATHS(1 << 3), // + SYMBOL_OPTIONS(1 << 4), // + TYPE_OPTIONS(1 << 5), // + ; + + private ChangeSymbolState(int mask) { + this.mask = mask; + } + + private final int mask; + + @Override + public long getMask() { + return mask; + } + } + + public static enum DebugAttachFlags implements BitmaskUniverse { + DEFAULT(0), // + NONINVASIVE(1 << 0), // + EXISTING(1 << 1), // + NONINVASIVE_NO_SUSPEND(1 << 2), // + INVASIVE_NO_INITIAL_BREAK(1 << 3), // + INVASIVE_RESUME_PROCESS(1 << 4), // + NONINVASIVE_ALLOW_PARTIAL(1 << 5), // + ; + + DebugAttachFlags(int mask) { + this.mask = mask; + } + + private final int mask; + + @Override + public long getMask() { + return mask; + } + } + + public static enum DebugCreateFlags implements BitmaskUniverse { + DEBUG_PROCESS(WinBase.DEBUG_PROCESS), // + DEBUG_ONLY_THIS_PROCESS(WinBase.DEBUG_ONLY_THIS_PROCESS), // + CREATE_SUSPENDED(WinBase.CREATE_SUSPENDED), // + DETACHED_PROCESS(WinBase.DETACHED_PROCESS), // + + CREATE_NEW_CONSOLE(WinBase.CREATE_NEW_CONSOLE), // + //NORMAL_PRIORITY_CLASS(WinBase.NORMAL_PRIORITY_CLASS), // + //IDLE_PRIORITY_CLASS(WinBase.IDLE_PRIORITY_CLASS), // + //HIGH_PRIORITY_CLASS(WinBase.HIGH_PRIORITY_CLASS), // + + //REALTIME_PRIORITY_CLASS(WinBase.REALTIME_PRIORITY_CLASS), // + CREATE_NEW_PROCESS_GROUP(WinBase.CREATE_NEW_PROCESS_GROUP), // + CREATE_UNICODE_ENVIRONMENT(WinBase.CREATE_UNICODE_ENVIRONMENT), // + CREATE_SEPARATE_WOW_VDM(WinBase.CREATE_SEPARATE_WOW_VDM), // + + CREATE_SHARED_WOW_VDM(WinBase.CREATE_SHARED_WOW_VDM), // + CREATE_FORCEDOS(WinBase.CREATE_FORCEDOS), // + //BELOW_NORMAL_PRIORITY_CLASS(WinBase.BELOW_NORMAL_PRIORITY_CLASS), // + //ABOVE_NORMAL_PRIORITY_CLASS(WinBase.ABOVE_NORMAL_PRIORITY_CLASS), // + + INHERIT_PARENT_AFFINITY(WinBase.INHERIT_PARENT_AFFINITY), // + //INHERIT_CALLER_PRIORITY(WinBase.INHERIT_CALLER_PRIORITY), // + CREATE_PROTECTED_PROCESS(WinBase.CREATE_PROTECTED_PROCESS), // + EXTENDED_STARTUPINFO_PRESENT(WinBase.EXTENDED_STARTUPINFO_PRESENT), // + + //PROCESS_MODE_BACKGROUND_BEGIN(WinBase.PROCESS_MODE_BACKGROUND_BEGIN), // + //PROCESS_MODE_BACKGROUND_END(WinBase.PROCESS_MODE_BACKGROUND_END), // + + CREATE_BREAKAWAY_FROM_JOB(WinBase.CREATE_BREAKAWAY_FROM_JOB), // + CREATE_PRESERVE_CODE_AUTHZ_LEVEL(WinBase.CREATE_PRESERVE_CODE_AUTHZ_LEVEL), // + CREATE_DEFAULT_ERROR_MODE(WinBase.CREATE_DEFAULT_ERROR_MODE), // + CREATE_NO_WINDOW(WinBase.CREATE_NO_WINDOW), // + + //PROFILE_USER(WinBase.PROFILE_USER), // + //PROFILE_KERNEL(WinBase.PROFILE_KERNEL), // + //PROFILE_SERVER(WinBase.PROFILE_SERVER), // + //CREATE_IGNORE_SYSTEM_DEFAULT(WinBase.CREATE_IGNORE_SYSTEM_DEFAULT), // + DEBUG_CREATE_NO_DEBUG_HEAP(0x00000400), // + DEBUG_CREATE_THROUGH_RTL(0x00010000), // + ; + + DebugCreateFlags(int mask) { + this.mask = mask; + } + + private final int mask; + + @Override + public long getMask() { + return mask; + } + } + + public enum DebugEndSessionFlags { + DEBUG_END_PASSIVE(0x00000000), + DEBUG_END_ACTIVE_TERMINATE(0x00000001), + DEBUG_END_ACTIVE_DETACH(0x00000002), + DEBUG_END_REENTRANT(0x00000003), + DEBUG_END_DISCONNECT(0x00000004); + + DebugEndSessionFlags(int value) { + this.value = value; + } + + private final int value; + + public long getValue() { + return value; + } + } + + public enum DebugOutputFlags { + DEBUG_OUTPUT_NORMAL(0x1), // + DEBUG_OUTPUT_ERROR(0x2), // + DEBUG_OUTPUT_WARNING(0x4), // + DEBUG_OUTPUT_VERBOSE(0x8), // + DEBUG_OUTPUT_PROMPT(0x10), // + DEBUG_OUTPUT_PROMPT_REGISTERS(0x20), // + DEBUG_OUTPUT_EXTENSION_WARNING(0x40), // + DEBUG_OUTPUT_DEBUGGEE(0x80), // + DEBUG_OUTPUT_DEBUGGEE_PROMPT(0x100), // + DEBUG_OUTPUT_SYMBOLS(0x200); + + DebugOutputFlags(int value) { + this.value = value; + } + + private final int value; + + public long getValue() { + return value; + } + } + + /** + * Obtain the advanced interface to this client. + * + * @return the advanced interface + */ + DebugAdvanced getAdvanced(); + + /** + * Obtain the control interface to this client + * + * @return the control interface + */ + @Override + DebugControl getControl(); + + /** + * Obtain the data spaces interface to this client + * + * @return the data spaces interface + */ + DebugDataSpaces getDataSpaces(); + + /** + * Obtain the registers interface to this client + * + * @return the registers interface + */ + DebugRegisters getRegisters(); + + /** + * Obtain the symbols interface to this client + * + * @return the symbols interface + */ + DebugSymbols getSymbols(); + + /** + * Obtain the system objects interface to this client + * + * @return the system objects interface + */ + DebugSystemObjects getSystemObjects(); + + /** + * The the ID for the local server + * + * @return the ID + */ + DebugServerId getLocalServer(); + + void attachKernel(long flags, String options); + + void startProcessServer(String options); + + DebugServerId connectProcessServer(String options); + + boolean dispatchCallbacks(int timeout); + + void flushCallbacks(); + + default void dispatchCallbacks() { + this.dispatchCallbacks(-1); + } + + void exitDispatch(DebugClient client); + + default void exitDispatch() { + exitDispatch(this); + } + + void setInputCallbacks(DebugInputCallbacks cb); + + void setOutputCallbacks(DebugOutputCallbacks cb); + + void setEventCallbacks(DebugEventCallbacks cb); + + List getRunningProcesses(DebugServerId server); + + Description getProcessDescription(DebugServerId si, int systemId, + BitmaskSet flags); + + void attachProcess(DebugServerId si, int processId, BitmaskSet attachFlags); + + void createProcess(DebugServerId si, String commandLine, + BitmaskSet createFlags); + + void createProcessAndAttach(DebugServerId si, String commandLine, + BitmaskSet createFlags, int processId, + BitmaskSet attachFlags); + + void startServer(String options); + + // Only in IDebugClient2 + + void waitForProcessServerEnd(int timeout); + + default void waitForProcessServerEnd() { + waitForProcessServerEnd(-1); + } + + void terminateCurrentProcess(); + + void detachCurrentProcess(); + + void abandonCurrentProcess(); + + void connectSession(int flags); + + void endSession(DebugEndSessionFlags flags); + + // Only in IDebugClient4+ + + void openDumpFileWide(String fileName); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClientReentrant.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClientReentrant.java new file mode 100644 index 0000000000..cd846f72f9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClientReentrant.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * An interface containing the subset of {@link DebugClient} methods which are reentrant. + * + * All other methods should be called only by the thread which created the client. + */ +public interface DebugClientReentrant { + /** + * Create a new client for the calling thread, connected to the same session as this client. + * + * @return the new client + */ + DebugClient createClient(); + + /** + * Get the reentrant control interface to the client + * + * @return the control interface + */ + DebugControlReentrant getControl(); + + /** + * End a session without acquiring locks + * + * Note. This method calls {@code IDebugClient::EndSession(DEBUG_END_REENTRANT)}. Per the MSDN, + * this may leave the engine in an indeterminate state. The engine should no longer be used by + * this process. It's really only appropriate to use this method when terminating the debugger. + */ + void endSessionReentrant(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControl.java new file mode 100644 index 0000000000..25dc3acbf0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControl.java @@ -0,0 +1,315 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import java.util.ArrayList; +import java.util.List; + +import com.sun.jna.platform.win32.WinBase; +import com.sun.jna.platform.win32.COM.COMException; + +import agent.dbgeng.dbgeng.DebugBreakpoint.BreakType; +import agent.dbgeng.dbgeng.DebugClient.DebugStatus; +import ghidra.comm.util.BitmaskSet; +import ghidra.comm.util.BitmaskUniverse; +import ghidra.util.Msg; + +/** + * A wrapper for {@code IDebugControl} and its newer variants. + */ +public interface DebugControl extends DebugControlReentrant { + public static final BitmaskSet SET_ALL_CLIENTS = + BitmaskSet.of(DebugOutputControl.ALL_CLIENTS); + public static final BitmaskSet SET_DEFAULT = BitmaskSet.of(DebugExecute.DEFAULT); + + public static enum DebugOutputLevel implements BitmaskUniverse { + NORMAL(1 << 0), // + ERROR(1 << 1), // + WARNING(1 << 2), // + VERBOSE(1 << 3), // + PROMPT(1 << 4), // + PROMPT_REGISTERS(1 << 5), // + EXTENSION_WARNING(1 << 6), // + OUTPUT_DEBUGEE(1 << 7), // + OUTPUT_DEBUGEE_PROMPT(1 << 8), // + OUTPUT_SYMBOLS(1 << 9), // + OUTPUT_STATUS(1 << 10), // + ; + + private final int mask; + + DebugOutputLevel(int mask) { + this.mask = mask; + } + + @Override + public long getMask() { + return mask; + } + } + + public static enum DebugOutputControl implements BitmaskUniverse { + THIS_CLIENT(0), // + ALL_CLIENTS(1), // + ALL_OTHER_CLIENTS(2), // + IGNORE(3), // + LOG_ONLY(4), // + SEND_MASK(7), // + NOT_LOGGED(1 << 3), // + OVERRIDE_MASK(1 << 4), // + DML(1 << 5), // + AMBIENT_DML(0xfffffffe), // + AMBIENT_TEXT(0xffffffff), // + AMBIENT(0xffffffff), // + ; + + private final int mask; + + DebugOutputControl(int mask) { + this.mask = mask; + } + + @Override + public long getMask() { + return mask; + } + } + + public static enum DebugExecute implements BitmaskUniverse { + DEFAULT(0), // + ECHO(1 << 0), // + NOT_LOGGED(1 << 1), // + NO_REPEAT(1 << 2), // + ; + + private final int mask; + + DebugExecute(int mask) { + this.mask = mask; + } + + @Override + public long getMask() { + return mask; + } + } + + public static enum DebugInterrupt { + ACTIVE, // + PASSIVE, // + EXIT, // + ; + } + + boolean getInterrupt(); + + int getInterruptTimeout(); + + void setInterruptTimeout(int seconds); + + void print(BitmaskSet levels, String message); + + /** + * A shortcut for {@link #print(BitmaskSet, String)} that includes a newline. + * + * @param levels the log levels for the message + * @param message the message + */ + void println(BitmaskSet levels, String message); + + /** + * A shortcut for {@link #print(BitmaskSet, String)} that applies to a single level. + * + * @param level the log level for the message + * @param message the message + */ + default void print(DebugOutputLevel level, String message) { + print(BitmaskSet.of(level), message); + } + + /** + * A shortcut for {@link #print(BitmaskSet, String)} that includes a newline and applies to a + * single level. + * + * @param level the log level for the message + * @param message the message + */ + default void println(DebugOutputLevel level, String message) { + println(BitmaskSet.of(level), message); + } + + /** + * A shortcut for {@link #print(BitmaskSet, String)} at normal level. + * + * @param message the message + */ + default void out(String message) { + print(DebugOutputLevel.NORMAL, message); + } + + /** + * A shortcut for {@link #println(BitmaskSet, String)} at normal level. + * + * @param message the message + */ + default void outln(String message) { + println(DebugOutputLevel.NORMAL, message); + } + + /** + * A shortcut for {@link #print(BitmaskSet, String)} at warning level. + * + * @param message the message + */ + default void warn(String message) { + print(DebugOutputLevel.WARNING, message); + } + + /** + * A shortcut for {@link #println(BitmaskSet, String)} at warning level. + * + * @param message the message + */ + default void warnln(String message) { + println(DebugOutputLevel.WARNING, message); + } + + /** + * A shortcut for {@link #print(BitmaskSet, String)} at error level. + * + * @param message the message + */ + default void err(String message) { + print(DebugOutputLevel.ERROR, message); + } + + /** + * A shortcut for {@link #println(BitmaskSet, String)} at error level. + * + * @param message the message + */ + default void errln(String message) { + println(DebugOutputLevel.ERROR, message); + } + + /** + * A shortcut for {@link #print(BitmaskSet, String)} at verbose level. + * + * @param message the message + */ + default void verb(String message) { + print(DebugOutputLevel.VERBOSE, message); + } + + /** + * A shortcut for {@link #println(BitmaskSet, String)} at verbose level. + * + * @param message the message + */ + default void verbln(String message) { + println(DebugOutputLevel.VERBOSE, message); + } + + T evaluate(Class desiredType, String expression); + + void execute(BitmaskSet ctl, String str, BitmaskSet flags); + + /** + * A shortcut for {@link #execute(BitmaskSet, String, BitmaskSet)} outputting to all clients + * with the default execution flag. + * + * @param str the command string + */ + default void execute(String str) { + execute(SET_ALL_CLIENTS, str, SET_DEFAULT); + } + + void prompt(BitmaskSet ctl, String message); + + String getPromptText(); + + void returnInput(String input); + + DebugStatus getExecutionStatus(); + + void setExecutionStatus(DebugStatus status); + + int getNumberBreakpoints(); + + DebugBreakpoint getBreakpointByIndex(int index); + + /** + * Shortcut to retrieve all breakpoints for the current process. + * + * Uses {@link #getNumberBreakpoints()} and {@link #getBreakpointByIndex(int)} to enumerate all + * breakpoints for the current process. + * + * @return the list of retrieved breakpoints. + */ + default List getBreakpoints() { + int count = getNumberBreakpoints(); + List result = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + try { + result.add(getBreakpointByIndex(i)); + } + catch (COMException e) { + if (!COMUtilsExtra.isE_NOINTERFACE(e)) { + throw e; + } + Msg.trace(this, "Discarding private breakpoint at index " + i); + } + } + return result; + } + + /** + * Get a breakpoint by ID + * + * According to the MSDN, though the IDs may be global, this method should only succeed for + * breakpoints belonging to the current process. + * + * @param id + * @return + */ + DebugBreakpoint getBreakpointById(int id); + + DebugBreakpoint addBreakpoint(BreakType type, int desiredId); + + DebugBreakpoint addBreakpoint(BreakType type); + + void waitForEvent(int timeout); + + DebugEventInformation getLastEventInformation(); + + DebugStackInformation getStackTrace(long frameOffset, long stackOffset, long instructionOffset); + + /** + * Shortcut for {@link #waitForEvent(int)} with infinite timeout. + */ + default void waitForEvent() { + waitForEvent(WinBase.INFINITE); + } + + int getActualProcessorType(); + + int getEffectiveProcessorType(); + + int getExecutingProcessorType(); + + int getDebuggeeType(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControlReentrant.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControlReentrant.java new file mode 100644 index 0000000000..544d451e77 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControlReentrant.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import agent.dbgeng.dbgeng.DebugControl.DebugInterrupt; + +/** + * An interface containing the subset of {@link DebugControl} methods which are reentrant. + * + * All other methods should be called only by the thread which created the client. + */ +public interface DebugControlReentrant { + void setInterrupt(DebugInterrupt interrupt); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugDataSpaces.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugDataSpaces.java new file mode 100644 index 0000000000..4fe1e9684f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugDataSpaces.java @@ -0,0 +1,285 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import java.nio.ByteBuffer; +import java.util.*; + +import com.sun.jna.platform.win32.COM.COMException; + +import ghidra.comm.util.BitmaskSet; +import ghidra.comm.util.BitmaskUniverse; +import ghidra.util.Msg; + +/** + * A wrapper for {@code IDebugDataSpaces} and its newer variants. + */ +public interface DebugDataSpaces { + public enum PageState { + COMMIT(0x1000), FREE(0x10000), RESERVE(0x2000); + + private final int val; + + private PageState(int val) { + this.val = val; + } + + public static PageState byValue(int val) { + for (PageState state : values()) { + if (state.val == val) { + return state; + } + } + Msg.warn(PageState.class, "No such value: 0x" + Integer.toHexString(val)); + return null; + } + } + + public enum PageProtection implements BitmaskUniverse { + NOACCESS(1 << 0, false, false, false), // + READONLY(1 << 1, true, false, false), // + READWRITE(1 << 2, true, true, false), // + WRITE_COPY(1 << 3, true, true, false), // Becomes READWRITE after copy + EXECUTE(1 << 4, false, false, true), // + EXECUTE_READ(1 << 5, true, false, true), // + EXECUTE_READWRITE(1 << 6, true, true, true), // + EXECUTE_WRITECOPY(1 << 7, true, true, true), // + // + GUARD(1 << 8, false, false, false), // + NOCACHE(1 << 9, false, false, false), // + WRITECOMBINE(1 << 10, false, false, false), // + ; + + private PageProtection(int mask, boolean isRead, boolean isWrite, boolean isExecute) { + this.mask = mask; + this.isRead = isRead; + this.isWrite = isWrite; + this.isExecute = isExecute; + } + + final int mask; + final boolean isRead; + final boolean isWrite; + final boolean isExecute; + + @Override + public long getMask() { + return mask; + } + + public boolean isRead() { + return isRead; + } + + public boolean isWrite() { + return isWrite; + } + + public boolean isExecute() { + return isExecute; + } + } + + public enum PageType { + NONE(0), // + IMAGE(0x1000000), // + MAPPED(0x40000), // + PRIVATE(0x20000), // + ; + + private final int val; + + private PageType(int val) { + this.val = val; + } + + public static PageType byValue(int val) { + for (PageType type : values()) { + if (type.val == val) { + return type; + } + } + Msg.warn(PageType.class, "No such value: 0x" + Integer.toHexString(val)); + return null; + } + } + + public static class DebugMemoryBasicInformation { + public final long baseAddress; + public final long allocationBase; + public final Set allocationProtect; + public final long regionSize; + public final PageState state; + public final Set protect; + public final PageType type; + + public DebugMemoryBasicInformation(long baseAddress, long allocationBase, + BitmaskSet allocationProtect, long regionSize, PageState state, + BitmaskSet protect, PageType type) { + this.baseAddress = baseAddress; + this.allocationBase = allocationBase; + this.allocationProtect = Collections.unmodifiableSet(allocationProtect); + this.regionSize = regionSize; + this.state = state; + this.protect = Collections.unmodifiableSet(protect); + this.type = type; + } + + @Override + public String toString() { + return ""; + } + + @Override + public int hashCode() { + return Objects.hash(baseAddress, allocationBase, allocationProtect, regionSize, state, + protect, type); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DebugMemoryBasicInformation)) { + return false; + } + DebugMemoryBasicInformation that = (DebugMemoryBasicInformation) obj; + if (this.baseAddress != that.baseAddress) { + return false; + } + if (this.allocationBase != that.allocationBase) { + return false; + } + if (!this.allocationProtect.equals(that.allocationProtect)) { + return false; + } + if (this.regionSize != that.regionSize) { + return false; + } + if (this.state != that.state) { + return false; + } + if (!this.protect.equals(that.protect)) { + return false; + } + if (this.type != that.type) { + return false; + } + return true; + } + } + + int readVirtual(long offset, ByteBuffer into, int len); + + int writeVirtual(long offset, ByteBuffer from, int len); + + int readVirtualUncached(long offset, ByteBuffer into, int len); + + int writeVirtualUncached(long offset, ByteBuffer from, int len); + + int readPhysical(long offset, ByteBuffer into, int len); + + int writePhysical(long offset, ByteBuffer from, int len); + + int readControl(int processor, long offset, ByteBuffer into, int len); + + int writeControl(int processor, long offset, ByteBuffer from, int len); + + int readBusData(int busDataType, int busNumber, int slotNumber, long offset, ByteBuffer into, + int len); + + int writeBusData(int busDataType, int busNumber, int slotNumber, long offset, ByteBuffer from, + int len); + + int readIo(int interfaceType, int busNumber, int addressSpace, long offset, ByteBuffer into, + int len); + + int writeIo(int interfaceType, int busNumber, int addressSpace, long offset, ByteBuffer from, + int len); + + long readMsr(int msr); + + void writeMsr(int msr, long value); + + int readDebuggerData(int offset, ByteBuffer into, int len); + + DebugMemoryBasicInformation queryVirtual(long offset); + + /** + * A shortcut for iterating over virtual memory regions. + * + * This operates by calling {@link #queryVirtual(long)} to get each next entry, starting at an + * offset of -start-, adding the size of the returned region to determine the offset for the + * next call. + * + * @param start the starting offset + * @return an iterator over virtual memory regions after the given start + */ + default Iterable iterateVirtual(long start) { + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + private long last = start; + private long offset = start; + private DebugMemoryBasicInformation next = doGetNext(); + + private DebugMemoryBasicInformation getNext() { + if (Long.compareUnsigned(last, offset) < 0) { + return doGetNext(); + } + return null; + } + + private DebugMemoryBasicInformation doGetNext() { + try { + DebugMemoryBasicInformation info = queryVirtual(offset); + last = offset; + if (info != null) { + offset += info.regionSize; + } + return info; + } + catch (COMException e) { + if (!COMUtilsExtra.isE_NOINTERFACE(e)) { + throw e; + } + return null; + } + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public DebugMemoryBasicInformation next() { + DebugMemoryBasicInformation ret = next; + next = getNext(); + return ret; + } + }; + } + }; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugEventCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugEventCallbacks.java new file mode 100644 index 0000000000..2cd1c6360f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugEventCallbacks.java @@ -0,0 +1,116 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import java.lang.annotation.*; + +import agent.dbgeng.dbgeng.DebugClient.*; +import ghidra.comm.util.BitmaskSet; +import ghidra.comm.util.BitmaskUniverse; + +/** + * The interface for receiving event callbacks via {@code IDebugEventCallbacks} or a newer variant. + * + * Note: The wrapper implementation will select the appropriate native interface version. + * + * Note: Even though {@link #changeDebuggeeState(BitmaskSet, long)}, + * {@link #changeEngineState(BitmaskSet, long)} and {@link #changeSymbolState(BitmaskSet, long)} + * purport to return a {@link DebugStatus}, the returned value is ignored by {@code dbgeng.dll}. + */ +public interface DebugEventCallbacks { + public static enum DebugEvent implements BitmaskUniverse { + BREAKPOINT(1 << 0), // + EXCEPTION(1 << 1), // + CREATE_THREAD(1 << 2), // + EXIT_THREAD(1 << 3), // + CREATE_PROCESS(1 << 4), // + EXIT_PROCESS(1 << 5), // + LOAD_MODULE(1 << 6), // + UNLOAD_MODULE(1 << 7), // + SYSTEM_ERROR(1 << 8), // + SESSION_STATUS(1 << 9), // + CHANGE_DEBUGEE_STATE(1 << 10), // + CHANGE_ENGINE_STATE(1 << 11), // + CHANGE_SYMBOL_STATE(1 << 12), // + ; + + private DebugEvent(int mask) { + this.mask = mask; + } + + private final int mask; + + @Override + public long getMask() { + return mask; + } + } + + /** + * An annotation for marking each callback with its interest flag. + */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + static @interface ForInterest { + /** + * The flag corresponding to the annotated callback method + * + * @return the flag + */ + DebugEvent value(); + } + + BitmaskSet getInterestMask(); + + @ForInterest(DebugEvent.BREAKPOINT) + DebugStatus breakpoint(DebugBreakpoint bp); + + @ForInterest(DebugEvent.EXCEPTION) + DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance); + + @ForInterest(DebugEvent.CREATE_THREAD) + DebugStatus createThread(DebugThreadInfo debugThreadInfo); + + @ForInterest(DebugEvent.EXIT_THREAD) + DebugStatus exitThread(int exitCode); + + @ForInterest(DebugEvent.CREATE_PROCESS) + DebugStatus createProcess(DebugProcessInfo debugProcessInfo); + + @ForInterest(DebugEvent.EXIT_PROCESS) + DebugStatus exitProcess(int exitCode); + + @ForInterest(DebugEvent.LOAD_MODULE) + DebugStatus loadModule(DebugModuleInfo debugModuleInfo); + + @ForInterest(DebugEvent.UNLOAD_MODULE) + DebugStatus unloadModule(String imageBaseName, long baseOffset); + + @ForInterest(DebugEvent.SYSTEM_ERROR) + DebugStatus systemError(int error, int level); + + @ForInterest(DebugEvent.SESSION_STATUS) + DebugStatus sessionStatus(SessionStatus status); + + @ForInterest(DebugEvent.CHANGE_DEBUGEE_STATE) + DebugStatus changeDebuggeeState(BitmaskSet flags, long argument); + + @ForInterest(DebugEvent.CHANGE_ENGINE_STATE) + DebugStatus changeEngineState(BitmaskSet flags, long argument); + + @ForInterest(DebugEvent.CHANGE_SYMBOL_STATE) + DebugStatus changeSymbolState(BitmaskSet flags, long argument); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugEventInformation.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugEventInformation.java new file mode 100644 index 0000000000..753fb45cc8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugEventInformation.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import agent.dbgeng.jna.dbgeng.WinNTExtra; + +public class DebugEventInformation { + + private int type; + private DebugProcessId pid; + private DebugThreadId tid; + private DebugSessionId sid; + private int executingProcessorType = WinNTExtra.Machine.IMAGE_FILE_MACHINE_AMD64.val; + + public DebugEventInformation(int type, int pid, int tid) { + this.type = type; + this.pid = new DebugProcessId(pid); + this.tid = new DebugThreadId(tid); + } + + public int getType() { + return type; + } + + public DebugSessionId getSessionId() { + return sid; + } + + public DebugProcessId getProcessId() { + return pid; + } + + public DebugThreadId getThreadId() { + return tid; + } + + public void setThread(DebugThreadId tid) { + this.tid = tid; + } + + public void setProcess(DebugProcessId pid) { + this.pid = pid; + } + + public void setSession(DebugSessionId sid) { + this.sid = sid; + } + + public int getExecutingProcessorType() { + return executingProcessorType; + } + + public void setExecutingProcessorType(int execType) { + this.executingProcessorType = execType; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugExceptionRecord64.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugExceptionRecord64.java new file mode 100644 index 0000000000..447e0cc775 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugExceptionRecord64.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import java.util.Collections; +import java.util.List; + +/** + * Data copied from a {@code EXCEPTION_RECORD64} as defined in {@code winnt.h}. + * + * TODO: Some enums, flags, etc., to help interpret some of the fields. + */ +public class DebugExceptionRecord64 { + public final int code; // TODO: How to interpret + public final int flags; // TODO: How to interpret + public final long record; // TODO: How to interpret + public final long address; + public final List information; + + public DebugExceptionRecord64(int code, int flags, long record, long address, + List information) { + this.code = code; + this.flags = flags; + this.record = record; + this.address = address; + this.information = Collections.unmodifiableList(information); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugInputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugInputCallbacks.java new file mode 100644 index 0000000000..7aef585707 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugInputCallbacks.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import java.util.concurrent.CompletableFuture; + +/** + * The interface for receiving input callbacks via {@code IDebugInputCallbacks} or a newer variant. + * + * Note: The wrapper implementation will select the appropriate native interface version. + */ +@FunctionalInterface +public interface DebugInputCallbacks { + CompletableFuture startInput(); + + default void endInput() { + // Optional implementation + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugModule.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugModule.java new file mode 100644 index 0000000000..df524160d1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugModule.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * Handle to a module (program or library image). + */ +public interface DebugModule { + public enum DebugModuleName { + IMAGE, MODULE, LOADED_IMAGE, SYMBOL_FILE, MAPPED_IMAGE; + } + + /** + * Get a name for the module. + * + * @param which identifies which name + * @return the requested name, if available + */ + String getName(DebugModuleName which); + + /** + * Get the index assigned to this module. + * + * @return the index + */ + int getIndex(); + + /** + * Get the base address where this module is loaded, if applicable. + * + * @return the base address + */ + long getBase(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugModuleInfo.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugModuleInfo.java new file mode 100644 index 0000000000..c8a87dfb0c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugModuleInfo.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * Information about a module (program or library image). + * + * The fields correspond to the parameters taken by {@code LoadModule} of + * {@code IDebugEventCallbacks}. They also appear as a subset of parameters taken by + * {@code CreateProcess} of {@code IDebugEventCallbacks}. + */ +public class DebugModuleInfo { + public final long imageFileHandle; + public final long baseOffset; + public final int moduleSize; + public final String moduleName; + public final String imageName; + public final int checkSum; + public final int timeDateStamp; + + public DebugModuleInfo(long imageFileHandle, long baseOffset, int moduleSize, String moduleName, + String imageName, int checkSum, int timeDateStamp) { + this.imageFileHandle = imageFileHandle; + this.baseOffset = baseOffset; + this.moduleSize = moduleSize; + this.moduleName = moduleName; + this.imageName = imageName; + this.checkSum = checkSum; + this.timeDateStamp = timeDateStamp; // TODO: Convert to DateTime? + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugOutputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugOutputCallbacks.java new file mode 100644 index 0000000000..26fe938dcc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugOutputCallbacks.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import agent.dbgeng.dbgeng.DebugControl.DebugOutputLevel; + +/** + * The interface for receiving output callbacks via {@code IDebugOutputCallbacks} or a newer + * variant. + * + * Note: The wrapper implementation will select the apprirate native interface version. + * + * TODO: Change {@link #output(int, String)} {@code mask} parameter to use {@link DebugOutputLevel} + * flags. + */ +@FunctionalInterface +public interface DebugOutputCallbacks { + void output(int mask, String text); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugProcessId.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugProcessId.java new file mode 100644 index 0000000000..53dd553282 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugProcessId.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * The engine ID assigned to a debugged process. + * + * Note: This is not the same as the "PID." {@code dbgeng.dll} calls that the system ID of + * the process. + * + * This is essentially just a boxed integer, but having an explicit data type prevents confusion + * with other integral values. In particular, this prevents confusion of engine PIDs with system + * PIDs. + */ +public class DebugProcessId implements Comparable { + public final int id; + + public DebugProcessId(int id) { + this.id = id; + } + + @Override + public int hashCode() { + return Integer.hashCode(id); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DebugProcessId)) { + return false; + } + DebugProcessId that = (DebugProcessId) obj; + if (this.id != that.id) { + return false; + } + return true; + } + + @Override + public int compareTo(DebugProcessId that) { + return Integer.compare(this.id, that.id); + } + + @Override + public String toString() { + return ""; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugProcessInfo.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugProcessInfo.java new file mode 100644 index 0000000000..714fcb61f3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugProcessInfo.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * Information about a process. + * + * The fields correspond to parameters taken by {@code CreateProcess} of + * {@code IDebugEventCallbacks}. Note that parameters common to other callbacks have been factored + * into types aggregated here. + */ +public class DebugProcessInfo { + public final long handle; + public final DebugModuleInfo moduleInfo; + public final DebugThreadInfo initialThreadInfo; + + public DebugProcessInfo(long handle, DebugModuleInfo moduleInfo, + DebugThreadInfo initialThreadInfo) { + this.handle = handle; + this.moduleInfo = moduleInfo; + this.initialThreadInfo = initialThreadInfo; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugRegisters.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugRegisters.java new file mode 100644 index 0000000000..717f449134 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugRegisters.java @@ -0,0 +1,158 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import java.util.*; + +import agent.dbgeng.dbgeng.DebugValue.DebugValueType; +import ghidra.comm.util.BitmaskSet; +import ghidra.comm.util.BitmaskUniverse; + +/** + * A wrapper for {@code IDebugRegisters} and its newer variants. + */ +public interface DebugRegisters { + public static enum DebugRegisterSource { + DEBUG_REGSRC_DEBUGGEE, // + DEBUG_REGSRC_EXPLICIT, // + DEBUG_REGSRC_FRAME, // + ; + } + + public static enum DebugRegisterFlags implements BitmaskUniverse { + SUB_REGISTER(1 << 0), // + ; + + private DebugRegisterFlags(int mask) { + this.mask = mask; + } + + private final int mask; + + @Override + public long getMask() { + return mask; + } + } + + public static class DebugRegisterDescription { + public final String name; + public final int index; + public final DebugValueType type; + public final Set flags; + public final int subregMaster; + public final int subregLengthBits; + public final long subregMask; + public final int subregShift; + + public DebugRegisterDescription(String name, int index, DebugValueType type, + BitmaskSet flags, int subregMaster, int subregLengthBits, + long subregMask, int subregShift) { + this.name = name; + this.index = index; + this.type = type; + this.flags = Collections.unmodifiableSet(flags); + this.subregMaster = subregMaster; + this.subregLengthBits = subregLengthBits; + this.subregMask = subregMask; + this.subregShift = subregShift; + } + + @Override + public String toString() { + return String.format( + "<%s: name='%s' index=%d type=%s flags=%s subregMaster=%d subregLengthBits=%d" + + " subregMask=%x subregShift=%d>", + getClass().getSimpleName(), name, index, type, flags, subregMaster, + subregLengthBits, subregMask, subregShift); + } + } + + int getNumberRegisters(); + + DebugRegisterDescription getDescription(int registerNumber); + + /** + * A shortcut to get all register descriptions for the current process. + * + * Uses {@link #getNumberRegisters()} and {@link #getDescription(int)} to retrieve all + * descriptions for the current process. + * + * @return the list of register descriptions + */ + default Set getAllDescriptions() { + Set result = new LinkedHashSet<>(); + int count = getNumberRegisters(); + for (int i = 0; i < count; i++) { + result.add(getDescription(i)); + } + return result; + } + + int getIndexByName(String name); + + /** + * A shortcut to get many register indices in one call. + * + * Uses {@link #getIndexByName(String)}. + * + * @param names the names whose indices to get + * @return the indices in respective order to the given names + */ + default int[] getIndicesByNames(String... names) { + int[] indices = new int[names.length]; + for (int i = 0; i < names.length; i++) { + indices[i] = getIndexByName(names[i]); + } + return indices; + } + + DebugValue getValue(int index); + + Map getValues(DebugRegisterSource source, Collection indices); + + /** + * A shortcut to get a register value by name. + * + * Uses {@link #getIndexByName(String)} followed by {@link #getValue(int)}. + * + * @param name the name of the register + * @return the value + */ + default DebugValue getValueByName(String name) { + int indexByName = getIndexByName(name); + if (indexByName > 0) { + return getValue(indexByName); + } + return null; + } + + void setValue(int index, DebugValue value); + + void setValues(DebugRegisterSource source, Map values); + + /** + * A shortcut to set a register value by name. + * + * Uses {@link #getIndexByName(String)} followed by {@link #setValue(int, DebugValue)}. + * + * @param name the name of the register + * @param value the desired value + */ + default void setValueByName(String name, DebugValue value) { + setValue(getIndexByName(name), value); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugRunningProcess.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugRunningProcess.java new file mode 100644 index 0000000000..082638ef6a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugRunningProcess.java @@ -0,0 +1,123 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import agent.dbgeng.dbgeng.DebugRunningProcess.Description.ProcessDescriptionFlags; +import ghidra.comm.util.BitmaskUniverse; + +/** + * Information about a running process, not necessarily a debugged process. + */ +public interface DebugRunningProcess { + /** + * Description of a running process + */ + public static class Description { + public static enum ProcessDescriptionFlags implements BitmaskUniverse { + NO_PATHS(1 << 0), // + NO_SERVICES(1 << 1), // + NO_MTS_PACKAGES(1 << 2), // + NO_COMMAND_LINE(1 << 3), // + NO_SESSION_ID(1 << 4), // + NO_USER_NAME(1 << 5), // + ; + + ProcessDescriptionFlags(int mask) { + this.mask = mask; + } + + private final int mask; + + @Override + public long getMask() { + return mask; + } + } + + public Description(int systemId, String exeName, String description) { + this.systemId = systemId; + this.exeName = exeName; + this.description = description; + } + + private final int systemId; + private final String exeName; + private final String description; + + /** + * The system ID (PID) for the process. + * + * @return the PID + */ + public int getSystemId() { + return systemId; + } + + /** + * The name of the executable defining the process + * + * @return the name + */ + public String getExecutableName() { + return exeName; + } + + /** + * A textual description of the process. + * + * @return the description + */ + public String getDescription() { + return description; + } + + @Override + public String toString() { + return String.format("PID:%d, EXE:%s, Description:%s", systemId, exeName, description); + } + } + + /** + * The system ID (PID) for the process. + * + * @return the PID + */ + int getSystemId(); + + /** + * Get the "full" description of the process. + * + * @param flags indicate which information to include in the description + * @return the description + */ + Description getFullDescription(ProcessDescriptionFlags... flags); + + /** + * The name of the executable defining the process. + * + * @param flags indicate which information to include in the description + * @return the name + */ + String getExecutableName(ProcessDescriptionFlags... flags); + + /** + * A textual description of the process. + * + * @param flags indicate which information to include in the description + * @return the description + */ + String getDescription(ProcessDescriptionFlags... flags); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugServerId.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugServerId.java new file mode 100644 index 0000000000..d44683ab86 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugServerId.java @@ -0,0 +1,58 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * The ID of a debug server. + * + * Each server to which a client is connected is assigned a server ID. The local server, to which + * every client is connected by default, has the ID 0. This is essentially just a boxed integer, but + * having an explicit data type prevents confusion with other integral values. + */ +public class DebugServerId implements Comparable { + public final long id; + + public DebugServerId(long id) { + this.id = id; + } + + @Override + public int hashCode() { + return Long.hashCode(id); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DebugServerId)) { + return false; + } + DebugServerId that = (DebugServerId) obj; + if (this.id != that.id) { + return false; + } + return true; + } + + @Override + public int compareTo(DebugServerId that) { + return Long.compare(this.id, that.id); + } + + @Override + public String toString() { + return ""; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSessionId.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSessionId.java new file mode 100644 index 0000000000..84d1757a0e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSessionId.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * The engine ID assigned to a debugged process. + * + * Note: This is not the same as the "PID." {@code dbgeng.dll} calls that the system ID of + * the process. + * + * This is essentially just a boxed integer, but having an explicit data type prevents confusion + * with other integral values. In particular, this prevents confusion of engine PIDs with system + * PIDs. + */ +public class DebugSessionId implements Comparable { + public final int id; + + public DebugSessionId(int id) { + this.id = id; + } + + @Override + public int hashCode() { + return Integer.hashCode(id); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DebugSessionId)) { + return false; + } + DebugSessionId that = (DebugSessionId) obj; + if (this.id != that.id) { + return false; + } + return true; + } + + @Override + public int compareTo(DebugSessionId that) { + return Integer.compare(this.id, that.id); + } + + @Override + public String toString() { + return ""; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugStackInformation.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugStackInformation.java new file mode 100644 index 0000000000..a63be6271f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugStackInformation.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_STACK_FRAME; + +public class DebugStackInformation { + + private int nFrames; + private DEBUG_STACK_FRAME[] stackFrames; + + public DebugStackInformation(int nFrames, DEBUG_STACK_FRAME[] stackFrames) { + this.nFrames = nFrames; + this.stackFrames = stackFrames; + } + + public int getNumberOfFrames() { + return nFrames; + } + + public DEBUG_STACK_FRAME getFrame(int frameNumber) { + return stackFrames[frameNumber]; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolEntry.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolEntry.java new file mode 100644 index 0000000000..fe0904ae67 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolEntry.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * Data copied from a {@code DEBUG_SYMBOL_ENTRY} as defined in {@code dbgeng.h}. + * + * TODO: Some enums, flags, etc., to help interpret some of the fields. + */ +public class DebugSymbolEntry { + public final long moduleBase; + public final long offset; + public final long symbolId; + public final long size; + public final int flags; + public final int typeId; + public final String name; + public final int tag; + + public DebugSymbolEntry(long moduleBase, long offset, long symbolId, long size, int flags, + int typeId, String name, int tag) { + this.moduleBase = moduleBase; + this.offset = offset; + this.symbolId = symbolId; + this.size = size; + this.flags = flags; + this.typeId = typeId; + this.name = name; + this.tag = tag; + } + + @Override + public String toString() { + return String.format("", // + moduleBase, symbolId, offset, size, flags, typeId, name, tag); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolId.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolId.java new file mode 100644 index 0000000000..451093b95b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolId.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * Symbol identifier, consisting of module ID and symbol index. + */ +public class DebugSymbolId { + public final long moduleBase; + public final long symbolIndex; + + public DebugSymbolId(long moduleBase, long symbolIndex) { + this.moduleBase = moduleBase; + this.symbolIndex = symbolIndex; + } + + @Override + public String toString() { + return String.format("", moduleBase, symbolIndex); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolName.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolName.java new file mode 100644 index 0000000000..325d6db4ff --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbolName.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * Symbol name, consisting of textual name and offset. + */ +public class DebugSymbolName { + public final String name; + public final long offset; + + public DebugSymbolName(String name, long offset) { + this.name = name; + this.offset = offset; + } + + @Override + public String toString() { + return String.format("<%016x: %s>", offset, name); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbols.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbols.java new file mode 100644 index 0000000000..5992eee9d0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSymbols.java @@ -0,0 +1,78 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import java.util.Iterator; +import java.util.List; + +/** + * A wrapper for {@code IDebugSymbols} and its newer variants. + */ +public interface DebugSymbols { + int getNumberLoadedModules(); + + int getNumberUnloadedModules(); + + DebugModule getModuleByIndex(int index); + + DebugModule getModuleByModuleName(String name, int startIndex); + + DebugModule getModuleByOffset(long offset, int startIndex); + + /** + * A shortcut for iterating over all loaded modules, lazily. + * + * @param startIndex the module index to start at + * @return an iterator over modules starting at the given index + */ + default Iterable iterateModules(int startIndex) { + int count = getNumberLoadedModules(); // TODO: What about unloaded? + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + int cur = startIndex; + + @Override + public boolean hasNext() { + return cur < count; + } + + @Override + public DebugModule next() { + DebugModule ret = getModuleByIndex(cur); + cur++; + return ret; + } + }; + } + }; + } + + Iterable iterateSymbolMatches(String pattern); + + List getSymbolIdsByName(String pattern); + + DebugSymbolEntry getSymbolEntry(DebugSymbolId id); + + String getSymbolPath(); + + void setSymbolPath(String path); + + int getSymbolOptions(); + + void setSymbolOptions(int options); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSystemObjects.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSystemObjects.java new file mode 100644 index 0000000000..9accb9cbc3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSystemObjects.java @@ -0,0 +1,102 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import java.util.ArrayList; +import java.util.List; + +/** + * A wrapper for {@code IDebugSystemObjects} and its newer variants. + */ +public interface DebugSystemObjects { + + DebugThreadId getEventThread(); + + DebugProcessId getEventProcess(); + + DebugSessionId getEventSystem(); + + DebugThreadId getCurrentThreadId(); + + void setCurrentThreadId(DebugThreadId id); + + DebugProcessId getCurrentProcessId(); + + void setCurrentProcessId(DebugProcessId id); + + DebugSessionId getCurrentSystemId(); + + void setCurrentSystemId(DebugSessionId id); + + int getNumberThreads(); + + int getTotalNumberThreads(); // TODO: LargestProcess? + + /** + * Get the threads IDs by index from the current process + * + * @param start the starting index + * @param count the number of threads + * @return the list of thread IDs + */ + List getThreads(int start, int count); + + /** + * Get all thread IDs in the current process + * + * @return the list of thread IDs + */ + default List getThreads() { + return getThreads(0, getNumberThreads()); + } + + DebugThreadId getThreadIdByHandle(long handle); + + DebugProcessId getProcessIdByHandle(long handle); + + int getNumberSystems(); + + List getSystems(int start, int count); + + default List getSessions() { + int numberSystems = getNumberSystems(); + if (numberSystems < 0) { + return new ArrayList(); + } + return getSystems(0, numberSystems); + } + + int getNumberProcesses(); + + List getProcesses(int start, int count); + + default List getProcesses() { + int numberProcesses = getNumberProcesses(); + if (numberProcesses < 0) { + return new ArrayList(); + } + return getProcesses(0, numberProcesses); + } + + int getCurrentThreadSystemId(); + + int getCurrentProcessSystemId(); + + DebugThreadId getThreadIdBySystemId(int systemId); + + DebugProcessId getProcessIdBySystemId(int systemId); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugThreadId.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugThreadId.java new file mode 100644 index 0000000000..f9799bcc14 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugThreadId.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * The engine ID assigned to a debugged thread. + * + * Note: This is not the same as the "TID." {@code dbgeng.dll} calls that the system ID of + * the thread. + * + * This is essentially just a boxed integer, but having an explicit data type prevents confusion + * with other integral values. In particular, this prevents confusion of engine TIDs with system + * TIDs. + */ +public class DebugThreadId implements Comparable { + public final int id; + + public DebugThreadId(int id) { + this.id = id; + } + + @Override + public int hashCode() { + return Integer.hashCode(id); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DebugThreadId)) { + return false; + } + DebugThreadId that = (DebugThreadId) obj; + if (this.id != that.id) { + return false; + } + return true; + } + + @Override + public int compareTo(DebugThreadId that) { + return Integer.compare(this.id, that.id); + } + + @Override + public String toString() { + return ""; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugThreadInfo.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugThreadInfo.java new file mode 100644 index 0000000000..96672c0d69 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugThreadInfo.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +/** + * Information about a thread. + * + * The fields correspond to parameters taken by {@code CreateThread} of + * {@code IDebugEventCallbacks}. They also appear as a subset of parameters taken by + * {@code CreateProcess} of {@code IDebugEventCallbacks}. + */ +public class DebugThreadInfo { + public final long handle; + public final long dataOffset; + public final long startOffset; + + public DebugThreadInfo(long handle, long dataOffset, long startOffset) { + this.handle = handle; + this.dataOffset = dataOffset; + this.startOffset = startOffset; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugValue.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugValue.java new file mode 100644 index 0000000000..28f3af5fb0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugValue.java @@ -0,0 +1,457 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import java.lang.annotation.*; +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; + +import agent.dbgeng.dbgeng.DebugValue.DebugValueType; +import agent.dbgeng.dbgeng.DebugValue.ForDebugValueType; +import ghidra.util.NumericUtilities; + +/** + * Data copied from a {@code DEBUG_VALUE} as defined in {dbgeng.h}. + */ +@ForDebugValueType(DebugValueType.INVALID) +public interface DebugValue { + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public static @interface ForDebugValueType { + DebugValueType value(); + } + + public static enum DebugValueType { + INVALID(0), // + INT8(Byte.SIZE), // + INT16(Short.SIZE), // + INT32(Integer.SIZE), // + INT64(Long.SIZE), // + FLOAT32(Float.SIZE), // + FLOAT64(Double.SIZE), // + FLOAT80(80), // + FLOAT82(82), // + FLOAT128(128), // + VECTOR64(64), // + VECTOR128(128), // + ; + + private static final Class[] CLASSES; + + static { + @SuppressWarnings("unchecked") + Class[] supressed = new Class[DebugValueType.values().length]; + CLASSES = supressed; + for (Class cls : DebugValue.class.getDeclaredClasses()) { + if (!DebugValue.class.isAssignableFrom(cls)) { + continue; + } + Class dvCls = cls.asSubclass(DebugValue.class); + DebugValueType type = getDebugValueTypeForClass(dvCls); + CLASSES[type.ordinal()] = dvCls; + } + } + + public static DebugValueType getDebugValueTypeForClass(Class cls) { + ForDebugValueType annot = cls.getAnnotation(ForDebugValueType.class); + if (annot == null) { + throw new AssertionError( + "INTERNAL: Missing ForDebugValueType annotation on " + cls); + } + return annot.value(); + } + + public final int bitLength; + public final int byteLength; + + private DebugValueType(int bitLength) { + this.bitLength = bitLength; + this.byteLength = (bitLength + 7) / 8; + } + + public Class getDebugValueClass() { + return CLASSES[ordinal()]; + } + + public DebugValue decodeBytes(byte[] bytes) throws IllegalArgumentException { + try { + return CLASSES[ordinal()].getConstructor(byte[].class).newInstance(bytes); + } + catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | NoSuchMethodException | SecurityException e) { + throw new AssertionError(e); + } + catch (InvocationTargetException e) { + if (e.getCause() instanceof IllegalArgumentException) { + throw (IllegalArgumentException) e.getCause(); + } + throw new AssertionError(e); + } + } + } + + @ForDebugValueType(DebugValueType.INT8) + public static class DebugInt8Value implements DebugValue { + private final byte value; + + public DebugInt8Value(byte value) { + this.value = value; + } + + public DebugInt8Value(byte[] bytes) { + if (bytes.length != 1) { + throw new IllegalArgumentException("Must have exactly 1 byte"); + } + this.value = bytes[0]; + } + + public byte byteValue() { + return value; + } + + @Override + public byte[] encodeAsBytes() { + return new byte[] { value }; + } + + @Override + public String toString() { + return "byte " + Integer.toHexString(value) + "h"; + } + } + + @ForDebugValueType(DebugValueType.INT16) + public static class DebugInt16Value implements DebugValue { + private final short value; + + public DebugInt16Value(short value) { + this.value = value; + } + + public DebugInt16Value(byte[] bytes) { + if (bytes.length != Short.BYTES) { + throw new IllegalArgumentException("Must have exactly " + Short.BYTES + " bytes"); + } + ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); + this.value = buf.getShort(); + } + + public short shortValue() { + return value; + } + + @Override + public byte[] encodeAsBytes() { + ByteBuffer buf = ByteBuffer.allocate(Short.BYTES).order(ByteOrder.BIG_ENDIAN); + buf.putShort(value); + return buf.array(); + } + + @Override + public String toString() { + return "word " + Integer.toHexString(value) + "h"; + } + } + + @ForDebugValueType(DebugValueType.INT32) + public static class DebugInt32Value implements DebugValue { + private final int value; + + public DebugInt32Value(int value) { + this.value = value; + } + + public DebugInt32Value(byte[] bytes) { + if (bytes.length != Integer.BYTES) { + throw new IllegalArgumentException("Must have exactly " + Integer.BYTES + " bytes"); + } + ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); + this.value = buf.getInt(); + } + + public int intValue() { + return value; + } + + @Override + public byte[] encodeAsBytes() { + ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.BIG_ENDIAN); + buf.putInt(value); + return buf.array(); + } + + @Override + public String toString() { + return "dword " + Integer.toHexString(value) + "h"; + } + } + + @ForDebugValueType(DebugValueType.INT64) + public static class DebugInt64Value implements DebugValue { + private final long value; + + public DebugInt64Value(long value) { + this.value = value; + } + + public DebugInt64Value(byte[] bytes) { + if (bytes.length != Long.BYTES) { + throw new IllegalArgumentException("Must have exactly " + Long.BYTES + " bytes"); + } + ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); + this.value = buf.getLong(); + } + + public long longValue() { + return value; + } + + @Override + public byte[] encodeAsBytes() { + ByteBuffer buf = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.BIG_ENDIAN); + buf.putLong(value); + return buf.array(); + } + + @Override + public String toString() { + return "qword " + Long.toHexString(value) + "h"; + } + } + + @ForDebugValueType(DebugValueType.FLOAT32) + public static class DebugFloat32Value implements DebugValue { + private final float value; + + public DebugFloat32Value(float value) { + this.value = value; + } + + public DebugFloat32Value(byte[] bytes) { + if (bytes.length != Float.BYTES) { + throw new IllegalArgumentException("Must have exactly " + Float.BYTES + " bytes"); + } + ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); + this.value = buf.getFloat(); + } + + public float floatValue() { + return value; + } + + @Override + public byte[] encodeAsBytes() { + ByteBuffer buf = ByteBuffer.allocate(Float.BYTES).order(ByteOrder.BIG_ENDIAN); + buf.putFloat(value); + return buf.array(); + } + + @Override + public String toString() { + return "f32 " + value; + } + } + + @ForDebugValueType(DebugValueType.FLOAT64) + public static class DebugFloat64Value implements DebugValue { + private final double value; + + public DebugFloat64Value(double value) { + this.value = value; + } + + public DebugFloat64Value(byte[] bytes) { + if (bytes.length != Double.BYTES) { + throw new IllegalArgumentException("Must have exactly " + Double.BYTES + " bytes"); + } + ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); + this.value = buf.getDouble(); + } + + public double doubleValue() { + return value; + } + + @Override + public byte[] encodeAsBytes() { + ByteBuffer buf = ByteBuffer.allocate(Double.BYTES).order(ByteOrder.BIG_ENDIAN); + buf.putDouble(value); + return buf.array(); + } + + @Override + public String toString() { + return "f64 " + value; + } + } + + /** + * Extended-precision float + */ + @ForDebugValueType(DebugValueType.FLOAT80) + public static class DebugFloat80Value implements DebugValue { + private final byte[] bytes; + + public DebugFloat80Value(byte[] bytes) { + if (bytes.length != 10) { + throw new IllegalArgumentException("Must have exactly 10 bytes"); + } + this.bytes = Arrays.copyOf(bytes, 10); + } + + public byte[] bytes() { + return bytes; + } + + @Override + public byte[] encodeAsBytes() { + return bytes; + } + + @Override + public String toString() { + return "f80 " + NumericUtilities.convertBytesToString(bytes); + } + } + + /** + * Specific to IA-64 (Itanium) floating-point registers + * + * 17-bit exponent, 64-bit fraction. Not sure how it's aligned in memory, though. + */ + @ForDebugValueType(DebugValueType.FLOAT82) + public static class DebugFloat82Value implements DebugValue { + private final byte[] bytes; + + public DebugFloat82Value(byte[] bytes) { + if (bytes.length != 11) { + throw new IllegalArgumentException("Must have exactly 11 bytes"); + } + this.bytes = Arrays.copyOf(bytes, 11); + } + + public byte[] bytes() { + return bytes; + } + + @Override + public byte[] encodeAsBytes() { + return bytes; + } + + @Override + public String toString() { + return "f82 " + NumericUtilities.convertBytesToString(bytes); + } + } + + /** + * Quadruple-precision float + */ + @ForDebugValueType(DebugValueType.FLOAT128) + public static class DebugFloat128Value implements DebugValue { + private final byte[] bytes; + + public DebugFloat128Value(byte[] bytes) { + if (bytes.length != 16) { + throw new IllegalArgumentException("Must have exactly 16 bytes"); + } + this.bytes = Arrays.copyOf(bytes, 16); + } + + public byte[] bytes() { + return bytes; + } + + @Override + public byte[] encodeAsBytes() { + return bytes; + } + + @Override + public String toString() { + return "f128 " + NumericUtilities.convertBytesToString(bytes); + } + } + + @ForDebugValueType(DebugValueType.VECTOR64) + public static class DebugVector64Value implements DebugValue { + private final byte[] bytes; + + public DebugVector64Value(byte[] bytes) { + if (bytes.length != 8) { + throw new IllegalArgumentException("Must have exactly 8 bytes"); + } + this.bytes = Arrays.copyOf(bytes, 8); + } + + public byte[] vi4() { + return bytes; + } + + @Override + public byte[] encodeAsBytes() { + return bytes; + } + + @Override + public String toString() { + return "vec64 " + NumericUtilities.convertBytesToString(bytes); + } + } + + @ForDebugValueType(DebugValueType.VECTOR128) + public static class DebugVector128Value implements DebugValue { + private final byte[] bytes; + + public DebugVector128Value(byte[] bytes) { + if (bytes.length != 16) { + throw new IllegalArgumentException( + "Must have exactly 16 bytes. got " + bytes.length); + } + this.bytes = Arrays.copyOf(bytes, 16); + } + + public byte[] vi8() { + return bytes; + } + + @Override + public byte[] encodeAsBytes() { + return bytes; + } + + @Override + public String toString() { + return "vec128 " + NumericUtilities.convertBytesToString(bytes); + } + } + + default DebugValueType getValueType() { + return DebugValueType.getDebugValueTypeForClass(getClass()); + } + + /** + * TODO: Document me + * + * Encodes the value as an array of bytes in big-endian order + * + * @return the encoded value + */ + public byte[] encodeAsBytes(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/err/DbgEngException.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/err/DbgEngException.java new file mode 100644 index 0000000000..8aa25a50de --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/err/DbgEngException.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng.err; + +/** + * The base exception for checked {@code dbgeng.dll} wrapper-related errors. + */ +public class DbgEngException extends Exception { + public DbgEngException() { + super(); + } + + public DbgEngException(String message) { + super(message); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/err/DbgEngRuntimeException.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/err/DbgEngRuntimeException.java new file mode 100644 index 0000000000..45409e4ee1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/err/DbgEngRuntimeException.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng.err; + +/** + * The base exception for unchecked {@code dbgeng.dll} wrapper-related errors. + */ +public class DbgEngRuntimeException extends RuntimeException { + public DbgEngRuntimeException() { + super(); + } + + public DbgEngRuntimeException(String message) { + super(message); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/ext/JavaProvider.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/ext/JavaProvider.java new file mode 100644 index 0000000000..c3db3835f8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/ext/JavaProvider.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng.ext; + +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgeng.impl.dbgeng.client.DebugClientInternal; +import agent.dbgeng.jna.dbgeng.client.WrapIDebugClient; +import agent.dbgeng.jna.javaprovider.JavaProviderNative; + +/** + * Wrapper for "javaprovider" plugin library + * + * @deprecated In one (abandoned) use case, the SCTL server can be loaded as a + * "{@code engext.cpp}-style" plugin, presumably into any {@code dbgeng.dll}-powered + * debugger. This is accomplished by embedding the JVM into the plugin, and then calling + * an alternative entry point. This plugin also provides a utility function for invoking + * {@code CreateClient} on the client provided to the plugin by the host debugger. + */ +@Deprecated +public class JavaProvider { + public static DebugClient createClient() { + PointerByReference pClient = new PointerByReference(); + COMUtils.checkRC(JavaProviderNative.INSTANCE.createClient(pClient.getPointer())); + WrapIDebugClient wrap = new WrapIDebugClient(pClient.getValue()); + + try { + return DebugClientInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/util/DebugEventCallbacksAdapter.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/util/DebugEventCallbacksAdapter.java new file mode 100644 index 0000000000..3440fa29b7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/util/DebugEventCallbacksAdapter.java @@ -0,0 +1,125 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng.util; + +import java.lang.reflect.Method; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugClient.*; +import ghidra.comm.util.BitmaskSet; + +/** + * A convenient abstract implementation of {@link DebugEventCallbacks} + * + * This implementation automatically computes the value for {@link #getInterestMask()} based on the + * overridden methods. The default implementations all return {@link DebugStatus#NO_CHANGE}, should + * they happen to be called. + */ +public class DebugEventCallbacksAdapter implements DebugEventCallbacks { + + private BitmaskSet interests = new BitmaskSet<>(DebugEvent.class); + + public DebugEventCallbacksAdapter() { + try { + // Compute the interest mask based on methods that are overridden + for (Method im : DebugEventCallbacks.class.getDeclaredMethods()) { + Method m = this.getClass().getMethod(im.getName(), im.getParameterTypes()); + if (m.getDeclaringClass() == DebugEventCallbacksAdapter.class) { + continue; + } + // The interface method is overridden, grab the annotation from the interface + ForInterest fi = im.getAnnotation(ForInterest.class); + if (fi == null) { + throw new AssertionError("No ForInterest annotation present on " + m); + } + interests.add(fi.value()); + } + } + catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + + @Override + public BitmaskSet getInterestMask() { + return interests; + } + + @Override + public DebugStatus breakpoint(DebugBreakpoint bp) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus exitThread(int exitCode) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus loadModule(DebugModuleInfo debugModuleInfo) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus unloadModule(String imageBaseName, long baseOffset) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus systemError(int error, int level) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus sessionStatus(SessionStatus status) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus changeDebuggeeState(BitmaskSet flags, long argument) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus changeEngineState(BitmaskSet flags, long argument) { + return DebugStatus.NO_CHANGE; + } + + @Override + public DebugStatus changeSymbolState(BitmaskSet flags, long argument) { + return DebugStatus.NO_CHANGE; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngGadpServer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngGadpServer.java new file mode 100644 index 0000000000..67fbfc60b8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngGadpServer.java @@ -0,0 +1,242 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.gadp; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import agent.dbgeng.gadp.impl.DbgEngGadpServerImpl; +import ghidra.dbg.agent.AgentWindow; +import ghidra.util.Msg; + +public interface DbgEngGadpServer extends AutoCloseable { + public static final String DEFAULT_DBGSRV_TRANSPORT = "tcp:port=11200"; + + /** + * The entry point for the SCTL-DBGENG server in stand-alone mode + * + * Run it to see help. + * + * @param args the command-line arguments + * @throws IOException if an I/O error occurs + * @throws ExecutionException + * @throws InterruptedException + */ + public static void main(String[] args) throws Exception { + new DbgEngRunner().run(args); + } + + /** + * Create a new instance of the server + * + * @param addr the address to bind the SCTL server to + * @param busId the client ID the server should use on the bus for synthesized commands + * @param dbgSrvTransport the transport specification for the {@code dbgeng.dll} server + * @return the server instance + * @throws IOException + */ + public static DbgEngGadpServer newInstance(SocketAddress addr) throws IOException { + return new DbgEngGadpServerImpl(addr); + } + + /** + * Runs the server from the command line + */ + public class DbgEngRunner { + protected InetSocketAddress bindTo; + protected List dbgengArgs = new ArrayList<>(); + protected byte busId = 1; + protected String dbgSrvTransport = DEFAULT_DBGSRV_TRANSPORT; + protected String remote = null; + + public DbgEngRunner() { + } + + public void run(String args[]) + throws IOException, InterruptedException, ExecutionException { + parseArguments(args); + + try (DbgEngGadpServer server = newInstance(bindTo)) { + //TODO: fix/test the debugConnect case when args are passed + server.startDbgEng(dbgengArgs.toArray(new String[] {})).exceptionally(e -> { + Msg.error(this, "Error starting dbgeng/GADP", e); + System.exit(-1); + return null; + }); + new AgentWindow("dbgeng.dll Agent for Ghidra", server.getLocalAddress()); + while (server.isRunning()) { + // TODO: Put consoleLoop back? + Thread.sleep(1000); + } + System.exit(0); + } + } + + protected void parseArguments(String[] args) { + String iface = "localhost"; + int port = 12345; + // NOTE: Maybe commons-cli or Argparse4j? + Iterator ait = Arrays.asList(args).iterator(); + while (ait.hasNext()) { + String a = ait.next(); + if ("-h".equals(a) || "--help".equals(a)) { + printUsage(); + System.exit(0); + } + else if ("-p".equals(a) || "--port".equals(a)) { + if (!ait.hasNext()) { + System.err.println("Expected PORT"); + printUsage(); + System.exit(-1); + } + String portStr = ait.next(); + try { + port = Integer.parseInt(portStr); + } + catch (NumberFormatException e) { + System.err.println("Integer required. Got " + portStr); + printUsage(); + System.exit(-1); + } + } + else if ("-H".equals(a) || "--host".equals(a)) { + if (!ait.hasNext()) { + System.err.println("Expected HOST/ADDR"); + printUsage(); + System.exit(-1); + } + iface = ait.next(); + } + else if ("-i".equals(a) || "--bus-id".equals(a)) { + if (!ait.hasNext()) { + System.err.println("Expected ID"); + printUsage(); + System.exit(-1); + } + String busIdStr = ait.next(); + try { + busId = Byte.parseByte(busIdStr); + //dbgengArgs.add(busIdStr); + } + catch (NumberFormatException e) { + System.err.println("Byte required. Got " + busIdStr); + printUsage(); + System.exit(-1); + } + } + else if ("-t".equals(a) || "--transport".equals(a)) { + if (!ait.hasNext()) { + System.err.println("Expected TRANSPORT"); + System.err.println("See the MSDN 'Activating a Process Server'"); + printUsage(); + System.exit(-1); + } + dbgSrvTransport = ait.next(); + dbgengArgs.add(dbgSrvTransport); + } + else if ("-r".equals(a) || "--remote".equals(a)) { + if (!ait.hasNext()) { + System.err.println("Expected TRANSPORT:HOST,PORT"); + printUsage(); + System.exit(-1); + } + remote = ait.next(); + dbgengArgs.add(remote); + } + else { + System.err.println("Unknown option: " + a); + printUsage(); + System.exit(-1); + } + } + + bindTo = new InetSocketAddress(iface, port); + } + + protected void printUsage() { + System.out.println("This is the GADP server for Windows dbgeng.dll. Usage:"); + System.out.println(); + System.out.println(" [-H HOST/ADDR] [-p PORT] [-i ID] [-t TRANSPORT] [-r REMOTE]"); + System.out.println(); + System.out.println("Options:"); + System.out.println( + " --host/-H The address of the interface on which to listen."); + System.out.println(" Default is localhost"); + System.out.println( + " --port/-p The TCP port on which to listen for GADP. Default is 12345"); + System.out.println( + " --bus-id/-i The numeric client id for synthetic requests. Default is 1"); + System.out.println( + " --transport/-t The transport specification for the Process Server."); + System.out.println(" Default is tcp:port=11200"); + System.out.println( + " --remote/-r The transport specification for a remote server."); + } + } + + /** + * Start the debugging server + * + * @return a future that completes when the server is ready + */ + CompletableFuture startDbgEng(String[] args); + + /** + * Get the local address to which the SCTL server is bound. + * + * @return the local socket address + */ + SocketAddress getLocalAddress(); + + /** + * Starts the dbgeng manager's console loop + * + * @throws IOException if an I/O error occurs + */ + //public void consoleLoop() throws IOException; + + /** + * Close all connections and ports, GADP and Process Server, and terminate the server + * + * @throws IOException if an I/O error occurs + */ + public void terminate() throws IOException; + + /** + * Check if the server is running + * + * This will return false: 1) Before the server has been started, 2) After a call to + * {@link #terminate()}, or 3) When an error occurs causing the server to terminate + * unexpectedly. Otherwise, it returns true. + * + * @returns true if the server is currently running. + */ + public boolean isRunning(); + + /** + * Calls {@link #terminate()} + * + * @throws IOException if an I/O error occurs + */ + @Override + default void close() throws IOException { + terminate(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngLocalDebuggerModelFactory.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngLocalDebuggerModelFactory.java new file mode 100644 index 0000000000..62c57012f6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngLocalDebuggerModelFactory.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.gadp; + +import java.util.List; + +import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory; +import ghidra.dbg.util.ConfigurableFactory.FactoryDescription; +import ghidra.util.classfinder.ExtensionPointProperties; + +@FactoryDescription( // + brief = "MS dbgeng.dll (WinDbg) local agent via GADP/TCP", // + htmlDetails = "Launch a new agent using the Microsoft Debug Engine." // +) +@ExtensionPointProperties(priority = 100) +public class DbgEngLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory { + + protected String remote = "none"; // Require user to start server + @FactoryOption("DebugConnect options (.server)") + public final Property agentRemoteOption = + Property.fromAccessors(String.class, this::getAgentRemote, this::setAgentRemote); + + protected String transport = "none"; // Require user to start server + @FactoryOption("Remote process server options (untested)") + public final Property agentTransportOption = + Property.fromAccessors(String.class, this::getAgentTransport, this::setAgentTransport); + + @Override + public boolean isCompatible() { + // TODO: Might instead look for the DLL + return System.getProperty("os.name").toLowerCase().contains("windows"); + } + + public String getAgentTransport() { + return transport; + } + + public void setAgentTransport(String transport) { + this.transport = transport; + } + + public String getAgentRemote() { + return remote; + } + + public void setAgentRemote(String remote) { + this.remote = remote; + } + + @Override + protected String getThreadName() { + return "Local dbgeng.dll Agent stdout"; + } + + protected Class getServerClass() { + return DbgEngGadpServer.class; + } + + @Override + protected void completeCommandLine(List cmd) { + cmd.add(getServerClass().getCanonicalName()); + cmd.addAll(List.of("-H", host)); + cmd.addAll(List.of("-p", Integer.toString(port))); + //cmd.addAll(List.of("-t", transport)); + if (!remote.equals("none")) { + cmd.addAll(List.of("-r", remote)); + } + if (!transport.equals("none")) { + cmd.addAll(List.of("-t", transport)); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/AbstractClientThreadExecutor.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/AbstractClientThreadExecutor.java new file mode 100644 index 0000000000..69d913a42c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/AbstractClientThreadExecutor.java @@ -0,0 +1,236 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.gadp.impl; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; + +import com.sun.jna.platform.win32.COM.COMException; + +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgeng.dbgeng.DebugClient.DebugStatus; +import agent.dbgeng.dbgeng.DebugControl; +import agent.dbgeng.manager.DbgManager; +import ghidra.util.Msg; + +/** + * A single-threaded executor which creates and exclusively accesses the {@code dbgeng.dll} client. + * + * The executor also has a priority mechanism, so that callbacks may register follow-on handlers + * which take precedence over other tasks in the queue (which could trigger additional callbacks). + * This is required since certain operation are not allowed during normal callback processing. For + * example, changing the current process is typically not allowed, but it is necessary to retrieve a + * thread's context. + */ +public abstract class AbstractClientThreadExecutor extends AbstractExecutorService { + private static final int DEFAULT_PRIORITY = 10; + + protected DebugClient client; + protected boolean shuttingDown = false; + protected final Queue queue = new PriorityQueue<>(); + protected Thread thread = new Thread(this::run, "DebugClient"); + protected final AtomicBoolean waitRegistered = new AtomicBoolean(); + + protected abstract void init(); + + public static class Entry implements Comparable { + final int priority; + public final Runnable command; + + public Entry(int priority, Runnable command) { + this.priority = priority; + this.command = command; + } + + @Override + public int compareTo(Entry that) { + return Integer.compare(this.priority, that.priority); + } + } + + /** + * Obtain a reference to the client, only if the calling thread is this executor's thread. + * + * @return the client + */ + public DebugClient getClient() { + if (thread != Thread.currentThread()) { + //TODO: throw new AssertionError("Cannot get client outside owning thread"); + } + return client; + } + + /** + * Instruct the executor to call {@link DebugClient#dispatchCallbacks()} when it next idles. + */ + public void cancelWait() { + waitRegistered.set(false); + } + + /** + * Instruct the executor to call {@link DebugControl#waitForEvent()} when it next idles. + */ + public void registerWait() { + waitRegistered.set(true); + } + + private Entry pollQueue() { + synchronized (queue) { + return queue.poll(); + } + } + + private void run() { + /** + * The general idea is to run indefinitely, taking every precaution to protect this thread's + * life, since only it can access the client. Granted, if it turns out to be too difficult, + * we can always create a new thread and client, using the existing client's reentrant + * methods. + * + * As stated in the MSDN, this thread repeatedly calls {@code DispatchEvents} in order to + * receive callbacks regarding events caused by other clients. If, however, an wait is + * registered, or the current engine state indicates that a wait is proper, the thread calls + * {@code WaitForEvent} instead. The thread is occupied until the wait completes, which is + * fine since the engine is inaccessible (except to certain callbacks) until it completes, + * anyway. + */ + try { + init(); + while (!shuttingDown) { + Entry next; + while (null != (next = pollQueue())) { + if (shuttingDown) { + return; + } + try { + //System.out.println("Executing: " + next); + next.command.run(); + //System.out.println("Done"); + } + catch (Throwable t) { + Msg.error(this, "Task in executor threw: " + t); + } + } + DebugStatus status = client.getControl().getExecutionStatus(); + if (status.shouldWait && status != DebugStatus.NO_DEBUGGEE || + waitRegistered.get()) { + waitRegistered.set(false); + try { + getManager().waitForEventEx(); + //client.getControl().waitForEvent(); + } + catch (COMException e) { + Msg.error(this, "Error during WaitForEvents: " + e); + } + } + else { + try { + client.dispatchCallbacks(100); // TODO: Better synchronization + } + catch (COMException e) { + Msg.error(this, "Error during DispatchCallbacks: " + e); + } + } + } + } + catch (Throwable t) { + Msg.error(this, "Non-respawnable executor terminated unexpectedly", t); + shuttingDown = true; + } + } + + @Override + public void shutdown() { + shuttingDown = true; + } + + @Override + public List shutdownNow() { + shuttingDown = true; + client.exitDispatch(); + thread.interrupt(); + List left = new ArrayList<>(queue.size()); + for (Entry ent : queue) { + left.add(ent.command); + } + return left; + } + + @Override + public boolean isShutdown() { + return shuttingDown; + } + + @Override + public boolean isTerminated() { + return !thread.isAlive(); + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + long millis = TimeUnit.MILLISECONDS.convert(timeout, unit); + thread.join(millis); + return !thread.isAlive(); + } + + @Override + public void execute(Runnable command) { + execute(DEFAULT_PRIORITY, command); + } + + /** + * Schedule a task with a given priority. + * + * Smaller priority values indicate earlier execution. The default priority is + * {@link #DEFAULT_PRIORITY}. + * + * @param priority the priority + * @param command the task + */ + public void execute(int priority, Runnable command) { + if (shuttingDown) { + // TODO: Is this the correct exception? + throw new RejectedExecutionException("Executor is shutting down"); + } + if (!thread.isAlive()) { + throw new RejectedExecutionException("Executor has terminated"); + } + synchronized (queue) { + queue.add(new Entry(priority, command)); + // TODO: Putting this in causes sync/output flushing problems + //client.exitDispatch(); + } + } + + /** + * Schedule a task with the given priority, taking a reference to the client. + * + * This is a convenience which spares a call to {@link #getClient()}. See + * {@link #execute(int, Runnable)} about priority. + * + * @param priority the priority + * @param command the task + */ + public void execute(int priority, Consumer command) { + execute(priority, () -> command.accept(client)); + } + + public abstract DbgManager getManager(); + + public abstract void setManager(DbgManager manager); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/DbgEngClientThreadExecutor.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/DbgEngClientThreadExecutor.java new file mode 100644 index 0000000000..282daf06d4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/DbgEngClientThreadExecutor.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.gadp.impl; + +import java.util.function.Supplier; + +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgeng.manager.DbgManager; + +/** + * A single-threaded executor which creates and exclusively accesses the {@code dbgeng.dll} client. + * + * The executor also has a priority mechanism, so that callbacks may register follow-on handlers + * which take precedence over other tasks in the queue (which could trigger additional callbacks). + * This is required since certain operation are not allowed during normal callback processing. For + * example, changing the current process is typically not allowed, but it is necessary to retrieve a + * thread's context. + */ +public class DbgEngClientThreadExecutor extends AbstractClientThreadExecutor { + + private final Supplier makeClient; + private DbgManager manager; + + /** + * Instantiate a new executor, providing a means of creating the client + * + * @param makeClient the callback to create the client + */ + public DbgEngClientThreadExecutor(Supplier makeClient) { + this.makeClient = makeClient; + thread.setDaemon(true); + thread.start(); + } + + @Override + protected void init() { + this.client = makeClient.get(); + } + + @Override + public DbgManager getManager() { + return manager; + } + + @Override + public void setManager(DbgManager manager) { + this.manager = manager; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/DbgEngGadpServerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/DbgEngGadpServerImpl.java new file mode 100644 index 0000000000..455d81da20 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/impl/DbgEngGadpServerImpl.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.gadp.impl; + +import java.io.IOException; +import java.net.SocketAddress; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.gadp.DbgEngGadpServer; +import agent.dbgeng.model.AbstractDbgModel; +import agent.dbgeng.model.impl.DbgModelImpl; +import ghidra.dbg.gadp.server.AbstractGadpServer; + +public class DbgEngGadpServerImpl implements DbgEngGadpServer { + public class GadpSide extends AbstractGadpServer { + public GadpSide(AbstractDbgModel model, SocketAddress addr) + throws IOException { + super(model, addr); + } + } + + protected final AbstractDbgModel model; + protected final GadpSide server; + + public DbgEngGadpServerImpl(SocketAddress addr) throws IOException { + super(); + this.model = new DbgModelImpl(); + this.server = new GadpSide(model, addr); + } + + @Override + public CompletableFuture startDbgEng(String[] args) { + return model.startDbgEng(args).thenCompose(__ -> server.launchAsyncService()); + } + + @Override + public SocketAddress getLocalAddress() { + return server.getLocalAddress(); + } + + @Override + public boolean isRunning() { + return model.isRunning(); + } + + @Override + public void terminate() throws IOException { + model.terminate(); + server.terminate(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/DbgEngUtil.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/DbgEngUtil.java new file mode 100644 index 0000000000..429a63e862 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/DbgEngUtil.java @@ -0,0 +1,86 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.function.Function; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.*; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.dbgeng.err.DbgEngRuntimeException; +import ghidra.util.Msg; + +public abstract class DbgEngUtil { + public static final ULONG DEBUG_ANY_ID = new ULONG(-1); + + private DbgEngUtil() { + } + + public static interface InterfaceSupplier { + HRESULT get(REFIID refiid, PointerByReference pClient); + } + + @SuppressWarnings("unchecked") + public static I tryPreferredInterfaces(Class cls, + Map> preferred, InterfaceSupplier supplier) { + PointerByReference ppClient = new PointerByReference(); + for (Map.Entry> ent : preferred.entrySet()) { + try { + COMUtils.checkRC(supplier.get(ent.getKey(), ppClient)); + if (ppClient.getValue() == null) { + continue; + } + Object impl = + ent.getValue().getConstructor(Pointer.class).newInstance(ppClient.getValue()); + Method instanceFor = cls.getMethod("instanceFor", ent.getValue()); + Object instance = instanceFor.invoke(null, impl); + return (I) instance; + } + catch (COMException e) { + Msg.debug(DbgEngUtil.class, e + " (" + ent.getValue() + ")"); + // TODO: Only try next on E_NOINTERFACE? + // Try next + } + catch (Exception e) { + throw new AssertionError("INTERNAL: Unexpected exception", e); + } + } + throw new DbgEngRuntimeException("None of the preferred interfaces are supported"); + } + + public static U lazyWeakCache(Map cache, T unk, + Function forNew) { + U present = cache.get(unk.getPointer()); + if (present != null) { + unk.Release(); + return present; + } + U absent = forNew.apply(unk); + cache.put(unk.getPointer(), absent); + return absent; + } + + public static void dbgline() { + System.out.println(new Exception().getStackTrace()[1]); + System.out.flush(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/DebugRunningProcessImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/DebugRunningProcessImpl.java new file mode 100644 index 0000000000..d1efcb178a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/DebugRunningProcessImpl.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng; + +import agent.dbgeng.dbgeng.DebugRunningProcess; +import agent.dbgeng.dbgeng.DebugServerId; +import agent.dbgeng.impl.dbgeng.client.DebugClientInternal; +import ghidra.comm.util.BitmaskSet; + +public class DebugRunningProcessImpl + implements DebugRunningProcess, Comparable { + public DebugRunningProcessImpl(DebugClientInternal client, DebugServerId server, int systemId) { + this.client = client; + this.server = server; + this.systemId = systemId; + } + + protected final DebugClientInternal client; + protected final DebugServerId server; + protected final int systemId; + + @Override + public int getSystemId() { + return systemId; + } + + @Override + public Description getFullDescription(Description.ProcessDescriptionFlags... flags) { + return client.getProcessDescription(server, systemId, BitmaskSet.of(flags)); + } + + @Override + public String getExecutableName(Description.ProcessDescriptionFlags... flags) { + return getFullDescription(flags).getExecutableName(); + } + + @Override + public String getDescription(Description.ProcessDescriptionFlags... flags) { + return getFullDescription(flags).getDescription(); + } + + @Override + public int compareTo(DebugRunningProcessImpl that) { + int result; + + result = Integer.compare(this.systemId, that.systemId); + if (result != 0) { + return result; + } + + return 0; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl1.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl1.java new file mode 100644 index 0000000000..277c15eb15 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl1.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.advanced; + +import agent.dbgeng.dbgeng.DbgEng; +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable; +import agent.dbgeng.jna.dbgeng.advanced.IDebugAdvanced; + +public class DebugAdvancedImpl1 implements DebugAdvancedInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + @SuppressWarnings("unused") + private final IDebugAdvanced jnaAdvanced; + + public DebugAdvancedImpl1(IDebugAdvanced jnaAdvanced) { + this.cleanable = DbgEng.releaseWhenPhantom(this, jnaAdvanced); + this.jnaAdvanced = jnaAdvanced; + } + + @Override + public DebugThreadBasicInformation getThreadBasicInformation(DebugThreadId tid) { + throw new UnsupportedOperationException("Not supported by this interface"); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl2.java new file mode 100644 index 0000000000..13dc3b97aa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl2.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.advanced; + +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; + +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_THREAD_BASIC_INFORMATION; +import agent.dbgeng.jna.dbgeng.advanced.IDebugAdvanced2; + +import com.sun.jna.platform.win32.COM.COMUtils; + +import ghidra.comm.util.BitmaskSet; + +public class DebugAdvancedImpl2 extends DebugAdvancedImpl1 { + private final IDebugAdvanced2 jnaAdvanced; + + public DebugAdvancedImpl2(IDebugAdvanced2 jnaAdvanced) { + super(jnaAdvanced); + this.jnaAdvanced = jnaAdvanced; + } + + @Override + public DebugThreadBasicInformation getThreadBasicInformation(DebugThreadId tid) { + ULONG ulWhich = new ULONG(WhichSystemObjectInformation.THREAD_BASIC_INFORMATION.ordinal()); + ULONGLONG ullUnused = new ULONGLONG(0); + ULONG ulThreadId = new ULONG(tid.id); + DEBUG_THREAD_BASIC_INFORMATION sInfo = new DEBUG_THREAD_BASIC_INFORMATION(); + ULONG ulBufferSize = new ULONG(sInfo.size()); + COMUtils.checkRC(jnaAdvanced.GetSystemObjectInformation(ulWhich, ullUnused, ulThreadId, + sInfo.getPointer(), ulBufferSize, null)); + sInfo.read(); + + Integer exitStatus = null; + Integer priorityClass = null; + Integer priority = null; + Long createTime = null; + Long exitTime = null; + Long kernelTime = null; + Long userTime = null; + Long startOffset = null; + Long affinity = null; + + BitmaskSet valid = + new BitmaskSet<>(ThreadBasicInformationValidBits.class, sInfo.Valid.intValue()); + if (valid.contains(ThreadBasicInformationValidBits.EXIT_STATUS)) { + exitStatus = sInfo.ExitStatus.intValue(); + } + if (valid.contains(ThreadBasicInformationValidBits.PRIORITY_CLASS)) { + priorityClass = sInfo.PriorityClass.intValue(); + } + if (valid.contains(ThreadBasicInformationValidBits.PRIORITY)) { + priority = sInfo.Priority.intValue(); + } + if (valid.contains(ThreadBasicInformationValidBits.TIMES)) { + createTime = sInfo.CreateTime.longValue(); + exitTime = sInfo.ExitTime.longValue(); + kernelTime = sInfo.KernelTime.longValue(); + userTime = sInfo.UserTime.longValue(); + } + if (valid.contains(ThreadBasicInformationValidBits.START_OFFSET)) { + startOffset = sInfo.StartOffset.longValue(); + } + if (valid.contains(ThreadBasicInformationValidBits.AFFINITY)) { + affinity = sInfo.Affinity.longValue(); + } + + return new DebugThreadBasicInformation(exitStatus, priorityClass, priority, createTime, + exitTime, kernelTime, userTime, startOffset, affinity); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl3.java new file mode 100644 index 0000000000..1d4de8f45d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedImpl3.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.advanced; + +import agent.dbgeng.jna.dbgeng.advanced.IDebugAdvanced3; + +public class DebugAdvancedImpl3 extends DebugAdvancedImpl2 { + @SuppressWarnings("unused") + private final IDebugAdvanced3 jnaAdvanced; + + public DebugAdvancedImpl3(IDebugAdvanced3 jnaAdvanced) { + super(jnaAdvanced); + this.jnaAdvanced = jnaAdvanced; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedInternal.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedInternal.java new file mode 100644 index 0000000000..b1ce0bdfd5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/advanced/DebugAdvancedInternal.java @@ -0,0 +1,88 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.advanced; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgeng.dbgeng.DebugAdvanced; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier; +import agent.dbgeng.jna.dbgeng.advanced.*; +import ghidra.comm.util.BitmaskUniverse; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugAdvancedInternal extends DebugAdvanced { + Map CACHE = new WeakValueHashMap<>(); + + static DebugAdvancedInternal instanceFor(WrapIDebugAdvanced advanced) { + return DbgEngUtil.lazyWeakCache(CACHE, advanced, DebugAdvancedImpl1::new); + } + + static DebugAdvancedInternal instanceFor(WrapIDebugAdvanced2 advanced) { + return DbgEngUtil.lazyWeakCache(CACHE, advanced, DebugAdvancedImpl2::new); + } + + static DebugAdvancedInternal instanceFor(WrapIDebugAdvanced3 advanced) { + return DbgEngUtil.lazyWeakCache(CACHE, advanced, DebugAdvancedImpl3::new); + } + + ImmutableMap.Builder> PREFERRED_ADVANCED_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_ADVANCED_IIDS = + PREFERRED_ADVANCED_IIDS_BUILDER // + .put(new REFIID(IDebugAdvanced3.IID_IDEBUG_ADVANCED3), WrapIDebugAdvanced3.class) // + .put(new REFIID(IDebugAdvanced2.IID_IDEBUG_ADVANCED2), WrapIDebugAdvanced2.class) // + .put(new REFIID(IDebugAdvanced.IID_IDEBUG_ADVANCED), WrapIDebugAdvanced.class) // + .build(); + + static DebugAdvancedInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgEngUtil.tryPreferredInterfaces(DebugAdvancedInternal.class, + PREFERRED_ADVANCED_IIDS, supplier); + } + + public enum WhichSystemObjectInformation { + THREAD_BASIC_INFORMATION, // + THREAD_NAME_WIDE, // + CURRENT_PROCESS_COOKIE, // + ; + } + + public enum ThreadBasicInformationValidBits implements BitmaskUniverse { + EXIT_STATUS(1 << 0), // + PRIORITY_CLASS(1 << 1), // + PRIORITY(1 << 2), // + TIMES(1 << 3), // + START_OFFSET(1 << 4), // + AFFINITY(1 << 5), // + ALL(0x3f); + ; + + ThreadBasicInformationValidBits(int mask) { + this.mask = mask; + } + + int mask; + + @Override + public long getMask() { + return mask; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl1.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl1.java new file mode 100644 index 0000000000..0e3b288d26 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl1.java @@ -0,0 +1,164 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.breakpoint; + +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.dbgeng.DbgEng; +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable; +import agent.dbgeng.impl.dbgeng.client.DebugClientInternal; +import agent.dbgeng.impl.dbgeng.control.DebugControlInternal; +import agent.dbgeng.jna.dbgeng.WinNTExtra.Machine; +import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint; +import agent.dbgeng.jna.dbgeng.client.WrapIDebugClient; +import ghidra.comm.util.BitmaskSet; + +public class DebugBreakpointImpl1 implements DebugBreakpointInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private IDebugBreakpoint jnaBreakpoint; + private DebugControlInternal control; + + public DebugBreakpointImpl1(IDebugBreakpoint jnaBreakpoint) { + this.cleanable = DbgEng.releaseWhenPhantom(this, jnaBreakpoint); + this.jnaBreakpoint = jnaBreakpoint; + } + + @Override + public void setControl(DebugControlInternal control) { + this.control = control; + } + + @Override + public void remove() { + control.removeBreakpoint(jnaBreakpoint); + // Prevent accidental access. Will be released during GC. NPE is better than segfault. + jnaBreakpoint = null; + } + + @Override + public int getId() { + ULONGByReference pulId = new ULONGByReference(); + COMUtils.checkRC(jnaBreakpoint.GetId(pulId)); + return pulId.getValue().intValue(); + } + + @Override + public BreakFullType getType() { + ULONGByReference pulBreakType = new ULONGByReference(); + ULONGByReference pulProcType = new ULONGByReference(); + COMUtils.checkRC(jnaBreakpoint.GetType(pulBreakType, pulProcType)); + BreakType breakType = BreakType.values()[pulBreakType.getValue().intValue()]; + Machine procType = Machine.getByNumber(pulProcType.getValue().intValue()); + return new BreakFullType(breakType, procType); + } + + @Override + public DebugClient getAdder() { + PointerByReference pClient = new PointerByReference(); + COMUtils.checkRC(jnaBreakpoint.GetAdder(pClient.getPointer())); + WrapIDebugClient wrap = new WrapIDebugClient(pClient.getValue()); + + try { + return DebugClientInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public BitmaskSet getFlags() { + ULONGByReference pulFlags = new ULONGByReference(); + COMUtils.checkRC(jnaBreakpoint.GetFlags(pulFlags)); + return new BitmaskSet<>(BreakFlags.class, pulFlags.getValue().longValue()); + } + + @Override + public void addFlags(BitmaskSet flags) { + ULONG ulFlags = new ULONG(flags.getBitmask()); + COMUtils.checkRC(jnaBreakpoint.AddFlags(ulFlags)); + } + + @Override + public void addFlags(BreakFlags... flags) { + addFlags(BitmaskSet.of(flags)); + } + + @Override + public void removeFlags(BitmaskSet flags) { + ULONG ulFlags = new ULONG(flags.getBitmask()); + COMUtils.checkRC(jnaBreakpoint.RemoveFlags(ulFlags)); + } + + @Override + public void removeFlags(BreakFlags... flags) { + removeFlags(BitmaskSet.of(flags)); + } + + @Override + public void setFlags(BitmaskSet flags) { + ULONG ulFlags = new ULONG(flags.getBitmask()); + COMUtils.checkRC(jnaBreakpoint.SetFlags(ulFlags)); + } + + @Override + public void setFlags(BreakFlags... flags) { + setFlags(BitmaskSet.of(flags)); + } + + @Override + public long getOffset() { + ULONGLONGByReference pullOffset = new ULONGLONGByReference(); + COMUtils.checkRC(jnaBreakpoint.GetOffset(pullOffset)); + return pullOffset.getValue().longValue(); + } + + @Override + public void setOffset(long offset) { + ULONGLONG ullOffset = new ULONGLONG(offset); + COMUtils.checkRC(jnaBreakpoint.SetOffset(ullOffset)); + } + + @Override + public BreakDataParameters getDataParameters() { + ULONGByReference pulSize = new ULONGByReference(); + ULONGByReference pulAccessType = new ULONGByReference(); + COMUtils.checkRC(jnaBreakpoint.GetDataParameters(pulSize, pulAccessType)); + return new BreakDataParameters(pulSize.getValue().intValue(), + new BitmaskSet<>(BreakAccess.class, pulAccessType.getValue().intValue())); + } + + @Override + public void setDataParameters(BreakDataParameters params) { + setDataParameters(params.size, params.access); + } + + @Override + public void setDataParameters(int size, BitmaskSet access) { + ULONG ulSize = new ULONG(size); + ULONG ulAccessType = new ULONG(access.getBitmask()); + COMUtils.checkRC(jnaBreakpoint.SetDataParameters(ulSize, ulAccessType)); + } + + @Override + public void setDataParameters(int size, BreakAccess... access) { + setDataParameters(size, BitmaskSet.of(access)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl2.java new file mode 100644 index 0000000000..eb8e3de7f6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl2.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.breakpoint; + +import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint2; + +public class DebugBreakpointImpl2 extends DebugBreakpointImpl1 { + @SuppressWarnings("unused") + private final IDebugBreakpoint2 jnaBreakpoint; + + public DebugBreakpointImpl2(IDebugBreakpoint2 jnaBreakpoint) { + super(jnaBreakpoint); + this.jnaBreakpoint = jnaBreakpoint; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl3.java new file mode 100644 index 0000000000..ffc857fdcd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointImpl3.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.breakpoint; + +import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint3; + +public class DebugBreakpointImpl3 extends DebugBreakpointImpl2 { + @SuppressWarnings("unused") + private final IDebugBreakpoint3 jnaBreakpoint; + + public DebugBreakpointImpl3(IDebugBreakpoint3 jnaBreakpoint) { + super(jnaBreakpoint); + this.jnaBreakpoint = jnaBreakpoint; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointInternal.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointInternal.java new file mode 100644 index 0000000000..def9c9533a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/breakpoint/DebugBreakpointInternal.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.breakpoint; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgeng.dbgeng.DebugBreakpoint; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier; +import agent.dbgeng.impl.dbgeng.control.DebugControlInternal; +import agent.dbgeng.jna.dbgeng.breakpoint.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugBreakpointInternal extends DebugBreakpoint { + Map CACHE = new WeakValueHashMap<>(); + + static DebugBreakpointInternal instanceFor(WrapIDebugBreakpoint bp) { + return DbgEngUtil.lazyWeakCache(CACHE, bp, DebugBreakpointImpl1::new); + } + + static DebugBreakpointInternal instanceFor(WrapIDebugBreakpoint2 bp) { + return DbgEngUtil.lazyWeakCache(CACHE, bp, DebugBreakpointImpl2::new); + } + + static DebugBreakpointInternal instanceFor(WrapIDebugBreakpoint3 bp) { + return DbgEngUtil.lazyWeakCache(CACHE, bp, DebugBreakpointImpl3::new); + } + + ImmutableMap.Builder> PREFERRED_BREAKPOINT_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_BREAKPOINT_IIDS = + PREFERRED_BREAKPOINT_IIDS_BUILDER // + .put(new REFIID(IDebugBreakpoint3.IID_IDEBUG_BREAKPOINT3), + WrapIDebugBreakpoint3.class) // + .put(new REFIID(IDebugBreakpoint2.IID_IDEBUG_BREAKPOINT2), + WrapIDebugBreakpoint2.class) // + .put(new REFIID(IDebugBreakpoint.IID_IDEBUG_BREAKPOINT), WrapIDebugBreakpoint.class) // + .build(); + + static DebugBreakpointInternal tryPreferredInterfaces(DebugControlInternal control, + InterfaceSupplier supplier) { + DebugBreakpointInternal bpt = DbgEngUtil.tryPreferredInterfaces( + DebugBreakpointInternal.class, PREFERRED_BREAKPOINT_IIDS, supplier); + bpt.setControl(control); + return bpt; + } + + void setControl(DebugControlInternal control); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl1.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl1.java new file mode 100644 index 0000000000..3eb055bd30 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl1.java @@ -0,0 +1,323 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.client; + +import java.util.ArrayList; +import java.util.List; + +import com.sun.jna.Native; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable; +import agent.dbgeng.impl.dbgeng.DebugRunningProcessImpl; +import agent.dbgeng.impl.dbgeng.advanced.DebugAdvancedInternal; +import agent.dbgeng.impl.dbgeng.control.DebugControlInternal; +import agent.dbgeng.impl.dbgeng.dataspaces.DebugDataSpacesInternal; +import agent.dbgeng.impl.dbgeng.event.WrapCallbackIDebugEventCallbacks; +import agent.dbgeng.impl.dbgeng.io.WrapCallbackIDebugInputCallbacks; +import agent.dbgeng.impl.dbgeng.io.WrapCallbackIDebugOutputCallbacks; +import agent.dbgeng.impl.dbgeng.registers.DebugRegistersInternal; +import agent.dbgeng.impl.dbgeng.symbols.DebugSymbolsInternal; +import agent.dbgeng.impl.dbgeng.sysobj.DebugSystemObjectsInternal; +import agent.dbgeng.jna.dbgeng.client.IDebugClient; +import agent.dbgeng.jna.dbgeng.event.ListenerIDebugEventCallbacks; +import agent.dbgeng.jna.dbgeng.event.MarkerEventCallbacks; +import agent.dbgeng.jna.dbgeng.io.*; +import ghidra.comm.util.BitmaskSet; + +public class DebugClientImpl1 implements DebugClientInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugClient jnaClient; + + private DebugAdvancedInternal advanced; + private DebugControlInternal control; + private DebugDataSpaces data; + private DebugRegisters registers; + private DebugSymbols symbols; + private DebugSystemObjects sysobjs; + + // Keep references to callbacks here, since JNA doesn't keep one for things handed to natives. + protected MarkerOutputCallbacks listenerOutput; + protected MarkerInputCallbacks listenerInput; + protected MarkerEventCallbacks listenerEvent; + + public DebugClientImpl1(IDebugClient jnaClient) { + // TODO: Debug and verify COM resource management + this.cleanable = DbgEng.releaseWhenPhantom(this, jnaClient); + this.jnaClient = jnaClient; + } + + @Override + public IDebugClient getJNAClient() { + return jnaClient; + } + + @Override + public DebugAdvanced getAdvanced() { + if (advanced == null) { + advanced = DebugAdvancedInternal.tryPreferredInterfaces(jnaClient::QueryInterface); + } + return advanced; + } + + @Override + public DebugControlInternal getControlInternal() { + if (control == null) { + control = DebugControlInternal.tryPreferredInterfaces(jnaClient::QueryInterface); + } + return control; + } + + @Override + public DebugControl getControl() { + return getControlInternal(); + } + + @Override + public DebugDataSpaces getDataSpaces() { + if (data == null) { + data = DebugDataSpacesInternal.tryPreferredInterfaces(jnaClient::QueryInterface); + } + return data; + } + + @Override + public DebugRegisters getRegisters() { + if (registers == null) { + registers = DebugRegistersInternal.tryPreferredInterfaces(jnaClient::QueryInterface); + } + return registers; + } + + @Override + public DebugSymbols getSymbols() { + if (symbols == null) { + symbols = DebugSymbolsInternal.tryPreferredInterfaces(jnaClient::QueryInterface); + } + return symbols; + } + + @Override + public DebugSystemObjects getSystemObjects() { + if (sysobjs == null) { + sysobjs = DebugSystemObjectsInternal.tryPreferredInterfaces(jnaClient::QueryInterface); + } + return sysobjs; + } + + @Override + public DebugServerId getLocalServer() { + return new DebugServerId(0); + } + + @Override + public void startProcessServer(String options) { + COMUtils.checkRC(jnaClient.StartProcessServer(new ULONG(DebugClass.USER_WINDOWS.ordinal()), + options, null)); + } + + @Override + public DebugServerId connectProcessServer(String options) { + ULONGLONGByReference pulServer = new ULONGLONGByReference(); + COMUtils.checkRC(jnaClient.ConnectProcessServer(options, pulServer)); + return new DebugServerId(pulServer.getValue().longValue()); + } + + @Override + public List getRunningProcesses(DebugServerId si) { + ULONGLONG server = new ULONGLONG(si.id); + ULONGByReference actualCount = new ULONGByReference(); + COMUtils.checkRC( + jnaClient.GetRunningProcessSystemIds(server, null, new ULONG(0), actualCount)); + + int[] ids = new int[actualCount.getValue().intValue()]; + COMUtils.checkRC( + jnaClient.GetRunningProcessSystemIds(server, ids, actualCount.getValue(), null)); + + List result = new ArrayList<>(ids.length); + for (int id : ids) { + result.add(new DebugRunningProcessImpl(this, si, id)); + } + return result; + } + + @Override + public DebugRunningProcess.Description getProcessDescription(DebugServerId si, int systemId, + BitmaskSet flags) { + ULONGLONG ullServer = new ULONGLONG(si.id); + ULONG ulId = new ULONG(systemId); + ULONG ulFlags = new ULONG(flags.getBitmask()); + + ULONGByReference actualExeNameSize = new ULONGByReference(); + ULONGByReference actualDescriptionSize = new ULONGByReference(); + COMUtils.checkRC(jnaClient.GetRunningProcessDescription(ullServer, ulId, ulFlags, null, + new ULONG(0), actualExeNameSize, null, new ULONG(0), actualDescriptionSize)); + + byte[] exeName = new byte[actualExeNameSize.getValue().intValue()]; + byte[] description = new byte[actualDescriptionSize.getValue().intValue()]; + COMUtils.checkRC(jnaClient.GetRunningProcessDescription(ullServer, ulId, ulFlags, exeName, + actualExeNameSize.getValue(), null, description, actualDescriptionSize.getValue(), + null)); + + return new DebugRunningProcess.Description(systemId, Native.toString(exeName), + Native.toString(description)); + } + + @Override + public void attachProcess(DebugServerId si, int processId, + BitmaskSet attachFlags) { + ULONGLONG ullServer = new ULONGLONG(si.id); + ULONG ulPid = new ULONG(processId); + ULONG ulFlags = new ULONG(attachFlags.getBitmask()); + COMUtils.checkRC(jnaClient.AttachProcess(ullServer, ulPid, ulFlags)); + } + + @Override + public void createProcess(DebugServerId si, String commandLine, + BitmaskSet createFlags) { + ULONGLONG ullServer = new ULONGLONG(si.id); + ULONG ulFlags = new ULONG(createFlags.getBitmask()); + COMUtils.checkRC(jnaClient.CreateProcess(ullServer, commandLine, ulFlags)); + } + + @Override + public void createProcessAndAttach(DebugServerId si, String commandLine, + BitmaskSet createFlags, int processId, + BitmaskSet attachFlags) { + ULONGLONG ullServer = new ULONGLONG(si.id); + ULONG ulFlags1 = new ULONG(createFlags.getBitmask()); + ULONG ulPid = new ULONG(processId); + ULONG ulFlags2 = new ULONG(attachFlags.getBitmask()); + COMUtils.checkRC( + jnaClient.CreateProcessAndAttach(ullServer, commandLine, ulFlags1, ulPid, ulFlags2)); + } + + @Override + public void startServer(String options) { + COMUtils.checkRC(jnaClient.StartServer(options)); + } + + @Override + public boolean dispatchCallbacks(int timeout) { + HRESULT hr = jnaClient.DispatchCallbacks(new ULONG(timeout)); + COMUtils.checkRC(hr); + return hr.equals(WinNT.S_OK); + } + + @Override + public void flushCallbacks() { + HRESULT hr = jnaClient.FlushCallbacks(); + COMUtils.checkRC(hr); + } + + @Override + public void exitDispatch(DebugClient client) { + DebugClientInternal ic = (DebugClientInternal) client; + COMUtils.checkRC(jnaClient.ExitDispatch(ic.getJNAClient())); + } + + @Override + public DebugClient createClient() { + PointerByReference ppClient = new PointerByReference(); + COMUtils.checkRC(jnaClient.CreateClient(ppClient)); + return DebugClientInternal.tryPreferredInterfaces(jnaClient::QueryInterface); + } + + @Override + public void setInputCallbacks(DebugInputCallbacks cb) { + ListenerIDebugInputCallbacks listener = null; + if (cb != null) { + WrapCallbackIDebugInputCallbacks callback = + new WrapCallbackIDebugInputCallbacks(this, cb); + listener = new ListenerIDebugInputCallbacks(callback); + callback.setListener(listener); + } + COMUtils.checkRC(jnaClient.SetInputCallbacks(listener)); + listenerInput = listener; + } + + @Override + public void setOutputCallbacks(DebugOutputCallbacks cb) { + ListenerIDebugOutputCallbacks listener = null; + if (cb != null) { + WrapCallbackIDebugOutputCallbacks callback = new WrapCallbackIDebugOutputCallbacks(cb); + listener = new ListenerIDebugOutputCallbacks(callback); + callback.setListener(listener); + } + COMUtils.checkRC(jnaClient.SetOutputCallbacks(listener)); + listenerOutput = listener; + } + + @Override + public void setEventCallbacks(DebugEventCallbacks cb) { + ListenerIDebugEventCallbacks listener = null; + if (cb != null) { + WrapCallbackIDebugEventCallbacks callback = + new WrapCallbackIDebugEventCallbacks(this, cb); + listener = new ListenerIDebugEventCallbacks(callback); + callback.setListener(listener); + } + COMUtils.checkRC(jnaClient.SetEventCallbacks(listener)); + listenerEvent = listener; + } + + @Override + public void terminateCurrentProcess() { + throw new UnsupportedOperationException("Not implemented by this interface"); + } + + @Override + public void detachCurrentProcess() { + throw new UnsupportedOperationException("Not implemented by this interface"); + } + + @Override + public void abandonCurrentProcess() { + throw new UnsupportedOperationException("Not implemented by this interface"); + } + + @Override + public void waitForProcessServerEnd(int timeout) { + throw new UnsupportedOperationException("Not implemented by this interface"); + } + + @Override + public void endSession(DebugEndSessionFlags flags) { + COMUtils.checkRC(jnaClient.EndSession(new ULONG(flags.getValue()))); + } + + @Override + public void connectSession(int flags) { + COMUtils.checkRC(jnaClient.ConnectSession(new ULONG(flags), new ULONG(10000))); + } + + @Override + public void openDumpFileWide(String fileName) { + throw new UnsupportedOperationException("Not implemented by this interface"); + } + + @Override + public void attachKernel(long flags, String options) { + throw new UnsupportedOperationException("Not implemented by this interface"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl2.java new file mode 100644 index 0000000000..15578d0a24 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl2.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.client; + +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgeng.jna.dbgeng.client.IDebugClient2; + +public class DebugClientImpl2 extends DebugClientImpl1 { + private final IDebugClient2 jnaClient; + + public DebugClientImpl2(IDebugClient2 jnaClient) { + super(jnaClient); + this.jnaClient = jnaClient; + } + + @Override + public void terminateCurrentProcess() { + COMUtils.checkRC(jnaClient.TerminateCurrentProcess()); + } + + @Override + public void detachCurrentProcess() { + COMUtils.checkRC(jnaClient.DetachCurrentProcess()); + } + + @Override + public void abandonCurrentProcess() { + COMUtils.checkRC(jnaClient.AbandonCurrentProcess()); + } + + @Override + public void waitForProcessServerEnd(int timeout) { + COMUtils.checkRC(jnaClient.WaitForProcessServerEnd(new ULONG(timeout))); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl3.java new file mode 100644 index 0000000000..106ed2fc34 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl3.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.client; + +import com.sun.jna.Native; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinDef.*; + +import agent.dbgeng.dbgeng.DebugRunningProcess; +import agent.dbgeng.dbgeng.DebugServerId; +import agent.dbgeng.dbgeng.DebugRunningProcess.Description.ProcessDescriptionFlags; +import agent.dbgeng.jna.dbgeng.client.IDebugClient3; + +import com.sun.jna.platform.win32.COM.COMUtils; + +import ghidra.comm.util.BitmaskSet; + +public class DebugClientImpl3 extends DebugClientImpl2 { + private final IDebugClient3 jnaClient; + + public DebugClientImpl3(IDebugClient3 jnaClient) { + super(jnaClient); + this.jnaClient = jnaClient; + } + + @Override + public void createProcess(DebugServerId si, String commandLine, + BitmaskSet createFlags) { + ULONGLONG ullServer = new ULONGLONG(si.id); + ULONG ulFlags = new ULONG(createFlags.getBitmask()); + COMUtils.checkRC(jnaClient.CreateProcessWide(ullServer, new WString(commandLine), ulFlags)); + } + + @Override + public DebugRunningProcess.Description getProcessDescription(DebugServerId si, int systemId, + BitmaskSet flags) { + ULONGLONG server = new ULONGLONG(si.id); + ULONG id = new ULONG(systemId); + ULONG f = new ULONG(flags.getBitmask()); + + ULONGByReference actualExeNameSize = new ULONGByReference(); + ULONGByReference actualDescriptionSize = new ULONGByReference(); + COMUtils.checkRC(jnaClient.GetRunningProcessDescriptionWide(server, id, f, null, + new ULONG(0), actualExeNameSize, null, new ULONG(0), actualDescriptionSize)); + + char[] exeName = new char[actualExeNameSize.getValue().intValue()]; + char[] description = new char[actualDescriptionSize.getValue().intValue()]; + COMUtils.checkRC(jnaClient.GetRunningProcessDescriptionWide(server, id, f, exeName, + actualExeNameSize.getValue(), null, description, actualDescriptionSize.getValue(), + null)); + + return new DebugRunningProcess.Description(systemId, Native.toString(exeName), + Native.toString(description)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl4.java new file mode 100644 index 0000000000..a97d1fd041 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl4.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.client; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; + +import agent.dbgeng.jna.dbgeng.client.IDebugClient4; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugClientImpl4 extends DebugClientImpl3 { + @SuppressWarnings("unused") + private final IDebugClient4 jnaClient; + + public DebugClientImpl4(IDebugClient4 jnaClient) { + super(jnaClient); + this.jnaClient = jnaClient; + } + + @Override + public void openDumpFileWide(String fileName) { + ULONGLONG ullFileHandle = new ULONGLONG(0); + COMUtils.checkRC(jnaClient.OpenDumpFileWide(new WString(fileName), ullFileHandle)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl5.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl5.java new file mode 100644 index 0000000000..0170458096 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl5.java @@ -0,0 +1,82 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.client; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.impl.dbgeng.event.WrapCallbackIDebugEventCallbacksWide; +import agent.dbgeng.impl.dbgeng.io.WrapCallbackIDebugOutputCallbacksWide; +import agent.dbgeng.jna.dbgeng.client.IDebugClient5; +import agent.dbgeng.jna.dbgeng.event.ListenerIDebugEventCallbacksWide; +import agent.dbgeng.jna.dbgeng.io.ListenerIDebugOutputCallbacksWide; + +public class DebugClientImpl5 extends DebugClientImpl4 { + private final IDebugClient5 jnaClient; + + public DebugClientImpl5(IDebugClient5 jnaClient) { + super(jnaClient); + this.jnaClient = jnaClient; + } + + @Override + public void attachKernel(long flags, String options) { + ULONG connectFlags = new ULONG(flags); + COMUtils.checkRC(jnaClient.AttachKernelWide(connectFlags, new WString(options))); + } + + @Override + public void startProcessServer(String options) { + COMUtils.checkRC(jnaClient.StartProcessServerWide( + new ULONG(DebugClass.USER_WINDOWS.ordinal()), new WString(options), null)); + } + + @Override + public DebugServerId connectProcessServer(String options) { + ULONGLONGByReference pulServer = new ULONGLONGByReference(); + COMUtils.checkRC(jnaClient.ConnectProcessServerWide(new WString(options), pulServer)); + return new DebugServerId(pulServer.getValue().longValue()); + } + + @Override + public void setOutputCallbacks(DebugOutputCallbacks cb) { + ListenerIDebugOutputCallbacksWide listener = null; + if (cb != null) { + WrapCallbackIDebugOutputCallbacksWide callback = + new WrapCallbackIDebugOutputCallbacksWide(cb); + listener = new ListenerIDebugOutputCallbacksWide(callback); + callback.setListener(listener); + } + COMUtils.checkRC(jnaClient.SetOutputCallbacksWide(listener)); + listenerOutput = listener; + } + + @Override + public void setEventCallbacks(DebugEventCallbacks cb) { + ListenerIDebugEventCallbacksWide listener = null; + if (cb != null) { + WrapCallbackIDebugEventCallbacksWide callback = + new WrapCallbackIDebugEventCallbacksWide(this, cb); + listener = new ListenerIDebugEventCallbacksWide(callback); + callback.setListener(listener); + } + COMUtils.checkRC(jnaClient.SetEventCallbacksWide(listener)); + listenerEvent = listener; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl6.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl6.java new file mode 100644 index 0000000000..8282a39d72 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl6.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.client; + +import agent.dbgeng.dbgeng.DebugEventCallbacks; +import agent.dbgeng.jna.dbgeng.client.IDebugClient6; + +public class DebugClientImpl6 extends DebugClientImpl5 { + @SuppressWarnings("unused") + private final IDebugClient6 jnaClient; + + public DebugClientImpl6(IDebugClient6 jnaClient) { + super(jnaClient); + this.jnaClient = jnaClient; + } + + @Override + public void setEventCallbacks(DebugEventCallbacks cb) { + // TODO: Use Context variant. Will require expanding the generic interface. + super.setEventCallbacks(cb); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl7.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl7.java new file mode 100644 index 0000000000..3ac1b9139c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl7.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.client; + +import agent.dbgeng.jna.dbgeng.client.IDebugClient7; + +public class DebugClientImpl7 extends DebugClientImpl6 { + @SuppressWarnings("unused") + private final IDebugClient7 jnaClient; + + public DebugClientImpl7(IDebugClient7 jnaClient) { + super(jnaClient); + this.jnaClient = jnaClient; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientInternal.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientInternal.java new file mode 100644 index 0000000000..f8e59a8f1a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientInternal.java @@ -0,0 +1,96 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.client; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier; +import agent.dbgeng.impl.dbgeng.control.DebugControlInternal; +import agent.dbgeng.jna.dbgeng.client.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugClientInternal extends DebugClient { + Map CACHE = new WeakValueHashMap<>(); + + enum DebugClass { + UNINITIALIZED, // + KERNEL, // + USER_WINDOWS, // + IMAGE_FILE, // + ; + } + + static DebugClientInternal instanceFor(WrapIDebugClient client) { + return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl1::new); + } + + static DebugClientInternal instanceFor(WrapIDebugClient2 client) { + return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl2::new); + } + + static DebugClientInternal instanceFor(WrapIDebugClient3 client) { + return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl3::new); + } + + static DebugClientInternal instanceFor(WrapIDebugClient4 client) { + return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl4::new); + } + + static DebugClientInternal instanceFor(WrapIDebugClient5 client) { + return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl5::new); + } + + static DebugClientInternal instanceFor(WrapIDebugClient6 client) { + return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl6::new); + } + + static DebugClientInternal instanceFor(WrapIDebugClient7 client) { + return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl7::new); + } + + ImmutableMap.Builder> PREFERRED_CLIENT_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_CLIENT_IIDS = + PREFERRED_CLIENT_IIDS_BUILDER // + .put(new REFIID(IDebugClient7.IID_IDEBUG_CLIENT7), WrapIDebugClient7.class) // + .put(new REFIID(IDebugClient6.IID_IDEBUG_CLIENT6), WrapIDebugClient6.class) // + .put(new REFIID(IDebugClient5.IID_IDEBUG_CLIENT5), WrapIDebugClient5.class) // + .put(new REFIID(IDebugClient4.IID_IDEBUG_CLIENT4), WrapIDebugClient4.class) // + .put(new REFIID(IDebugClient3.IID_IDEBUG_CLIENT3), WrapIDebugClient3.class) // + .put(new REFIID(IDebugClient2.IID_IDEBUG_CLIENT2), WrapIDebugClient2.class) // + .put(new REFIID(IDebugClient.IID_IDEBUG_CLIENT), WrapIDebugClient.class) // + .build(); + + static DebugClientInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgEngUtil.tryPreferredInterfaces(DebugClientInternal.class, PREFERRED_CLIENT_IIDS, + supplier); + } + + IDebugClient getJNAClient(); + + DebugControlInternal getControlInternal(); + + @Override + default void endSessionReentrant() { + endSession(DebugEndSessionFlags.DEBUG_END_REENTRANT); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl1.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl1.java new file mode 100644 index 0000000000..36b3c0d97b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl1.java @@ -0,0 +1,288 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.control; + +import com.sun.jna.Native; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinError; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable; +import agent.dbgeng.dbgeng.DebugBreakpoint.BreakType; +import agent.dbgeng.dbgeng.DebugClient.DebugStatus; +import agent.dbgeng.dbgeng.DebugValue.DebugValueType; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.impl.dbgeng.breakpoint.DebugBreakpointInternal; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_STACK_FRAME; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint; +import agent.dbgeng.jna.dbgeng.control.IDebugControl; +import ghidra.comm.util.BitmaskSet; + +public class DebugControlImpl1 implements DebugControlInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugControl jnaControl; + + public DebugControlImpl1(IDebugControl jnaControl) { + this.cleanable = DbgEng.releaseWhenPhantom(this, jnaControl); + this.jnaControl = jnaControl; + } + + @Override + public boolean getInterrupt() { + HRESULT interrupt = jnaControl.GetInterrupt(); + if (interrupt.equals(WinError.S_OK)) { + return true; + } + if (interrupt.equals(WinError.S_FALSE)) { + return false; + } + COMUtils.checkRC(interrupt); + throw new AssertionError("Shouldn't get here"); + } + + @Override + public void setInterrupt(DebugInterrupt interrupt) { + ULONG flags = new ULONG(interrupt.ordinal()); + COMUtils.checkRC(jnaControl.SetInterrupt(flags)); + } + + @Override + public int getInterruptTimeout() { + ULONGByReference pulSeconds = new ULONGByReference(); + COMUtils.checkRC(jnaControl.GetInterruptTimeout(pulSeconds)); + return pulSeconds.getValue().intValue(); + } + + @Override + public void setInterruptTimeout(int seconds) { + ULONG ulSeconds = new ULONG(seconds); + COMUtils.checkRC(jnaControl.SetInterruptTimeout(ulSeconds)); + } + + @Override + public void print(BitmaskSet levels, String message) { + ULONG mask = new ULONG(levels.getBitmask()); + COMUtils.checkRC(jnaControl.Output(mask, "%s", message)); + } + + @Override + public void println(BitmaskSet levels, String message) { + ULONG mask = new ULONG(levels.getBitmask()); + COMUtils.checkRC(jnaControl.Output(mask, "%s", message + "\r\n")); + } + + @Override + public void prompt(BitmaskSet ctl, String message) { + ULONG ctlMask = new ULONG(ctl.getBitmask()); + COMUtils.checkRC(jnaControl.OutputPrompt(ctlMask, "%s", message)); + } + + @Override + public String getPromptText() { + ULONGByReference pulTextSize = new ULONGByReference(); + COMUtils.checkRC(jnaControl.GetPromptText(null, new ULONG(0), pulTextSize)); + byte[] buffer = new byte[pulTextSize.getValue().intValue()]; + COMUtils.checkRC(jnaControl.GetPromptText(buffer, pulTextSize.getValue(), null)); + return Native.toString(buffer); + } + + protected DEBUG_VALUE doEval(DebugValueType type, String expression) { + DEBUG_VALUE.ByReference value = new DEBUG_VALUE.ByReference(); + ULONGByReference pulRemainder = new ULONGByReference(); + COMUtils.checkRC( + jnaControl.Evaluate(expression, new ULONG(type.ordinal()), value, pulRemainder)); + int remainder = pulRemainder.getValue().intValue(); + if (remainder != expression.length()) { + throw new RuntimeException("Failed to parse: " + expression.substring(remainder)); + } + return value; + } + + @Override + public T evaluate(Class desiredType, String expression) { + DebugValueType type = DebugValueType.getDebugValueTypeForClass(desiredType); + return doEval(type, expression).convertTo(desiredType); + } + + @Override + public void execute(BitmaskSet ctl, String cmd, + BitmaskSet flags) { + ULONG ctlMask = new ULONG(ctl.getBitmask()); + ULONG flagMask = new ULONG(flags.getBitmask()); + COMUtils.checkRC(jnaControl.Execute(ctlMask, cmd, flagMask)); + } + + @Override + public void returnInput(String input) { + COMUtils.checkRC(jnaControl.ReturnInput(input)); + } + + @Override + public DebugStatus getExecutionStatus() { + ULONGByReference pulStatus = new ULONGByReference(); + COMUtils.checkRC(jnaControl.GetExecutionStatus(pulStatus)); + return DebugStatus.values()[pulStatus.getValue().intValue()]; + } + + @Override + public void setExecutionStatus(DebugStatus status) { + ULONG ulStatus = new ULONG(status.ordinal()); + HRESULT hr = jnaControl.SetExecutionStatus(ulStatus); + if (!hr.equals(COMUtilsExtra.E_ACCESS_DENIED)) { + COMUtils.checkRC(hr); + } + } + + public DebugBreakpoint doAddBreakpoint(BreakType type, ULONG ulDesiredId) { + ULONG ulType = new ULONG(type.ordinal()); + PointerByReference ppBp = new PointerByReference(); + COMUtils.checkRC(jnaControl.AddBreakpoint(ulType, ulDesiredId, ppBp)); + IDebugBreakpoint Bp = new WrapIDebugBreakpoint(ppBp.getValue()); + DebugBreakpoint bpt = + DebugBreakpointInternal.tryPreferredInterfaces(this, Bp::QueryInterface); + // AddRef or no? Probably not. + return bpt; + } + + @Override + public int getNumberBreakpoints() { + ULONGByReference ulNumber = new ULONGByReference(); + COMUtils.checkRC(jnaControl.GetNumberBreakpoints(ulNumber)); + return ulNumber.getValue().intValue(); + } + + @Override + public DebugBreakpoint getBreakpointByIndex(int index) { + ULONG ulIndex = new ULONG(index); + PointerByReference ppBp = new PointerByReference(); + COMUtils.checkRC(jnaControl.GetBreakpointByIndex(ulIndex, ppBp)); + IDebugBreakpoint Bp = new WrapIDebugBreakpoint(ppBp.getValue()); + DebugBreakpoint bpt = + DebugBreakpointInternal.tryPreferredInterfaces(this, Bp::QueryInterface); + // NOTE: Do not AddRef. dbgeng manages lifecycle + return bpt; + } + + @Override + public DebugBreakpoint getBreakpointById(int id) { + ULONG ulId = new ULONG(id); + PointerByReference ppBp = new PointerByReference(); + HRESULT hr = jnaControl.GetBreakpointById(ulId, ppBp); + if (hr.equals(COMUtilsExtra.E_NOINTERFACE)) { + return null; + } + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + return null; + } + COMUtils.checkRC(hr); + IDebugBreakpoint Bp = new WrapIDebugBreakpoint(ppBp.getValue()); + DebugBreakpoint bpt = + DebugBreakpointInternal.tryPreferredInterfaces(this, Bp::QueryInterface); + // NOTE: Do not AddRef. dbgeng manages lifecycle + return bpt; + } + + @Override + public DebugBreakpoint addBreakpoint(BreakType type, int desiredId) { + return doAddBreakpoint(type, new ULONG(desiredId)); + } + + @Override + public DebugBreakpoint addBreakpoint(BreakType type) { + return doAddBreakpoint(type, DbgEngUtil.DEBUG_ANY_ID); + } + + @Override + public void removeBreakpoint(IDebugBreakpoint comBpt) { + COMUtils.checkRC(jnaControl.RemoveBreakpoint(comBpt)); + } + + @Override + public void waitForEvent(int timeout) { + COMUtils.checkRC(jnaControl.WaitForEvent(new ULONG(0), new ULONG(timeout))); + } + + @Override + public DebugEventInformation getLastEventInformation() { + ULONGByReference pulType = new ULONGByReference(); + ULONGByReference pulProcessId = new ULONGByReference(); + ULONGByReference pulThreadId = new ULONGByReference(); + //PointerByReference pExtraInformation = new PointerByReference(); + ULONG ulExtraInformationSize = new ULONG(0); + ULONGByReference pulExtraInformationUsed = new ULONGByReference(); + //byte[] pstrDescription = new byte[0]; + ULONG ulDescriptionSize = new ULONG(0); + ULONGByReference pulDescriptionUsed = new ULONGByReference(); + COMUtils.checkRC(jnaControl.GetLastEventInformation(pulType, pulProcessId, pulThreadId, + null, ulExtraInformationSize, pulExtraInformationUsed, null, ulDescriptionSize, + pulDescriptionUsed)); + return new DebugEventInformation(pulType.getValue().intValue(), + pulProcessId.getValue().intValue(), pulThreadId.getValue().intValue()); + } + + @Override + public DebugStackInformation getStackTrace(long frameOffset, long stackOffset, + long instructionOffset) { + ULONGLONG ullFrameOffset = new ULONGLONG(frameOffset); + ULONGLONG ullStackOffset = new ULONGLONG(stackOffset); + ULONGLONG ullInstructionOffset = new ULONGLONG(instructionOffset); + ULONG ulFrameSize = new ULONG(100); + DEBUG_STACK_FRAME[] pParams = new DEBUG_STACK_FRAME[ulFrameSize.intValue()]; + ULONGByReference pulFramesFilled = new ULONGByReference(); + COMUtils.checkRC(jnaControl.GetStackTrace(ullFrameOffset, ullStackOffset, + ullInstructionOffset, pParams, ulFrameSize, pulFramesFilled)); + return new DebugStackInformation(pulFramesFilled.getValue().intValue(), pParams); + } + + @Override + public int getActualProcessorType() { + ULONGByReference ulType = new ULONGByReference(); + HRESULT hr = jnaControl.GetActualProcessorType(ulType); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + return -1; + } + COMUtils.checkRC(hr); + return ulType.getValue().intValue(); + } + + @Override + public int getEffectiveProcessorType() { + ULONGByReference ulType = new ULONGByReference(); + COMUtils.checkRC(jnaControl.GetEffectiveProcessorType(ulType)); + return ulType.getValue().intValue(); + } + + @Override + public int getExecutingProcessorType() { + ULONGByReference ulType = new ULONGByReference(); + COMUtils.checkRC(jnaControl.GetExecutingProcessorType(ulType)); + return ulType.getValue().intValue(); + } + + @Override + public int getDebuggeeType() { + ULONGByReference ulClass = new ULONGByReference(); + ULONGByReference ulQualifier = new ULONGByReference(); + COMUtils.checkRC(jnaControl.GetDebuggeeType(ulClass, ulQualifier)); + return ulClass.getValue().intValue(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl2.java new file mode 100644 index 0000000000..eb0f3f70db --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl2.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.control; + +import agent.dbgeng.jna.dbgeng.control.IDebugControl2; + +public class DebugControlImpl2 extends DebugControlImpl1 { + @SuppressWarnings("unused") + private final IDebugControl2 jnaControl; + + public DebugControlImpl2(IDebugControl2 jnaControl) { + super(jnaControl); + this.jnaControl = jnaControl; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl3.java new file mode 100644 index 0000000000..b194837d4b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl3.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.control; + +import agent.dbgeng.jna.dbgeng.control.IDebugControl3; + +public class DebugControlImpl3 extends DebugControlImpl2 { + @SuppressWarnings("unused") + private final IDebugControl3 jnaControl; + + public DebugControlImpl3(IDebugControl3 jnaControl) { + super(jnaControl); + this.jnaControl = jnaControl; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl4.java new file mode 100644 index 0000000000..50da82b8e5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl4.java @@ -0,0 +1,95 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.control; + +import com.sun.jna.Native; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; + +import agent.dbgeng.dbgeng.DebugValue.DebugValueType; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.control.IDebugControl4; + +import com.sun.jna.platform.win32.COM.COMUtils; + +import ghidra.comm.util.BitmaskSet; + +public class DebugControlImpl4 extends DebugControlImpl3 { + private final IDebugControl4 jnaControl; + + public DebugControlImpl4(IDebugControl4 jnaControl) { + super(jnaControl); + this.jnaControl = jnaControl; + } + + @Override + public void print(BitmaskSet levels, String message) { + ULONG mask = new ULONG(levels.getBitmask()); + COMUtils.checkRC(jnaControl.OutputWide(mask, new WString("%s"), new WString(message))); + } + + @Override + public void println(BitmaskSet levels, String message) { + ULONG mask = new ULONG(levels.getBitmask()); + COMUtils + .checkRC( + jnaControl.OutputWide(mask, new WString("%s"), new WString(message + "\r\n"))); + } + + @Override + public void prompt(BitmaskSet ctl, String message) { + ULONG ctlMask = new ULONG(ctl.getBitmask()); + COMUtils + .checkRC( + jnaControl.OutputPromptWide(ctlMask, new WString("%s"), new WString(message))); + } + + @Override + public String getPromptText() { + ULONGByReference pulTextSize = new ULONGByReference(); + COMUtils.checkRC(jnaControl.GetPromptTextWide(null, new ULONG(0), pulTextSize)); + char[] buffer = new char[pulTextSize.getValue().intValue()]; + COMUtils.checkRC(jnaControl.GetPromptTextWide(buffer, pulTextSize.getValue(), null)); + return Native.toString(buffer); + } + + @Override + protected DEBUG_VALUE doEval(DebugValueType type, String expression) { + DEBUG_VALUE.ByReference value = new DEBUG_VALUE.ByReference(); + ULONGByReference pulRemainder = new ULONGByReference(); + COMUtils.checkRC(jnaControl.EvaluateWide(new WString(expression), new ULONG(type.ordinal()), + value, pulRemainder)); + int remainder = pulRemainder.getValue().intValue(); + if (remainder != expression.length()) { + throw new RuntimeException("Failed to parse: " + expression.substring(remainder)); + } + return value; + } + + @Override + public void execute(BitmaskSet ctl, String cmd, + BitmaskSet flags) { + ULONG ctlMask = new ULONG(ctl.getBitmask()); + ULONG flagMask = new ULONG(flags.getBitmask()); + COMUtils.checkRC(jnaControl.ExecuteWide(ctlMask, new WString(cmd), flagMask)); + } + + @Override + public void returnInput(String input) { + COMUtils.checkRC(jnaControl.ReturnInputWide(new WString(input))); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl5.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl5.java new file mode 100644 index 0000000000..9b501e4d45 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl5.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.control; + +import agent.dbgeng.jna.dbgeng.control.IDebugControl5; + +public class DebugControlImpl5 extends DebugControlImpl4 { + @SuppressWarnings("unused") + private final IDebugControl5 jnaControl; + + public DebugControlImpl5(IDebugControl5 jnaControl) { + super(jnaControl); + this.jnaControl = jnaControl; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl6.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl6.java new file mode 100644 index 0000000000..83186a2f5b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl6.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.control; + +import agent.dbgeng.jna.dbgeng.control.IDebugControl6; + +public class DebugControlImpl6 extends DebugControlImpl5 { + @SuppressWarnings("unused") + private final IDebugControl6 jnaControl; + + public DebugControlImpl6(IDebugControl6 jnaControl) { + super(jnaControl); + this.jnaControl = jnaControl; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl7.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl7.java new file mode 100644 index 0000000000..b026dc7bed --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl7.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.control; + +import agent.dbgeng.jna.dbgeng.control.IDebugControl7; + +public class DebugControlImpl7 extends DebugControlImpl6 { + @SuppressWarnings("unused") + private final IDebugControl7 jnaControl; + + public DebugControlImpl7(IDebugControl7 jnaControl) { + super(jnaControl); + this.jnaControl = jnaControl; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlInternal.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlInternal.java new file mode 100644 index 0000000000..4e26bf7db6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlInternal.java @@ -0,0 +1,81 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.control; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgeng.dbgeng.DebugControl; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier; +import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint; +import agent.dbgeng.jna.dbgeng.control.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugControlInternal extends DebugControl { + Map CACHE = new WeakValueHashMap<>(); + + static DebugControlInternal instanceFor(WrapIDebugControl control) { + return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl1::new); + } + + static DebugControlInternal instanceFor(WrapIDebugControl2 control) { + return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl2::new); + } + + static DebugControlInternal instanceFor(WrapIDebugControl3 control) { + return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl3::new); + } + + static DebugControlInternal instanceFor(WrapIDebugControl4 control) { + return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl4::new); + } + + static DebugControlInternal instanceFor(WrapIDebugControl5 control) { + return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl5::new); + } + + static DebugControlInternal instanceFor(WrapIDebugControl6 control) { + return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl6::new); + } + + static DebugControlInternal instanceFor(WrapIDebugControl7 control) { + return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl7::new); + } + + ImmutableMap.Builder> PREFERRED_CONTROL_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_CONTROL_IIDS = + PREFERRED_CONTROL_IIDS_BUILDER // + .put(new REFIID(IDebugControl7.IID_IDEBUG_CONTROL7), WrapIDebugControl7.class) // + .put(new REFIID(IDebugControl6.IID_IDEBUG_CONTROL6), WrapIDebugControl6.class) // + .put(new REFIID(IDebugControl5.IID_IDEBUG_CONTROL5), WrapIDebugControl5.class) // + .put(new REFIID(IDebugControl4.IID_IDEBUG_CONTROL4), WrapIDebugControl4.class) // + .put(new REFIID(IDebugControl3.IID_IDEBUG_CONTROL3), WrapIDebugControl3.class) // + .put(new REFIID(IDebugControl2.IID_IDEBUG_CONTROL2), WrapIDebugControl2.class) // + .put(new REFIID(IDebugControl.IID_IDEBUG_CONTROL), WrapIDebugControl.class) // + .build(); + + static DebugControlInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgEngUtil.tryPreferredInterfaces(DebugControlInternal.class, PREFERRED_CONTROL_IIDS, + supplier); + } + + void removeBreakpoint(IDebugBreakpoint comBpt); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl1.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl1.java new file mode 100644 index 0000000000..865fb7fd97 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl1.java @@ -0,0 +1,271 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.dataspaces; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +import javax.help.UnsupportedOperationException; + +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgeng.dbgeng.COMUtilsExtra; +import agent.dbgeng.dbgeng.DbgEng; +import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable; +import agent.dbgeng.jna.dbgeng.dataspaces.IDebugDataSpaces; + +public class DebugDataSpacesImpl1 implements DebugDataSpacesInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanble; + private final IDebugDataSpaces jnaData; + + public DebugDataSpacesImpl1(IDebugDataSpaces jnaData) { + this.cleanble = DbgEng.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public DebugMemoryBasicInformation queryVirtual(long offset) { + throw new UnsupportedOperationException("Not implemented in this interface"); + } + + @Override + public int readVirtual(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + HRESULT hr = jnaData.ReadVirtual(ullOffset, into, ulLen, pulBytesRead); + if (hr.equals(COMUtilsExtra.E_CANNOT_READ)) { + return 0; + } + COMUtils.checkRC(hr); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeVirtual(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtual(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int readVirtualUncached(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtualUncached(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeVirtualUncached(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtualUncached(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int readPhysical(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadPhysical(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writePhysical(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WritePhysical(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int readControl(int processor, long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONG ulProcessor = new ULONG(processor); + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadControl(ulProcessor, ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeControl(int processor, long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONG ulProcessor = new ULONG(processor); + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC( + jnaData.WriteControl(ulProcessor, ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int readBusData(int busDataType, int busNumber, int slotNumber, long offset, + ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONG ulBusDataType = new ULONG(busDataType); + ULONG ulBusNumber = new ULONG(busNumber); + ULONG ulSlotNumber = new ULONG(slotNumber); + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadBusData(ulBusDataType, ulBusNumber, ulSlotNumber, ullOffset, + into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeBusData(int busDataType, int busNumber, int slotNumber, long offset, + ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONG ulBusDataType = new ULONG(busDataType); + ULONG ulBusNumber = new ULONG(busNumber); + ULONG ulSlotNumber = new ULONG(slotNumber); + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteBusData(ulBusDataType, ulBusNumber, ulSlotNumber, ullOffset, + from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int readIo(int interfaceType, int busNumber, int addressSpace, long offset, + ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONG ulInterfaceType = new ULONG(interfaceType); + ULONG ulBusNumber = new ULONG(busNumber); + ULONG ulAddressSpace = new ULONG(addressSpace); + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadIo(ulInterfaceType, ulBusNumber, ulAddressSpace, ullOffset, + into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeIo(int interfaceType, int busNumber, int addressSpace, long offset, + ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONG ulInterfaceType = new ULONG(interfaceType); + ULONG ulBusNumber = new ULONG(busNumber); + ULONG ulAddressSpace = new ULONG(addressSpace); + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteIo(ulInterfaceType, ulBusNumber, ulAddressSpace, ullOffset, + from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public long readMsr(int msr) { + ULONG ulNumber = new ULONG(msr); + ULONGLONGByReference pulValue = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.ReadMsr(ulNumber, pulValue)); + return pulValue.getValue().longValue(); + } + + @Override + public void writeMsr(int msr, long value) { + ULONG ulNumber = new ULONG(msr); + ULONGLONG ullValue = new ULONGLONG(value); + COMUtils.checkRC(jnaData.WriteMsr(ulNumber, ullValue)); + } + + @Override + public int readDebuggerData(int offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONG ulOffset = new ULONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadDebuggerData(ulOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl2.java new file mode 100644 index 0000000000..6517b798dd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl2.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.dataspaces; + +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgeng.dbgeng.COMUtilsExtra; +import agent.dbgeng.jna.dbgeng.WinNTExtra.MEMORY_BASIC_INFORMATION64; +import agent.dbgeng.jna.dbgeng.dataspaces.IDebugDataSpaces2; +import ghidra.comm.util.BitmaskSet; + +public class DebugDataSpacesImpl2 extends DebugDataSpacesImpl1 { + private final IDebugDataSpaces2 jnaData; + + public DebugDataSpacesImpl2(IDebugDataSpaces2 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public DebugMemoryBasicInformation queryVirtual(long offset) { + ULONGLONG ullOffset = new ULONGLONG(offset); + MEMORY_BASIC_INFORMATION64.ByReference pInfo = new MEMORY_BASIC_INFORMATION64.ByReference(); + HRESULT hr = jnaData.QueryVirtual(ullOffset, pInfo); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + return null; + } + COMUtils.checkRC(hr); + + return new DebugMemoryBasicInformation(pInfo.BaseAddress.longValue(), + pInfo.AllocationBase.longValue(), + new BitmaskSet<>(PageProtection.class, pInfo.AllocationProtect.intValue()), + pInfo.RegionSize.longValue(), PageState.byValue(pInfo.State.intValue()), + new BitmaskSet<>(PageProtection.class, pInfo.Protect.intValue()), + PageType.byValue(pInfo.Type.intValue())); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl3.java new file mode 100644 index 0000000000..8e0d8a7e88 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl3.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.dataspaces; + +import agent.dbgeng.jna.dbgeng.dataspaces.IDebugDataSpaces3; + +public class DebugDataSpacesImpl3 extends DebugDataSpacesImpl2 { + @SuppressWarnings("unused") + private final IDebugDataSpaces3 jnaData; + + public DebugDataSpacesImpl3(IDebugDataSpaces3 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl4.java new file mode 100644 index 0000000000..a0128ddc5d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesImpl4.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.dataspaces; + +import agent.dbgeng.jna.dbgeng.dataspaces.IDebugDataSpaces4; + +public class DebugDataSpacesImpl4 extends DebugDataSpacesImpl3 { + @SuppressWarnings("unused") + private final IDebugDataSpaces4 jnaData; + + public DebugDataSpacesImpl4(IDebugDataSpaces4 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesInternal.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesInternal.java new file mode 100644 index 0000000000..1b517d3846 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/dataspaces/DebugDataSpacesInternal.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.dataspaces; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgeng.dbgeng.DebugDataSpaces; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier; +import agent.dbgeng.jna.dbgeng.dataspaces.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugDataSpacesInternal extends DebugDataSpaces { + Map CACHE = new WeakValueHashMap<>(); + + static DebugDataSpacesInternal instanceFor(WrapIDebugDataSpaces data) { + return DbgEngUtil.lazyWeakCache(CACHE, data, DebugDataSpacesImpl1::new); + } + + static DebugDataSpacesInternal instanceFor(WrapIDebugDataSpaces2 data) { + return DbgEngUtil.lazyWeakCache(CACHE, data, DebugDataSpacesImpl2::new); + } + + static DebugDataSpacesInternal instanceFor(WrapIDebugDataSpaces3 data) { + return DbgEngUtil.lazyWeakCache(CACHE, data, DebugDataSpacesImpl3::new); + } + + static DebugDataSpacesInternal instanceFor(WrapIDebugDataSpaces4 data) { + return DbgEngUtil.lazyWeakCache(CACHE, data, DebugDataSpacesImpl4::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugDataSpaces4.IID_IDEBUG_DATA_SPACES4), + WrapIDebugDataSpaces4.class) // + .put(new REFIID(IDebugDataSpaces3.IID_IDEBUG_DATA_SPACES3), + WrapIDebugDataSpaces3.class) // + .put(new REFIID(IDebugDataSpaces2.IID_IDEBUG_DATA_SPACES2), + WrapIDebugDataSpaces2.class) // + .put(new REFIID(IDebugDataSpaces.IID_IDEBUG_DATA_SPACES), + WrapIDebugDataSpaces.class) // + .build(); + + static DebugDataSpacesInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgEngUtil.tryPreferredInterfaces(DebugDataSpacesInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/event/WrapCallbackIDebugEventCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/event/WrapCallbackIDebugEventCallbacks.java new file mode 100644 index 0000000000..f1f5cdf90a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/event/WrapCallbackIDebugEventCallbacks.java @@ -0,0 +1,296 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.event; + +import java.util.ArrayList; +import java.util.List; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinError; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.IUnknown; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugClient.DebugStatus; +import agent.dbgeng.dbgeng.DebugClient.SessionStatus; +import agent.dbgeng.dbgeng.DebugEventCallbacks.DebugEvent; +import agent.dbgeng.impl.dbgeng.breakpoint.DebugBreakpointInternal; +import agent.dbgeng.impl.dbgeng.client.DebugClientImpl1; +import agent.dbgeng.impl.dbgeng.client.DebugClientInternal; +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint; +import agent.dbgeng.jna.dbgeng.event.*; +import ghidra.comm.util.BitmaskSet; +import ghidra.util.Msg; + +public class WrapCallbackIDebugEventCallbacks implements CallbackIDebugEventCallbacks { + private final DebugClientInternal client; + private final DebugEventCallbacks cb; + private ListenerIDebugEventCallbacks listener; + + public WrapCallbackIDebugEventCallbacks(DebugClientImpl1 client, DebugEventCallbacks cb) { + this.client = client; + this.cb = cb; + } + + public void setListener(ListenerIDebugEventCallbacks listener) { + this.listener = listener; + } + + @Override + public Pointer getPointer() { + return listener.getPointer(); + } + + @Override + public HRESULT QueryInterface(REFIID refid, PointerByReference ppvObject) { + if (null == ppvObject) { + return new HRESULT(WinError.E_POINTER); + } + else if (refid.getValue().equals(IDebugEventCallbacks.IID_IDEBUG_EVENT_CALLBACKS)) { + ppvObject.setValue(this.getPointer()); + return WinError.S_OK; + } + else if (refid.getValue().equals(IUnknown.IID_IUNKNOWN)) { + ppvObject.setValue(this.getPointer()); + return WinError.S_OK; + } + return new HRESULT(WinError.E_NOINTERFACE); + } + + @Override + public int AddRef() { + return 0; + } + + @Override + public int Release() { + return 0; + } + + @Override + public HRESULT GetInterestMask(ULONGByReference Mask) { + try { + BitmaskSet interest = cb.getInterestMask(); + ULONG ulInterest = new ULONG(interest.getBitmask()); + Mask.setValue(ulInterest); + return WinError.S_OK; + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT Breakpoint(WrapIDebugBreakpoint.ByReference Bp) { + try { + DebugBreakpoint bpt = DebugBreakpointInternal + .tryPreferredInterfaces(client.getControlInternal(), Bp::QueryInterface); + cb.breakpoint(bpt); + return WinError.S_OK; + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT Exception(EXCEPTION_RECORD64.ByReference Exception, ULONG FirstChance) { + try { + int numParams = Exception.NumberParameters.intValue(); + List information = new ArrayList<>(numParams); + for (int i = 0; i < numParams; i++) { + information.set(i, Exception.ExceptionInformation[i].longValue()); + } + DebugExceptionRecord64 exc = + new DebugExceptionRecord64(Exception.ExceptionCode.intValue(), + Exception.ExceptionFlags.intValue(), Exception.ExceptionRecord.longValue(), + Exception.ExceptionAddress.longValue(), information); + boolean firstChance = FirstChance.intValue() != 0; + cb.exception(exc, firstChance); + return WinError.S_OK; + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT CreateThread(ULONGLONG Handle, ULONGLONG DataOffset, ULONGLONG StartOffset) { + try { + DebugStatus status = cb.createThread(new DebugThreadInfo(Handle.longValue(), + DataOffset.longValue(), StartOffset.longValue())); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT ExitThread(ULONG ExitCode) { + try { + DebugStatus status = cb.exitThread(ExitCode.intValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT CreateProcess(ULONGLONG ImageFileHandle, ULONGLONG Handle, ULONGLONG BaseOffset, + ULONG ModuleSize, String ModuleName, String ImageName, ULONG CheckSum, + ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, ULONGLONG ThreadDataOffset, + ULONGLONG StartOffset) { + try { + // TODO: Associate thread with process + // TODO: Record All these other parameters? + DebugStatus status = cb.createProcess(new DebugProcessInfo(Handle.longValue(), + new DebugModuleInfo(ImageFileHandle.longValue(), BaseOffset.longValue(), + ModuleSize.intValue(), ModuleName, ImageName, CheckSum.intValue(), + TimeDateStamp.intValue()), + new DebugThreadInfo(InitialThreadHandle.longValue(), ThreadDataOffset.longValue(), + StartOffset.longValue()))); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT ExitProcess(ULONG ExitCode) { + try { + DebugStatus status = cb.exitProcess(ExitCode.intValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT LoadModule(ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, ULONG ModuleSize, + String ModuleName, String ImageName, ULONG CheckSum, ULONG TimeDateStamp) { + try { + // All of these are potentially null + long imageFileHandle = ImageFileHandle == null ? -1L : ImageFileHandle.longValue(); + long baseOffset = BaseOffset == null ? -1L : BaseOffset.longValue(); + int moduleSize = ModuleSize == null ? -1 : ModuleSize.intValue(); + String moduleName = ModuleName == null ? "" : ModuleName.toString(); + String imageName = ImageName == null ? "" : ImageName.toString(); + int checkSum = CheckSum == null ? -1 : CheckSum.intValue(); + + DebugStatus status = cb.loadModule(new DebugModuleInfo(imageFileHandle, baseOffset, + moduleSize, moduleName, imageName, checkSum, TimeDateStamp.intValue())); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT UnloadModule(String ImageBaseName, ULONGLONG BaseOffset) { + try { + DebugStatus status = cb.unloadModule(ImageBaseName, BaseOffset.longValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT SystemError(ULONG Error, ULONG Level) { + try { + DebugStatus status = cb.systemError(Error.intValue(), Level.intValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT SessionStatus(ULONG Status) { + try { + SessionStatus ss = SessionStatus.values()[Status.intValue()]; + DebugStatus status = cb.sessionStatus(ss); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT ChangeDebuggeeState(ULONG Flags, ULONGLONG Argument) { + try { + BitmaskSet flags = + new BitmaskSet<>(DebugClient.ChangeDebuggeeState.class, Flags.intValue()); + DebugStatus status = cb.changeDebuggeeState(flags, Argument.longValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT ChangeEngineState(ULONG Flags, ULONGLONG Argument) { + try { + BitmaskSet flags = + new BitmaskSet<>(DebugClient.ChangeEngineState.class, Flags.intValue()); + DebugStatus status = cb.changeEngineState(flags, Argument.longValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT ChangeSymbolState(ULONG Flags, ULONGLONG Argument) { + try { + BitmaskSet flags = + new BitmaskSet<>(DebugClient.ChangeSymbolState.class, Flags.intValue()); + DebugStatus status = cb.changeSymbolState(flags, Argument.longValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/event/WrapCallbackIDebugEventCallbacksWide.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/event/WrapCallbackIDebugEventCallbacksWide.java new file mode 100644 index 0000000000..f1b0b4ad08 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/event/WrapCallbackIDebugEventCallbacksWide.java @@ -0,0 +1,295 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.event; + +import java.util.ArrayList; +import java.util.List; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinError; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.IUnknown; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugClient.DebugStatus; +import agent.dbgeng.dbgeng.DebugClient.SessionStatus; +import agent.dbgeng.dbgeng.DebugEventCallbacks.DebugEvent; +import agent.dbgeng.impl.dbgeng.breakpoint.DebugBreakpointInternal; +import agent.dbgeng.impl.dbgeng.client.DebugClientImpl5; +import agent.dbgeng.impl.dbgeng.client.DebugClientInternal; +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint; +import agent.dbgeng.jna.dbgeng.event.*; +import ghidra.comm.util.BitmaskSet; +import ghidra.util.Msg; + +public class WrapCallbackIDebugEventCallbacksWide implements CallbackIDebugEventCallbacksWide { + private final DebugClientInternal client; + private final DebugEventCallbacks cb; + private ListenerIDebugEventCallbacksWide listener; + + public WrapCallbackIDebugEventCallbacksWide(DebugClientImpl5 client, DebugEventCallbacks cb) { + this.client = client; + this.cb = cb; + } + + public void setListener(ListenerIDebugEventCallbacksWide listener) { + this.listener = listener; + } + + @Override + public Pointer getPointer() { + return listener.getPointer(); + } + + @Override + public HRESULT QueryInterface(REFIID refid, PointerByReference ppvObject) { + if (null == ppvObject) { + return new HRESULT(WinError.E_POINTER); + } + else if (refid.getValue() + .equals(IDebugEventCallbacksWide.IID_IDEBUG_EVENT_CALLBACKS_WIDE)) { + ppvObject.setValue(this.getPointer()); + return WinError.S_OK; + } + else if (refid.getValue().equals(IUnknown.IID_IUNKNOWN)) { + ppvObject.setValue(this.getPointer()); + return WinError.S_OK; + } + return new HRESULT(WinError.E_NOINTERFACE); + } + + @Override + public int AddRef() { + return 0; + } + + @Override + public int Release() { + return 0; + } + + @Override + public HRESULT GetInterestMask(ULONGByReference Mask) { + try { + BitmaskSet interest = cb.getInterestMask(); + ULONG ulInterest = new ULONG(interest.getBitmask()); + Mask.setValue(ulInterest); + return WinError.S_OK; + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT Breakpoint(WrapIDebugBreakpoint.ByReference Bp) { + try { + DebugBreakpoint bpt = DebugBreakpointInternal + .tryPreferredInterfaces(client.getControlInternal(), Bp::QueryInterface); + cb.breakpoint(bpt); + return WinError.S_OK; + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT Exception(EXCEPTION_RECORD64.ByReference Exception, ULONG FirstChance) { + try { + int numParams = Exception.NumberParameters.intValue(); + List information = new ArrayList<>(numParams); + for (int i = 0; i < numParams; i++) { + information.add(Exception.ExceptionInformation[i].longValue()); + } + DebugExceptionRecord64 exc = + new DebugExceptionRecord64(Exception.ExceptionCode.intValue(), + Exception.ExceptionFlags.intValue(), Exception.ExceptionRecord.longValue(), + Exception.ExceptionAddress.longValue(), information); + boolean firstChance = FirstChance.intValue() != 0; + cb.exception(exc, firstChance); + return WinError.S_OK; + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT CreateThread(ULONGLONG Handle, ULONGLONG DataOffset, ULONGLONG StartOffset) { + try { + DebugStatus status = cb.createThread(new DebugThreadInfo(Handle.longValue(), + DataOffset.longValue(), StartOffset.longValue())); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT ExitThread(ULONG ExitCode) { + try { + DebugStatus status = cb.exitThread(ExitCode.intValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT CreateProcess(ULONGLONG ImageFileHandle, ULONGLONG Handle, ULONGLONG BaseOffset, + ULONG ModuleSize, WString ModuleName, WString ImageName, ULONG CheckSum, + ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, ULONGLONG ThreadDataOffset, + ULONGLONG StartOffset) { + try { + DebugStatus status = cb.createProcess(new DebugProcessInfo(Handle.longValue(), + new DebugModuleInfo(ImageFileHandle.longValue(), BaseOffset.longValue(), + ModuleSize.intValue(), ModuleName.toString(), ImageName.toString(), + CheckSum.intValue(), TimeDateStamp.intValue()), + new DebugThreadInfo(InitialThreadHandle.longValue(), ThreadDataOffset.longValue(), + StartOffset.longValue()))); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT ExitProcess(ULONG ExitCode) { + try { + DebugStatus status = cb.exitProcess(ExitCode.intValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT LoadModule(ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, ULONG ModuleSize, + WString ModuleName, WString ImageName, ULONG CheckSum, ULONG TimeDateStamp) { + try { + long imageFileHandle = ImageFileHandle == null ? -1L : ImageFileHandle.longValue(); + long baseOffset = BaseOffset == null ? -1L : BaseOffset.longValue(); + int moduleSize = ModuleSize == null ? -1 : ModuleSize.intValue(); + String moduleName = ModuleName == null ? "" : ModuleName.toString(); + String imageName = ImageName == null ? "" : ImageName.toString(); + int checkSum = CheckSum == null ? -1 : CheckSum.intValue(); + + DebugStatus status = cb.loadModule(new DebugModuleInfo(imageFileHandle, baseOffset, + moduleSize, moduleName, imageName, checkSum, TimeDateStamp.intValue())); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT UnloadModule(WString ImageBaseName, ULONGLONG BaseOffset) { + try { + DebugStatus status = cb.unloadModule(ImageBaseName.toString(), BaseOffset.longValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT SystemError(ULONG Error, ULONG Level) { + try { + DebugStatus status = cb.systemError(Error.intValue(), Level.intValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT SessionStatus(ULONG Status) { + try { + SessionStatus ss = SessionStatus.values()[Status.intValue()]; + DebugStatus status = cb.sessionStatus(ss); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT ChangeDebuggeeState(ULONG Flags, ULONGLONG Argument) { + try { + BitmaskSet flags = + new BitmaskSet<>(DebugClient.ChangeDebuggeeState.class, Flags.intValue()); + DebugStatus status = cb.changeDebuggeeState(flags, Argument.longValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT ChangeEngineState(ULONG Flags, ULONGLONG Argument) { + try { + BitmaskSet flags = + new BitmaskSet<>(DebugClient.ChangeEngineState.class, Flags.intValue()); + DebugStatus status = cb.changeEngineState(flags, Argument.longValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT ChangeSymbolState(ULONG Flags, ULONGLONG Argument) { + try { + BitmaskSet flags = + new BitmaskSet<>(DebugClient.ChangeSymbolState.class, Flags.intValue()); + DebugStatus status = cb.changeSymbolState(flags, Argument.longValue()); + return new HRESULT(status.ordinal()); + } + catch (Throwable e) { + Msg.error(this, "Error during callback", e); + return new HRESULT(WinError.E_UNEXPECTED); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugInputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugInputCallbacks.java new file mode 100644 index 0000000000..b5bead36fe --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugInputCallbacks.java @@ -0,0 +1,122 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.io; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinError; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.IUnknown; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.dbgeng.DebugInputCallbacks; +import agent.dbgeng.impl.dbgeng.client.DebugClientImpl1; +import agent.dbgeng.jna.dbgeng.io.*; +import ghidra.util.exception.CancelledException; + +public class WrapCallbackIDebugInputCallbacks implements CallbackIDebugInputCallbacks { + private final DebugClientImpl1 client; + private final DebugInputCallbacks cb; + private ListenerIDebugInputCallbacks listener; + + private final Set> futures = new HashSet<>(); // TODO: Just one? + + public WrapCallbackIDebugInputCallbacks(DebugClientImpl1 client, DebugInputCallbacks cb) { + this.client = client; + this.cb = cb; + } + + public void setListener(ListenerIDebugInputCallbacks listener) { + this.listener = listener; + } + + @Override + public Pointer getPointer() { + return listener.getPointer(); + } + + @Override + public HRESULT QueryInterface(REFIID refid, PointerByReference ppvObject) { + if (null == ppvObject) { + return new HRESULT(WinError.E_POINTER); + } + else if (refid.getValue().equals(IDebugInputCallbacks.IID_IDEBUG_INPUT_CALLBACKS)) { + ppvObject.setValue(this.getPointer()); + return WinError.S_OK; + } + else if (refid.getValue().equals(IUnknown.IID_IUNKNOWN)) { + ppvObject.setValue(this.getPointer()); + return WinError.S_OK; + } + return new HRESULT(WinError.E_NOINTERFACE); + } + + @Override + public int AddRef() { + return 0; + } + + @Override + public int Release() { + return 0; + } + + @Override + public HRESULT StartInput(ULONG BufferSize) { + try { + CompletableFuture future = cb.startInput(); + if (future == null) { + return WinError.S_OK; + } + future.handle((input, exc) -> { + if (exc == null) { + client.getControl().returnInput(input); + } + else if (exc instanceof CancelledException) { + // Normal if another client provides input + } + else { + client.getControl().errln("ERROR getting input: " + exc.getMessage()); + } + futures.remove(future); + return null; + }); + return WinError.S_OK; + } + catch (Throwable e) { + return new HRESULT(WinError.E_UNEXPECTED); + } + } + + @Override + public HRESULT EndInput() { + try { + for (CompletableFuture future : futures) { + future.cancel(true); + } + cb.endInput(); + return WinError.S_OK; + } + catch (Throwable e) { + return new HRESULT(WinError.E_UNEXPECTED); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugOutputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugOutputCallbacks.java new file mode 100644 index 0000000000..df0bb80d62 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugOutputCallbacks.java @@ -0,0 +1,82 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.io; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinError; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.IUnknown; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.dbgeng.DebugOutputCallbacks; +import agent.dbgeng.jna.dbgeng.io.*; + +public class WrapCallbackIDebugOutputCallbacks implements CallbackIDebugOutputCallbacks { + private final DebugOutputCallbacks cb; + private ListenerIDebugOutputCallbacks listener; + + public WrapCallbackIDebugOutputCallbacks(DebugOutputCallbacks cb) { + this.cb = cb; + } + + public void setListener(ListenerIDebugOutputCallbacks listener) { + this.listener = listener; + } + + @Override + public Pointer getPointer() { + return listener.getPointer(); + } + + @Override + public HRESULT QueryInterface(REFIID refid, PointerByReference ppvObject) { + if (null == ppvObject) { + return new HRESULT(WinError.E_POINTER); + } + else if (refid.getValue().equals(IDebugOutputCallbacks.IID_IDEBUG_OUTPUT_CALLBACKS)) { + ppvObject.setValue(this.getPointer()); + return WinError.S_OK; + } + else if (refid.getValue().equals(IUnknown.IID_IUNKNOWN)) { + ppvObject.setValue(this.getPointer()); + return WinError.S_OK; + } + return new HRESULT(WinError.E_NOINTERFACE); + } + + @Override + public int AddRef() { + return 0; + } + + @Override + public int Release() { + return 0; + } + + @Override + public HRESULT Output(ULONG Mask, String Text) { + try { + cb.output(Mask.intValue(), Text); + return WinError.S_OK; + } + catch (Throwable e) { + return new HRESULT(WinError.E_UNEXPECTED); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugOutputCallbacksWide.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugOutputCallbacksWide.java new file mode 100644 index 0000000000..c75c8cbeeb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugOutputCallbacksWide.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.io; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinError; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.IUnknown; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.dbgeng.DebugOutputCallbacks; +import agent.dbgeng.jna.dbgeng.io.*; + +public class WrapCallbackIDebugOutputCallbacksWide implements CallbackIDebugOutputCallbacksWide { + private final DebugOutputCallbacks cb; + private ListenerIDebugOutputCallbacksWide listener; + + public WrapCallbackIDebugOutputCallbacksWide(DebugOutputCallbacks cb) { + this.cb = cb; + } + + public void setListener(ListenerIDebugOutputCallbacksWide listener) { + this.listener = listener; + } + + @Override + public Pointer getPointer() { + return listener.getPointer(); + } + + @Override + public HRESULT QueryInterface(REFIID refid, PointerByReference ppvObject) { + if (null == ppvObject) { + return new HRESULT(WinError.E_POINTER); + } + else if (refid.getValue() + .equals(IDebugOutputCallbacksWide.IID_IDEBUG_OUTPUT_CALLBACKS_WIDE)) { + ppvObject.setValue(this.getPointer()); + return WinError.S_OK; + } + else if (refid.getValue().equals(IUnknown.IID_IUNKNOWN)) { + ppvObject.setValue(this.getPointer()); + return WinError.S_OK; + } + return new HRESULT(WinError.E_NOINTERFACE); + } + + @Override + public int AddRef() { + return 0; + } + + @Override + public int Release() { + return 0; + } + + @Override + public HRESULT Output(ULONG Mask, WString Text) { + try { + cb.output(Mask.intValue(), Text.toString()); + return WinError.S_OK; + } + catch (Throwable e) { + return new HRESULT(WinError.E_UNEXPECTED); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersImpl1.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersImpl1.java new file mode 100644 index 0000000000..20862be8d3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersImpl1.java @@ -0,0 +1,154 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.registers; + +import java.util.*; + +import com.sun.jna.Native; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable; +import agent.dbgeng.dbgeng.DebugValue.DebugValueType; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_REGISTER_DESCRIPTION; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.registers.IDebugRegisters; +import ghidra.comm.util.BitmaskSet; + +public class DebugRegistersImpl1 implements DebugRegistersInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugRegisters jnaRegisters; + + public DebugRegistersImpl1(IDebugRegisters jnaRegisters) { + this.cleanable = DbgEng.releaseWhenPhantom(this, jnaRegisters); + this.jnaRegisters = jnaRegisters; + } + + @Override + public int getNumberRegisters() { + ULONGByReference pulNumber = new ULONGByReference(); + COMUtils.checkRC(jnaRegisters.GetNumberRegisters(pulNumber)); + return pulNumber.getValue().intValue(); + } + + @Override + public DebugRegisterDescription getDescription(int registerIndex) { + ULONG ulRegIdx = new ULONG(registerIndex); + ULONGByReference pulNameSize = new ULONGByReference(); + COMUtils.checkRC( + jnaRegisters.GetDescription(ulRegIdx, null, new ULONG(0), pulNameSize, null)); + byte[] name = new byte[pulNameSize.getValue().intValue()]; + DEBUG_REGISTER_DESCRIPTION.ByReference desc = new DEBUG_REGISTER_DESCRIPTION.ByReference(); + COMUtils.checkRC( + jnaRegisters.GetDescription(ulRegIdx, name, pulNameSize.getValue(), null, desc)); + + return new DebugRegisterDescription(Native.toString(name), registerIndex, + DebugValueType.values()[desc.Type.intValue()], + new BitmaskSet<>(DebugRegisterFlags.class, desc.Flags.intValue()), + desc.SubregMaster.intValue(), desc.SubregLength.intValue(), desc.SubregMask.longValue(), + desc.SubregShift.intValue()); + } + + @Override + public int getIndexByName(String name) { + ULONGByReference pulIndex = new ULONGByReference(); + HRESULT hr = jnaRegisters.GetIndexByName(name, pulIndex); + if (hr.equals(COMUtilsExtra.E_NOINTERFACE)) { + // This happens for 32-bit WOW execution + return -1; + } + COMUtils.checkRC(hr); + return pulIndex.getValue().intValue(); + } + + @Override + public DebugValue getValue(int index) { + ULONG ulIndex = new ULONG(index); + DEBUG_VALUE.ByReference dvVal = new DEBUG_VALUE.ByReference(); + COMUtils.checkRC(jnaRegisters.GetValue(ulIndex, dvVal)); + return dvVal.convertTo(DebugValue.class); + } + + protected void doGetValues(DebugRegisterSource source, ULONG ulCount, ULONG[] pulIndices, + DEBUG_VALUE[] pValues) { + if (source != DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE) { + throw new IllegalArgumentException("This interface only permits DEBUG_REGSRC_DEBUGGEE"); + } + COMUtils.checkRC(jnaRegisters.GetValues(ulCount, pulIndices, new ULONG(0), pValues)); + } + + @Override + public Map getValues(DebugRegisterSource source, + Collection indices) { + if (source != DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE) { + throw new IllegalArgumentException("This interface only permits DEBUG_REGSRC_DEBUGGEE"); + } + if (indices.isEmpty()) { + return Collections.emptyMap(); + } + List li = new ArrayList<>(indices); + ULONG ulCount = new ULONG(li.size()); + ULONG[] pulIndices = new ULONG[li.size()]; + DEBUG_VALUE[] pValues = (DEBUG_VALUE[]) new DEBUG_VALUE().toArray(li.size()); + for (int i = 0; i < indices.size(); i++) { + pulIndices[i] = new ULONG(li.get(i)); + } + doGetValues(source, ulCount, pulIndices, pValues); + Map result = new LinkedHashMap<>(); + for (int i = 0; i < li.size(); i++) { + result.put(li.get(i), pValues[i].convertTo(DebugValue.class)); + } + return result; + } + + @Override + public void setValue(int index, DebugValue value) { + ULONG ulIndex = new ULONG(index); + DEBUG_VALUE.ByReference dvVal = new DEBUG_VALUE.ByReference(); + DEBUG_VALUE.fromDebugValue(dvVal, value); + COMUtils.checkRC(jnaRegisters.SetValue(ulIndex, dvVal)); + } + + protected void doSetValues(DebugRegisterSource source, ULONG ulCount, ULONG[] pulIndices, + DEBUG_VALUE[] pValues) { + if (source != DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE) { + throw new IllegalArgumentException("This interface only permits DEBUG_REGSRC_DEBUGGEE"); + } + + COMUtils.checkRC(jnaRegisters.SetValues(ulCount, pulIndices, new ULONG(0), pValues)); + } + + @Override + public void setValues(DebugRegisterSource source, Map values) { + if (values.isEmpty()) { + return; + } + ULONG ulCount = new ULONG(values.size()); + ULONG[] pulIndices = new ULONG[values.size()]; + DEBUG_VALUE[] pValues = (DEBUG_VALUE[]) new DEBUG_VALUE().toArray(values.size()); + int i = 0; + for (Map.Entry ent : values.entrySet()) { + pulIndices[i] = new ULONG(ent.getKey()); + DEBUG_VALUE.fromDebugValue(pValues[i], ent.getValue()); + i++; + } + doSetValues(source, ulCount, pulIndices, pValues); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersImpl2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersImpl2.java new file mode 100644 index 0000000000..6e1b01420c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersImpl2.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.registers; + +import com.sun.jna.platform.win32.WinDef.ULONG; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.registers.IDebugRegisters2; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugRegistersImpl2 extends DebugRegistersImpl1 { + private final IDebugRegisters2 jnaRegisters; + + public DebugRegistersImpl2(IDebugRegisters2 jnaRegisters) { + super(jnaRegisters); + this.jnaRegisters = jnaRegisters; + } + + @Override + protected void doGetValues(DebugRegisterSource source, ULONG ulCount, ULONG[] pulIndices, + DEBUG_VALUE[] pValues) { + ULONG ulSource = new ULONG(source.ordinal()); + COMUtils + .checkRC( + jnaRegisters.GetValues2(ulSource, ulCount, pulIndices, new ULONG(0), pValues)); + } + + @Override + protected void doSetValues(DebugRegisterSource source, ULONG ulCount, ULONG[] pulIndices, + DEBUG_VALUE[] pValues) { + ULONG ulSource = new ULONG(source.ordinal()); + COMUtils + .checkRC( + jnaRegisters.SetValues2(ulSource, ulCount, pulIndices, new ULONG(0), pValues)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersInternal.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersInternal.java new file mode 100644 index 0000000000..5289c79928 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/registers/DebugRegistersInternal.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.registers; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgeng.dbgeng.DebugRegisters; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier; +import agent.dbgeng.jna.dbgeng.registers.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugRegistersInternal extends DebugRegisters { + Map CACHE = new WeakValueHashMap<>(); + + static DebugRegistersInternal instanceFor(WrapIDebugRegisters registers) { + return DbgEngUtil.lazyWeakCache(CACHE, registers, DebugRegistersImpl1::new); + } + + static DebugRegistersInternal instanceFor(WrapIDebugRegisters2 registers) { + return DbgEngUtil.lazyWeakCache(CACHE, registers, DebugRegistersImpl2::new); + } + + ImmutableMap.Builder> PREFERRED_REGISTERS_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_REGISTERS_IIDS = + PREFERRED_REGISTERS_IIDS_BUILDER // + .put(new REFIID(IDebugRegisters2.IID_IDEBUG_REGISTERS2), WrapIDebugRegisters2.class) // + .put(new REFIID(IDebugRegisters.IID_IDEBUG_REGISTERS), WrapIDebugRegisters.class) // + .build(); + + static DebugRegistersInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgEngUtil.tryPreferredInterfaces(DebugRegistersInternal.class, + PREFERRED_REGISTERS_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugModuleImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugModuleImpl.java new file mode 100644 index 0000000000..0c7cf0e8cf --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugModuleImpl.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.symbols; + +import java.util.Objects; + +import agent.dbgeng.dbgeng.DebugModule; + +public class DebugModuleImpl implements DebugModule { + private final DebugSymbolsInternal symbols; + final int index; + final long base; + + DebugModuleImpl(DebugSymbolsInternal symbols, int index, long base) { + this.symbols = symbols; + this.index = index; + this.base = base; + } + + @Override + public int hashCode() { + return Objects.hash(symbols, index, base); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DebugModuleImpl)) { + return false; + } + DebugModuleImpl that = (DebugModuleImpl) obj; + if (!this.symbols.equals(that.symbols)) { + return false; + } + if (this.index != that.index) { + return false; + } + if (this.base != that.base) { + return false; + } + return true; + } + + @Override + public String getName(DebugModuleName which) { + return symbols.getModuleName(which, this); + } + + @Override + public int getIndex() { + return index; + } + + @Override + public long getBase() { + return base; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl1.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl1.java new file mode 100644 index 0000000000..456013c297 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl1.java @@ -0,0 +1,213 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.symbols; + +import java.util.Iterator; +import java.util.List; + +import com.sun.jna.Native; +import com.sun.jna.platform.win32.WinDef.*; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable; +import agent.dbgeng.dbgeng.DebugModule.DebugModuleName; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.jna.dbgeng.symbols.IDebugSymbols; + +import com.sun.jna.platform.win32.COM.COMException; +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugSymbolsImpl1 implements DebugSymbolsInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugSymbols jnaSymbols; + + public DebugSymbolsImpl1(IDebugSymbols jnaSymbols) { + this.cleanable = DbgEng.releaseWhenPhantom(this, jnaSymbols); + this.jnaSymbols = jnaSymbols; + } + + @Override + public int getNumberLoadedModules() { + ULONGByReference pulLoaded = new ULONGByReference(); + ULONGByReference pulUnloaded = new ULONGByReference(); + COMUtils.checkRC(jnaSymbols.GetNumberModules(pulLoaded, pulUnloaded)); + return pulLoaded.getValue().intValue(); + } + + @Override + public int getNumberUnloadedModules() { + ULONGByReference pulLoaded = new ULONGByReference(); + ULONGByReference pulUnloaded = new ULONGByReference(); + COMUtils.checkRC(jnaSymbols.GetNumberModules(pulLoaded, pulUnloaded)); + return pulUnloaded.getValue().intValue(); + } + + @Override + public DebugModule getModuleByIndex(int index) { + ULONG ulIndex = new ULONG(index); + ULONGLONGByReference pullBase = new ULONGLONGByReference(); + COMUtils.checkRC(jnaSymbols.GetModuleByIndex(ulIndex, pullBase)); + return new DebugModuleImpl(this, index, pullBase.getValue().longValue()); + } + + @Override + public DebugModule getModuleByModuleName(String name, int startIndex) { + ULONG ulStartIndex = new ULONG(startIndex); + ULONGByReference pulIndex = new ULONGByReference(); + ULONGLONGByReference pullBase = new ULONGLONGByReference(); + COMUtils.checkRC(jnaSymbols.GetModuleByModuleName(name, ulStartIndex, pulIndex, pullBase)); + return new DebugModuleImpl(this, pulIndex.getValue().intValue(), + pullBase.getValue().longValue()); + } + + @Override + public DebugModule getModuleByOffset(long offset, int startIndex) { + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulStartIndex = new ULONG(startIndex); + ULONGByReference pulIndex = new ULONGByReference(); + ULONGLONGByReference pullBase = new ULONGLONGByReference(); + COMUtils.checkRC(jnaSymbols.GetModuleByOffset(ullOffset, ulStartIndex, pulIndex, pullBase)); + return new DebugModuleImpl(this, pulIndex.getValue().intValue(), + pullBase.getValue().longValue()); + } + + protected void callNamesForWhich(DebugModuleName which, ULONG Index, ULONGLONG Base, + byte[] Buffer, ULONG BufferSize, ULONGByReference NameSize) { + switch (which) { + case IMAGE: + COMUtils.checkRC(jnaSymbols.GetModuleNames(Index, Base, Buffer, BufferSize, + NameSize, null, new ULONG(0), null, null, new ULONG(0), null)); + case MODULE: + COMUtils.checkRC(jnaSymbols.GetModuleNames(Index, Base, null, new ULONG(0), null, + Buffer, BufferSize, NameSize, null, new ULONG(0), null)); + case LOADED_IMAGE: + COMUtils.checkRC(jnaSymbols.GetModuleNames(Index, Base, null, new ULONG(0), null, + null, new ULONG(0), null, Buffer, BufferSize, NameSize)); + default: + throw new UnsupportedOperationException("Interface does not support " + which); + } + } + + @Override + public String getModuleName(DebugModuleName which, DebugModule module) { + ULONGLONG ullBase = new ULONGLONG(module.getBase()); + ULONGByReference pulNameSize = new ULONGByReference(); + callNamesForWhich(which, DbgEngUtil.DEBUG_ANY_ID, ullBase, null, new ULONG(0), pulNameSize); + byte[] aBuffer = new byte[pulNameSize.getValue().intValue()]; + callNamesForWhich(which, DbgEngUtil.DEBUG_ANY_ID, ullBase, aBuffer, pulNameSize.getValue(), + null); + return Native.toString(aBuffer); + } + + @Override + public Iterable iterateSymbolMatches(String pattern) { + ULONGLONGByReference pullHandle = new ULONGLONGByReference(); + return new Iterable() { + @Override + public Iterator iterator() { + COMUtils.checkRC(jnaSymbols.StartSymbolMatch(pattern, pullHandle)); + return new Iterator() { + ULONGByReference pulMatchSize = new ULONGByReference(); + ULONGLONGByReference pullOffset = new ULONGLONGByReference(); + + @Override + public boolean hasNext() { + try { + COMUtils.checkRC(jnaSymbols.GetNextSymbolMatch(pullHandle.getValue(), + null, new ULONG(0), pulMatchSize, null)); + } + catch (COMException e) { + if (!COMUtilsExtra.isE_NOINTERFACE(e)) { + throw e; + } + return false; + } + return true; + } + + @Override + public DebugSymbolName next() { + try { + if (pulMatchSize.getValue().intValue() == 0) { + COMUtils.checkRC(jnaSymbols.GetNextSymbolMatch( + pullHandle.getValue(), null, new ULONG(0), pulMatchSize, null)); + } + byte[] aBuffer = new byte[pulMatchSize.getValue().intValue()]; + COMUtils.checkRC(jnaSymbols.GetNextSymbolMatch(pullHandle.getValue(), + aBuffer, pulMatchSize.getValue(), null, pullOffset)); + return new DebugSymbolName(Native.toString(aBuffer), + pullOffset.getValue().longValue()); + } + catch (COMException e) { + if (!COMUtilsExtra.isE_NOINTERFACE(e)) { + throw e; + } + return null; + } + finally { + pulMatchSize.getValue().setValue(0); + } + } + + @Override + protected void finalize() throws Throwable { + COMUtils.checkRC(jnaSymbols.EndSymbolMatch(pullHandle.getValue())); + } + }; + } + }; + } + + @Override + public List getSymbolIdsByName(String pattern) { + throw new UnsupportedOperationException("Not supported by this interface"); + } + + @Override + public DebugSymbolEntry getSymbolEntry(DebugSymbolId id) { + throw new UnsupportedOperationException("Not supported by this interface"); + } + + @Override + public String getSymbolPath() { + ULONGByReference pulPathLength = new ULONGByReference(); + COMUtils.checkRC(jnaSymbols.GetSymbolPath(null, new ULONG(0), pulPathLength)); + byte[] aBuffer = new byte[pulPathLength.getValue().intValue()]; + COMUtils.checkRC(jnaSymbols.GetSymbolPath(aBuffer, pulPathLength.getValue(), null)); + return Native.toString(aBuffer); + } + + @Override + public void setSymbolPath(String path) { + //WString wPath = new WString(path); + COMUtils.checkRC(jnaSymbols.SetSymbolPath(path)); + } + + @Override + public int getSymbolOptions() { + ULONGByReference pulOptions = new ULONGByReference(); + COMUtils.checkRC(jnaSymbols.GetSymbolPath(null, new ULONG(0), pulOptions)); + return pulOptions.getValue().intValue(); + } + + @Override + public void setSymbolOptions(int options) { + ULONG ulOptions = new ULONG(options); + COMUtils.checkRC(jnaSymbols.SetSymbolOptions(ulOptions)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl2.java new file mode 100644 index 0000000000..605bc4e534 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl2.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.symbols; + +import com.sun.jna.Native; +import com.sun.jna.platform.win32.WinDef.*; + +import agent.dbgeng.dbgeng.DebugModule; +import agent.dbgeng.dbgeng.DebugModule.DebugModuleName; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.jna.dbgeng.symbols.IDebugSymbols2; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugSymbolsImpl2 extends DebugSymbolsImpl1 { + private final IDebugSymbols2 jnaSymbols; + + public DebugSymbolsImpl2(IDebugSymbols2 jnaSymbols) { + super(jnaSymbols); + this.jnaSymbols = jnaSymbols; + } + + @Override + public String getModuleName(DebugModuleName which, DebugModule module) { + ULONG ulWhich = new ULONG(which.ordinal()); + ULONGLONG ullBase = new ULONGLONG(module.getBase()); + ULONGByReference pulNameSize = new ULONGByReference(); + COMUtils.checkRC(jnaSymbols.GetModuleNameString(ulWhich, DbgEngUtil.DEBUG_ANY_ID, ullBase, + null, new ULONG(0), pulNameSize)); + byte[] aBuffer = new byte[pulNameSize.getValue().intValue()]; + COMUtils.checkRC(jnaSymbols.GetModuleNameString(ulWhich, DbgEngUtil.DEBUG_ANY_ID, ullBase, + aBuffer, pulNameSize.getValue(), null)); + return Native.toString(aBuffer); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl3.java new file mode 100644 index 0000000000..0281ea0352 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl3.java @@ -0,0 +1,101 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.symbols; + +import java.util.*; + +import com.sun.jna.Native; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinDef.*; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugModule.DebugModuleName; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_MODULE_AND_ID; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_SYMBOL_ENTRY; +import agent.dbgeng.jna.dbgeng.symbols.IDebugSymbols3; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugSymbolsImpl3 extends DebugSymbolsImpl2 { + private final IDebugSymbols3 jnaSymbols; + + public DebugSymbolsImpl3(IDebugSymbols3 jnaSymbols) { + super(jnaSymbols); + this.jnaSymbols = jnaSymbols; + } + + @Override + public DebugModule getModuleByModuleName(String name, int startIndex) { + ULONG ulStartIndex = new ULONG(startIndex); + ULONGByReference pulIndex = new ULONGByReference(); + ULONGLONGByReference pullBase = new ULONGLONGByReference(); + COMUtils.checkRC(jnaSymbols.GetModuleByModuleNameWide(new WString(name), ulStartIndex, + pulIndex, pullBase)); + return new DebugModuleImpl(this, pulIndex.getValue().intValue(), + pullBase.getValue().longValue()); + } + + @Override + public String getModuleName(DebugModuleName which, DebugModule module) { + ULONG ulWhich = new ULONG(which.ordinal()); + ULONGLONG ullBase = new ULONGLONG(module.getBase()); + ULONGByReference pulNameSize = new ULONGByReference(); + COMUtils.checkRC(jnaSymbols.GetModuleNameStringWide(ulWhich, DbgEngUtil.DEBUG_ANY_ID, + ullBase, null, new ULONG(0), pulNameSize)); + char[] aBuffer = new char[pulNameSize.getValue().intValue()]; + COMUtils.checkRC(jnaSymbols.GetModuleNameStringWide(ulWhich, DbgEngUtil.DEBUG_ANY_ID, + ullBase, aBuffer, pulNameSize.getValue(), null)); + return Native.toString(aBuffer); + } + + @Override + public List getSymbolIdsByName(String pattern) { + ULONGByReference pulEntries = new ULONGByReference(); + WString wsPattern = new WString(pattern); + COMUtils.checkRC(jnaSymbols.GetSymbolEntriesByNameWide(wsPattern, new ULONG(0), null, + new ULONG(0), pulEntries)); + if (pulEntries.getValue().intValue() == 0) { + return Collections.emptyList(); + } + DEBUG_MODULE_AND_ID[] aIds = (DEBUG_MODULE_AND_ID[]) new DEBUG_MODULE_AND_ID() + .toArray(pulEntries.getValue().intValue()); + COMUtils.checkRC(jnaSymbols.GetSymbolEntriesByNameWide(wsPattern, new ULONG(0), aIds, + pulEntries.getValue(), null)); + List result = new ArrayList<>(aIds.length); + for (int i = 0; i < aIds.length; i++) { + result.add(new DebugSymbolId(aIds[i].ModuleBase.longValue(), aIds[i].Id.longValue())); + } + return result; + } + + @Override + public DebugSymbolEntry getSymbolEntry(DebugSymbolId id) { + DEBUG_MODULE_AND_ID sId = new DEBUG_MODULE_AND_ID(); + sId.ModuleBase = new ULONGLONG(id.moduleBase); + sId.Id = new ULONGLONG(id.symbolIndex); + DEBUG_SYMBOL_ENTRY.ByReference pInfo = new DEBUG_SYMBOL_ENTRY.ByReference(); + COMUtils.checkRC(jnaSymbols.GetSymbolEntryInformation(sId, pInfo)); + // Get the name while I'm here + char[] aName = new char[pInfo.NameSize.intValue() + 1]; + COMUtils.checkRC(jnaSymbols.GetSymbolEntryStringWide(sId, new ULONG(0), aName, + new ULONG(aName.length), null)); + return new DebugSymbolEntry(pInfo.ModuleBase.longValue(), pInfo.Offset.longValue(), + pInfo.Id.longValue(), pInfo.Size.longValue(), pInfo.Flags.intValue(), + pInfo.TypeId.intValue()/* TODO */, Native.toString(aName), + pInfo.Tag.intValue()/* TODO */); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl4.java new file mode 100644 index 0000000000..80274beb28 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl4.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.symbols; + +import agent.dbgeng.jna.dbgeng.symbols.IDebugSymbols4; + +public class DebugSymbolsImpl4 extends DebugSymbolsImpl3 { + @SuppressWarnings("unused") + private final IDebugSymbols4 jnaSymbols; + + public DebugSymbolsImpl4(IDebugSymbols4 jnaSymbols) { + super(jnaSymbols); + this.jnaSymbols = jnaSymbols; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl5.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl5.java new file mode 100644 index 0000000000..a4534dc7fa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsImpl5.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.symbols; + +import agent.dbgeng.jna.dbgeng.symbols.IDebugSymbols5; + +public class DebugSymbolsImpl5 extends DebugSymbolsImpl4 { + @SuppressWarnings("unused") + private final IDebugSymbols5 jnaSymbols; + + public DebugSymbolsImpl5(IDebugSymbols5 jnaSymbols) { + super(jnaSymbols); + this.jnaSymbols = jnaSymbols; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsInternal.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsInternal.java new file mode 100644 index 0000000000..c9a84b7eb5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/symbols/DebugSymbolsInternal.java @@ -0,0 +1,72 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.symbols; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgeng.dbgeng.DebugModule; +import agent.dbgeng.dbgeng.DebugSymbols; +import agent.dbgeng.dbgeng.DebugModule.DebugModuleName; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier; +import agent.dbgeng.jna.dbgeng.symbols.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugSymbolsInternal extends DebugSymbols { + final Map CACHE = new WeakValueHashMap<>(); + + static DebugSymbolsInternal instanceFor(WrapIDebugSymbols symbols) { + return DbgEngUtil.lazyWeakCache(CACHE, symbols, DebugSymbolsImpl1::new); + } + + static DebugSymbolsInternal instanceFor(WrapIDebugSymbols2 symbols) { + return DbgEngUtil.lazyWeakCache(CACHE, symbols, DebugSymbolsImpl2::new); + } + + static DebugSymbolsInternal instanceFor(WrapIDebugSymbols3 symbols) { + return DbgEngUtil.lazyWeakCache(CACHE, symbols, DebugSymbolsImpl3::new); + } + + static DebugSymbolsInternal instanceFor(WrapIDebugSymbols4 symbols) { + return DbgEngUtil.lazyWeakCache(CACHE, symbols, DebugSymbolsImpl4::new); + } + + static DebugSymbolsInternal instanceFor(WrapIDebugSymbols5 symbols) { + return DbgEngUtil.lazyWeakCache(CACHE, symbols, DebugSymbolsImpl5::new); + } + + ImmutableMap.Builder> PREFERRED_SYMBOLS_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFFERED_SYMBOLS_IIDS = + PREFERRED_SYMBOLS_IIDS_BUILDER // + .put(new REFIID(IDebugSymbols5.IID_IDEBUG_SYMBOLS5), WrapIDebugSymbols5.class) // + .put(new REFIID(IDebugSymbols4.IID_IDEBUG_SYMBOLS4), WrapIDebugSymbols4.class) // + .put(new REFIID(IDebugSymbols3.IID_IDEBUG_SYMBOLS3), WrapIDebugSymbols3.class) // + .put(new REFIID(IDebugSymbols2.IID_IDEBUG_SYMBOLS2), WrapIDebugSymbols2.class) // + .put(new REFIID(IDebugSymbols.IID_IDEBUG_SYMBOLS), WrapIDebugSymbols.class) // + .build(); + + static DebugSymbolsInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgEngUtil.tryPreferredInterfaces(DebugSymbolsInternal.class, PREFFERED_SYMBOLS_IIDS, + supplier); + } + + String getModuleName(DebugModuleName which, DebugModule module); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl1.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl1.java new file mode 100644 index 0000000000..65e60fb6a9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl1.java @@ -0,0 +1,249 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.sysobj; + +import java.util.*; + +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable; +import agent.dbgeng.jna.dbgeng.sysobj.IDebugSystemObjects; + +public class DebugSystemObjectsImpl1 implements DebugSystemObjectsInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugSystemObjects jnaSysobj; + + public DebugSystemObjectsImpl1(IDebugSystemObjects jnaSysobj) { + this.cleanable = DbgEng.releaseWhenPhantom(this, jnaSysobj); + this.jnaSysobj = jnaSysobj; + } + + @Override + public DebugThreadId getEventThread() { + ULONGByReference pulId = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetEventThread(pulId); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + return new DebugThreadId(-1); + } + COMUtils.checkRC(hr); + return new DebugThreadId(pulId.getValue().intValue()); + } + + @Override + public DebugProcessId getEventProcess() { + ULONGByReference pulId = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetEventProcess(pulId); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + return new DebugProcessId(-1); + } + COMUtils.checkRC(hr); + return new DebugProcessId(pulId.getValue().intValue()); + } + + @Override + public DebugThreadId getCurrentThreadId() { + ULONGByReference pulId = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetCurrentThreadId(pulId); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + return new DebugThreadId(-1); + } + COMUtils.checkRC(hr); + return new DebugThreadId(pulId.getValue().intValue()); + } + + @Override + public void setCurrentThreadId(DebugThreadId id) { + HRESULT hr = jnaSysobj.SetCurrentThreadId(new ULONG(id.id)); + if (!hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + COMUtils.checkRC(hr); + } + } + + @Override + public DebugProcessId getCurrentProcessId() { + ULONGByReference pulId = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetCurrentProcessId(pulId); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + return new DebugProcessId(-1); + } + COMUtils.checkRC(hr); + return new DebugProcessId(pulId.getValue().intValue()); + } + + @Override + public void setCurrentProcessId(DebugProcessId id) { + HRESULT hr = jnaSysobj.SetCurrentProcessId(new ULONG(id.id)); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + //System.err.println("Failure on setCurrentProcessId(" + id + ")"); + return; + } + if (hr.equals(COMUtilsExtra.E_NOINTERFACE)) { + return; + } + COMUtils.checkRC(hr); + } + + @Override + public int getNumberThreads() { + ULONGByReference pulNumber = new ULONGByReference(); + COMUtils.checkRC(jnaSysobj.GetNumberThreads(pulNumber)); + return pulNumber.getValue().intValue(); + } + + @Override + public int getTotalNumberThreads() { + ULONGByReference pulTotal = new ULONGByReference(); + ULONGByReference pulLargestProcess = new ULONGByReference(); + COMUtils.checkRC(jnaSysobj.GetTotalNumberThreads(pulTotal, pulLargestProcess)); + return pulTotal.getValue().intValue(); + } + + @Override + public List getThreads(int start, int count) { + if (count == 0) { + return Collections.emptyList(); + } + // TODO: Does dbgeng do the bounds checking? + ULONG ulStart = new ULONG(start); + ULONG ulCount = new ULONG(count); + ULONG[] aulIds = new ULONG[count]; + COMUtils.checkRC(jnaSysobj.GetThreadIdsByIndex(ulStart, ulCount, aulIds, null)); + List result = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + result.add(new DebugThreadId(aulIds[i].intValue())); + } + return result; + } + + @Override + public DebugThreadId getThreadIdByHandle(long handle) { + ULONGLONG ullHandle = new ULONGLONG(handle); + ULONGByReference pulId = new ULONGByReference(); + COMUtils.checkRC(jnaSysobj.GetThreadIdByHandle(ullHandle, pulId)); + return new DebugThreadId(pulId.getValue().intValue()); + } + + @Override + public DebugThreadId getThreadIdBySystemId(int systemId) { + ULONG ulHandle = new ULONG(systemId); + ULONGByReference pulId = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetThreadIdBySystemId(ulHandle, pulId); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED) || hr.equals(COMUtilsExtra.E_NOTIMPLEMENTED)) { + return null; + } + COMUtils.checkRC(hr); + return new DebugThreadId(pulId.getValue().intValue()); + } + + @Override + public DebugProcessId getProcessIdByHandle(long handle) { + ULONGLONG ullHandle = new ULONGLONG(handle); + ULONGByReference pulId = new ULONGByReference(); + COMUtils.checkRC(jnaSysobj.GetProcessIdByHandle(ullHandle, pulId)); + return new DebugProcessId(pulId.getValue().intValue()); + } + + @Override + public DebugProcessId getProcessIdBySystemId(int systemId) { + ULONG ulHandle = new ULONG(systemId); + ULONGByReference pulId = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetProcessIdBySystemId(ulHandle, pulId); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED) || hr.equals(COMUtilsExtra.E_NOTIMPLEMENTED)) { + return null; + } + COMUtils.checkRC(hr); + return new DebugProcessId(pulId.getValue().intValue()); + } + + @Override + public int getNumberProcesses() { + ULONGByReference pulNumber = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetNumberProcesses(pulNumber); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + return -1; + } + COMUtils.checkRC(hr); + return pulNumber.getValue().intValue(); + } + + @Override + public List getProcesses(int start, int count) { + if (count == 0) { + return Collections.emptyList(); + } + // TODO: Does dbgeng do the bounds checking? + ULONG ulStart = new ULONG(start); + ULONG ulCount = new ULONG(count); + ULONG[] aulIds = new ULONG[count]; + COMUtils.checkRC(jnaSysobj.GetProcessIdsByIndex(ulStart, ulCount, aulIds, null)); + List result = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + result.add(new DebugProcessId(aulIds[i].intValue())); + } + return result; + } + + @Override + public int getCurrentThreadSystemId() { + ULONGByReference pulSysId = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetCurrentThreadSystemId(pulSysId); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED) || hr.equals(COMUtilsExtra.E_NOTIMPLEMENTED)) { + return -1; + } + COMUtils.checkRC(hr); + return pulSysId.getValue().intValue(); + } + + @Override + public int getCurrentProcessSystemId() { + ULONGByReference pulSysId = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetCurrentProcessSystemId(pulSysId); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED) || hr.equals(COMUtilsExtra.E_NOTIMPLEMENTED)) { + return -1; + } + COMUtils.checkRC(hr); + return pulSysId.getValue().intValue(); + } + + @Override + public DebugSessionId getEventSystem() { + throw new UnsupportedOperationException("Not supported by this interface"); + } + + @Override + public DebugSessionId getCurrentSystemId() { + throw new UnsupportedOperationException("Not supported by this interface"); + } + + @Override + public void setCurrentSystemId(DebugSessionId id) { + throw new UnsupportedOperationException("Not supported by this interface"); + } + + @Override + public int getNumberSystems() { + throw new UnsupportedOperationException("Not supported by this interface"); + } + + @Override + public List getSystems(int start, int count) { + throw new UnsupportedOperationException("Not supported by this interface"); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl2.java new file mode 100644 index 0000000000..f4bf16593d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl2.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.sysobj; + +import agent.dbgeng.jna.dbgeng.sysobj.IDebugSystemObjects2; + +public class DebugSystemObjectsImpl2 extends DebugSystemObjectsImpl1 { + @SuppressWarnings("unused") + private final IDebugSystemObjects2 jnaSysobj; + + public DebugSystemObjectsImpl2(IDebugSystemObjects2 jnaSysobj) { + super(jnaSysobj); + this.jnaSysobj = jnaSysobj; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl3.java new file mode 100644 index 0000000000..f3d252e090 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl3.java @@ -0,0 +1,96 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.sysobj; + +import java.util.*; + +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgeng.dbgeng.COMUtilsExtra; +import agent.dbgeng.dbgeng.DebugSessionId; +import agent.dbgeng.jna.dbgeng.sysobj.IDebugSystemObjects3; + +public class DebugSystemObjectsImpl3 extends DebugSystemObjectsImpl2 { + @SuppressWarnings("unused") + private final IDebugSystemObjects3 jnaSysobj; + + public DebugSystemObjectsImpl3(IDebugSystemObjects3 jnaSysobj) { + super(jnaSysobj); + this.jnaSysobj = jnaSysobj; + } + + @Override + public DebugSessionId getEventSystem() { + ULONGByReference pulId = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetEventSystem(pulId); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + return new DebugSessionId(-1); + } + COMUtils.checkRC(hr); + return new DebugSessionId(pulId.getValue().intValue()); + } + + @Override + public DebugSessionId getCurrentSystemId() { + ULONGByReference pulId = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetCurrentSystemId(pulId); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + return new DebugSessionId(-1); + } + COMUtils.checkRC(hr); + return new DebugSessionId(pulId.getValue().intValue()); + } + + @Override + public void setCurrentSystemId(DebugSessionId id) { + HRESULT hr = jnaSysobj.SetCurrentSystemId(new ULONG(id.id)); + if (!hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + COMUtils.checkRC(hr); + } + } + + @Override + public int getNumberSystems() { + ULONGByReference pulNumber = new ULONGByReference(); + HRESULT hr = jnaSysobj.GetNumberSystems(pulNumber); + if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) { + return -1; + } + COMUtils.checkRC(hr); + return pulNumber.getValue().intValue(); + } + + @Override + public List getSystems(int start, int count) { + if (count == 0) { + return Collections.emptyList(); + } + // TODO: Does dbgeng do the bounds checking? + ULONG ulStart = new ULONG(start); + ULONG ulCount = new ULONG(count); + ULONG[] aulIds = new ULONG[count]; + COMUtils.checkRC(jnaSysobj.GetSystemIdsByIndex(ulStart, ulCount, aulIds, null)); + List result = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + result.add(new DebugSessionId(aulIds[i].intValue())); + } + return result; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl4.java new file mode 100644 index 0000000000..1797a2c9c3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsImpl4.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.sysobj; + +import agent.dbgeng.jna.dbgeng.sysobj.IDebugSystemObjects4; + +public class DebugSystemObjectsImpl4 extends DebugSystemObjectsImpl3 { + @SuppressWarnings("unused") + private final IDebugSystemObjects4 jnaSysobj; + + public DebugSystemObjectsImpl4(IDebugSystemObjects4 jnaSysobj) { + super(jnaSysobj); + this.jnaSysobj = jnaSysobj; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsInternal.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsInternal.java new file mode 100644 index 0000000000..f8b033dbb0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/sysobj/DebugSystemObjectsInternal.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.impl.dbgeng.sysobj; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgeng.dbgeng.DebugSystemObjects; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier; +import agent.dbgeng.jna.dbgeng.sysobj.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugSystemObjectsInternal extends DebugSystemObjects { + Map CACHE = new WeakValueHashMap<>(); + + static DebugSystemObjectsInternal instanceFor(WrapIDebugSystemObjects sysobj) { + return DbgEngUtil.lazyWeakCache(CACHE, sysobj, DebugSystemObjectsImpl1::new); + } + + static DebugSystemObjectsInternal instanceFor(WrapIDebugSystemObjects2 sysobj) { + return DbgEngUtil.lazyWeakCache(CACHE, sysobj, DebugSystemObjectsImpl2::new); + } + + static DebugSystemObjectsInternal instanceFor(WrapIDebugSystemObjects3 sysobj) { + return DbgEngUtil.lazyWeakCache(CACHE, sysobj, DebugSystemObjectsImpl3::new); + } + + static DebugSystemObjectsInternal instanceFor(WrapIDebugSystemObjects4 sysobj) { + return DbgEngUtil.lazyWeakCache(CACHE, sysobj, DebugSystemObjectsImpl4::new); + } + + ImmutableMap.Builder> PREFERRED_SYSTEM_OBJECTS_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_SYSTEM_OBJECTS_IIDS = + PREFERRED_SYSTEM_OBJECTS_IIDS_BUILDER // + .put(new REFIID(IDebugSystemObjects4.IID_IDEBUG_SYSTEM_OBJECTS4), + WrapIDebugSystemObjects4.class) // + .put(new REFIID(IDebugSystemObjects3.IID_IDEBUG_SYSTEM_OBJECTS3), + WrapIDebugSystemObjects3.class) // + .put(new REFIID(IDebugSystemObjects2.IID_IDEBUG_SYSTEM_OBJECTS2), + WrapIDebugSystemObjects2.class) // + .put(new REFIID(IDebugSystemObjects.IID_IDEBUG_SYSTEM_OBJECTS), + WrapIDebugSystemObjects.class) // + .build(); + + static DebugSystemObjectsInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgEngUtil.tryPreferredInterfaces(DebugSystemObjectsInternal.class, + PREFERRED_SYSTEM_OBJECTS_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/DbgEngNative.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/DbgEngNative.java new file mode 100644 index 0000000000..04caf3f3ed --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/DbgEngNative.java @@ -0,0 +1,432 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng; + +import java.util.List; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.win32.StdCallLibrary; + +import agent.dbgeng.dbgeng.DebugValue; +import agent.dbgeng.dbgeng.DebugValue.*; + +public interface DbgEngNative extends StdCallLibrary { + //DbgEngNative INSTANCE = Native.loadLibrary("dbgeng", DbgEngNative.class); + DbgEngNative INSTANCE = Native.load("dbgeng.dll", DbgEngNative.class); + + HRESULT DebugConnect(String RemoteOptions, REFIID InterfaceId, PointerByReference Interface); + + HRESULT DebugConnectWide(WString RemoteOptions, REFIID InterfaceId, + PointerByReference Interface); + + HRESULT DebugCreate(REFIID InterfaceId, PointerByReference Interface); + + HRESULT DebugCreateEx(REFIID InterfaceId, DWORD DbgEngOptions, PointerByReference Interface); + + public static class DEBUG_BREAKPOINT_PARAMETERS extends Structure { + public static class ByReference extends DEBUG_BREAKPOINT_PARAMETERS + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("Offset", "Id", "BreakType", + "ProcType", "Flags", "DataSize", "DataAccessType", "PassCount", "CurrentPassCount", + "MatchThread", "CommandSize", "OffsetExpressionSize"); + + public ULONGLONG Offset; + public ULONG Id; + public ULONG BreakType; + public ULONG ProcType; + public ULONG Flags; + public ULONG DataSize; + public ULONG DataAccessType; + public ULONG PassCount; + public ULONG CurrentPassCount; + public ULONG MatchThread; + public ULONG CommandSize; + public ULONG OffsetExpressionSize; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class DEBUG_REGISTER_DESCRIPTION extends Structure { + public static class ByReference extends DEBUG_REGISTER_DESCRIPTION + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("Type", "Flags", "SubregMaster", + "SubregLength", "SubregMask", "SubregShift", "Reserved0"); + + public ULONG Type; + public ULONG Flags; + + public ULONG SubregMaster; + public ULONG SubregLength; + public ULONGLONG SubregMask; + public ULONG SubregShift; + + public ULONG Reserved0; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class DEBUG_VALUE extends Structure { + public static class ByReference extends DEBUG_VALUE implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("u", "TailOfRawBytes", "Type"); + + public static class UNION extends Union { + public static class ByReference extends UNION implements Structure.ByReference { + } + + public static class INTEGER64 extends Structure { + public static class ByReference extends UNION implements Structure.ByReference { + } + + @SuppressWarnings("hiding") + public static final List FIELDS = createFieldsOrder("I64", "Nat"); + + public ULONGLONG I64; + public BOOL Nat; // Always false for non-Itanium + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class I64Parts32 extends Structure { + public static class ByReference extends UNION implements Structure.ByReference { + } + + @SuppressWarnings("hiding") + public static final List FIELDS = createFieldsOrder("LowPart", "HighPart"); + + public ULONG LowPart; + public ULONG HighPart; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class F128Parts64 extends Structure { + public static class ByReference extends UNION implements Structure.ByReference { + } + + @SuppressWarnings("hiding") + public static final List FIELDS = createFieldsOrder("LowPart", "HighPart"); + + public ULONGLONG LowPart; + public LONGLONG HighPart; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public UCHAR I8; + public USHORT I16; + public ULONG I32; + public INTEGER64 I64; + public float F32; + public double F64; + public byte[] F80Bytes = new byte[10]; + public byte[] F82Bytes = new byte[11]; + public byte[] F128Bytes = new byte[16]; + + public byte[] VI8 = new byte[16]; + public short[] VI16 = new short[8]; + public int[] VI32 = new int[4]; + public long[] VI64 = new long[2]; + + public float[] VF32 = new float[4]; + public double[] VF64 = new double[2]; + + public I64Parts32 I64Parts32; + public F128Parts64 F128Parts64; + + public byte[] RawBytes = new byte[24]; + } + + public UNION u; + public ULONG TailOfRawBytes; + public ULONG Type; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + protected void doUSetType() { + switch (DebugValueType.values()[Type.intValue()]) { + case INVALID: + u.setType("RawBytes"); + break; + case INT8: + u.setType("I8"); + break; + case INT16: + u.setType("I16"); + break; + case INT32: + u.setType("I32"); + break; + case INT64: + u.setType("I64"); + break; + case FLOAT32: + u.setType("F32"); + break; + case FLOAT64: + u.setType("F64"); + break; + case FLOAT80: + u.setType("F80Bytes"); + break; + case FLOAT82: + u.setType("F82Bytes"); + break; + case FLOAT128: + u.setType("F128Bytes"); + break; + case VECTOR64: + u.setType("VI8"); + break; + case VECTOR128: + u.setType("VI8"); + break; // TODO: Can I activate multiple types? + } + } + + @Override + public void read() { + super.read(); + doUSetType(); + u.read(); + } + + @Override + public void write() { + super.write(); + doUSetType(); + u.write(); + } + + @SuppressWarnings("unchecked") + public T convertTo(Class desiredType) { + if (desiredType != null && desiredType != DebugValue.class && + DebugValueType.getDebugValueTypeForClass( + desiredType).ordinal() != Type.intValue()) { + // TODO: Display value in exception + throw new ClassCastException("debug value is not of the desired type"); + } + switch (DebugValueType.values()[Type.intValue()]) { + case INVALID: + return null; // TODO: Some DebugInvalidWrapper class? + case INT8: + return (T) new DebugInt8Value(u.I8.byteValue()); + case INT16: + return (T) new DebugInt16Value(u.I16.shortValue()); + case INT32: + return (T) new DebugInt32Value(u.I32.intValue()); + case INT64: + return (T) new DebugInt64Value(u.I64.I64.longValue()); + case FLOAT32: + return (T) new DebugFloat32Value(u.F32); + case FLOAT64: + return (T) new DebugFloat64Value(u.F64); + case FLOAT80: + return (T) new DebugFloat80Value(u.F80Bytes); + case FLOAT82: + return (T) new DebugFloat82Value(u.F82Bytes); + case FLOAT128: + return (T) new DebugFloat128Value(u.F128Bytes); + case VECTOR64: + return (T) new DebugVector128Value(u.VI8); + case VECTOR128: + return (T) new DebugVector128Value(u.VI8); + } + throw new AssertionError("INTERNAL: Shouldn't be here"); + } + + public static DEBUG_VALUE fromDebugValue(DebugValue value) { + DEBUG_VALUE result = new DEBUG_VALUE(); + fromDebugValue(result, value); + return result; + } + + public static void fromDebugValue(DEBUG_VALUE into, DebugValue value) { + DebugValueType type = DebugValueType.getDebugValueTypeForClass(value.getClass()); + into.Type = new ULONG(type.ordinal()); + switch (type) { + case INVALID: + break; + case INT8: + DebugInt8Value int8 = (DebugInt8Value) value; + into.u.I8 = new UCHAR(int8.byteValue()); + break; + case INT16: + DebugInt16Value int16 = (DebugInt16Value) value; + into.u.I16 = new USHORT(int16.shortValue()); + break; + case INT32: + DebugInt32Value int32 = (DebugInt32Value) value; + into.u.I32 = new ULONG(int32.intValue()); + break; + case INT64: + DebugInt64Value int64 = (DebugInt64Value) value; + into.u.I64.I64 = new ULONGLONG(int64.longValue()); + break; + case FLOAT32: + DebugFloat32Value float32 = (DebugFloat32Value) value; + into.u.F32 = float32.floatValue(); + break; + case FLOAT64: + DebugFloat64Value float64 = (DebugFloat64Value) value; + into.u.F64 = float64.doubleValue(); + break; + case FLOAT80: + DebugFloat80Value float80 = (DebugFloat80Value) value; + into.u.F80Bytes = float80.bytes(); + break; + case FLOAT82: + DebugFloat82Value float82 = (DebugFloat82Value) value; + into.u.F82Bytes = float82.bytes(); + break; + case FLOAT128: + DebugFloat128Value float128 = (DebugFloat128Value) value; + into.u.F128Bytes = float128.bytes(); + break; + case VECTOR64: + DebugVector64Value vector64 = (DebugVector64Value) value; + into.u.VI8 = vector64.vi4(); // TODO: Copy Into? + break; + case VECTOR128: + DebugVector128Value vector128 = (DebugVector128Value) value; + into.u.VI8 = vector128.vi8(); // TODO: Copy Into? + break; + default: + throw new AssertionError("INTERNAL: Shouldn't be here"); + } + } + } + + public class DEBUG_MODULE_AND_ID extends Structure { + public static class ByReference extends DEBUG_MODULE_AND_ID + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("ModuleBase", "Id"); + + public ULONGLONG ModuleBase; + public ULONGLONG Id; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public class DEBUG_SYMBOL_ENTRY extends Structure { + public static class ByReference extends DEBUG_SYMBOL_ENTRY + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("ModuleBase", "Offset", "Id", + "Arg64", "Size", "Flags", "TypeId", "NameSize", "Token", "Tag", "Arg32", "Reserved"); + + public ULONGLONG ModuleBase; + public ULONGLONG Offset; + public ULONGLONG Id; + public ULONGLONG Arg64; + public ULONG Size; + public ULONG Flags; + public ULONG TypeId; + public ULONG NameSize; + public ULONG Token; + public ULONG Tag; + public ULONG Arg32; + public ULONG Reserved; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public class DEBUG_THREAD_BASIC_INFORMATION extends Structure { + public static class ByReference extends DEBUG_THREAD_BASIC_INFORMATION + implements Structure.ByReference { + } + + public static final List FIELDS = + createFieldsOrder("Valid", "ExitStatus", "PriorityClass", "Priority", "CreateTime", + "ExitTime", "KernelTime", "UserTime", "StartOffset", "Affinity"); + + public ULONG Valid; + public ULONG ExitStatus; + public ULONG PriorityClass; + public ULONG Priority; + public ULONGLONG CreateTime; + public ULONGLONG ExitTime; + public ULONGLONG KernelTime; + public ULONGLONG UserTime; + public ULONGLONG StartOffset; + public ULONGLONG Affinity; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public class DEBUG_STACK_FRAME extends Structure { + public static class ByReference extends DEBUG_STACK_FRAME implements Structure.ByReference { + } + + public static final List FIELDS = + createFieldsOrder("InstructionOffset", "ReturnOffset", "FrameOffset", "StackOffset", + "FuncTableEntry", "Params", "Reserved", "Virtual", "FrameNumber"); + + public ULONGLONG InstructionOffset; + public ULONGLONG ReturnOffset; + public ULONGLONG FrameOffset; + public ULONGLONG StackOffset; + public ULONGLONG FuncTableEntry; + public ULONGLONG[] Params = new ULONGLONG[4]; + public ULONGLONG[] Reserved = new ULONGLONG[6]; + public BOOL Virtual; + public ULONG FrameNumber; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/Kernel32Extra.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/Kernel32Extra.java new file mode 100644 index 0000000000..60f57a0869 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/Kernel32Extra.java @@ -0,0 +1,126 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng; + +import java.util.List; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR; +import com.sun.jna.platform.win32.WinDef; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HANDLE; +import com.sun.jna.win32.StdCallLibrary; +import com.sun.jna.win32.W32APIOptions; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_POINTERS; + +public interface Kernel32Extra extends StdCallLibrary { + Kernel32Extra INSTANCE = + Native.loadLibrary("kernel32", Kernel32Extra.class, W32APIOptions.DEFAULT_OPTIONS); + + interface VectoredHandlerCallback extends StdCallCallback { + LONG EXCEPTION_CONTINUE_EXECUTION = new LONG(0xffffffff); + LONG EXCEPTION_CONTINUE_SEARCH = new LONG(0x0); + + LONG invoke(EXCEPTION_POINTERS.ByReference ExceptionInfo); + } + + interface HandlerRoutineCallback extends StdCallCallback { + int CTRL_C_EVENT = 0; + int CTRL_CLOSE_EVENT = 2; + int CTRL_LOGOFF_EVENT = 5; + int CTRL_SHUTDOWN_EVENT = 6; + + boolean invoke(DWORD dwCtrlType); + } + + public static class PROCESSENTRY32W extends Structure { + public static class ByReference extends PROCESSENTRY32W implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("dwSize", "cntUsage", + "th32ProcessID", "th32DefaultHeapID", "th32ModuleID", "cntThreads", + "th32ParentProcessID", "pcPriClassBase", "dwFlags", "szExeFile"); + + public DWORD dwSize; + public DWORD cntUsage; + public DWORD th32ProcessID; + public ULONG_PTR th32DefaultHeapID; + public DWORD th32ModuleID; + public DWORD cntThreads; + public DWORD th32ParentProcessID; + public DWORD pcPriClassBase; + public DWORD dwFlags; + public char[] szExeFile = new char[WinDef.MAX_PATH]; + + public PROCESSENTRY32W() { + dwSize = new DWORD(size()); + } + + public PROCESSENTRY32W(Pointer p) { + super(p); + read(); + } + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class THREADENTRY32 extends Structure { + public static class ByReference extends THREADENTRY32 implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("dwSize", "cntUsage", + "th32ThreadID", "th32OwnerProcessID", "tpBasePri", "tpDeltaPri", "dwFlags"); + + public DWORD dwSize; + public DWORD cntUsage; + public DWORD th32ThreadID; + public DWORD th32OwnerProcessID; + public LONG tpBasePri; + public LONG tpDeltaPri; + public DWORD dwFlags; + + public THREADENTRY32() { + dwSize = new DWORD(size()); + } + + public THREADENTRY32(Pointer p) { + super(p); + read(); + } + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + Pointer AddVectoredExceptionHandler(ULONG FirstHandler, + Kernel32Extra.VectoredHandlerCallback VectoredHandler); + + boolean SetConsoleCtrlHandler(HandlerRoutineCallback HandlerRoutine, boolean Add); + + boolean Process32FirstW(HANDLE hSnapshot, PROCESSENTRY32W lppe); + + boolean Process32NextW(HANDLE hSnapshot, PROCESSENTRY32W lppe); + + boolean Thread32First(HANDLE hSnapshot, THREADENTRY32 lpte); + + boolean Thread32Next(HANDLE hSnapshot, THREADENTRY32 lpte); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/ToolhelpUtil.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/ToolhelpUtil.java new file mode 100644 index 0000000000..be625d1fbe --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/ToolhelpUtil.java @@ -0,0 +1,107 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Supplier; + +import com.sun.jna.platform.win32.*; +import com.sun.jna.platform.win32.WinDef.DWORD; +import com.sun.jna.platform.win32.WinNT.HANDLE; + +import agent.dbgeng.dbgeng.DbgEng; +import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable; +import agent.dbgeng.jna.dbgeng.Kernel32Extra.PROCESSENTRY32W; +import agent.dbgeng.jna.dbgeng.Kernel32Extra.THREADENTRY32; +import ghidra.comm.util.BitmaskSet; +import ghidra.comm.util.BitmaskUniverse; + +public class ToolhelpUtil { + public static class Snapshot { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final HANDLE handle; + + private Snapshot(HANDLE handle) { + this.cleanable = DbgEng.releaseWhenPhantom(this, handle); + this.handle = handle; + } + + protected List getItems(Supplier newStruct, BiFunction first, + BiFunction next) { + List items = new ArrayList<>(); + + T entry = newStruct.get(); + for (boolean has = first.apply(handle, entry); has; has = next.apply(handle, entry)) { + items.add(entry); + entry = newStruct.get(); + } + + int lastError = Kernel32.INSTANCE.GetLastError(); + if (lastError != W32Errors.ERROR_SUCCESS && + lastError != W32Errors.ERROR_NO_MORE_FILES) { + throw new Win32Exception(lastError); + } + + return items; + } + + public List getProcesses() { + return getItems(PROCESSENTRY32W::new, Kernel32Extra.INSTANCE::Process32FirstW, + Kernel32Extra.INSTANCE::Process32NextW); + } + + public List getThreads() { + return getItems(THREADENTRY32::new, Kernel32Extra.INSTANCE::Thread32First, + Kernel32Extra.INSTANCE::Thread32Next); + } + } + + public static enum SnapshotFlags implements BitmaskUniverse { + HEAPLIST(Tlhelp32.TH32CS_SNAPHEAPLIST), // + PROCESS(Tlhelp32.TH32CS_SNAPPROCESS), // + THREAD(Tlhelp32.TH32CS_SNAPTHREAD), // + MODULE(Tlhelp32.TH32CS_SNAPMODULE), // + MODULE32(Tlhelp32.TH32CS_SNAPMODULE32), // + ALL(Tlhelp32.TH32CS_SNAPALL), // + INHERIT(Tlhelp32.TH32CS_INHERIT), // + ; + + SnapshotFlags(DWORD mask) { + this.mask = mask.intValue(); + } + + int mask; + + @Override + public long getMask() { + return mask; + } + } + + public static Snapshot createSnapshot(BitmaskSet flags, int processId) { + DWORD dwFlags = new DWORD(flags.getBitmask()); + DWORD dwPID = new DWORD(processId); + HANDLE hSnap = Kernel32.INSTANCE.CreateToolhelp32Snapshot(dwFlags, dwPID); + if (hSnap == null) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + return new Snapshot(hSnap); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/UnknownWithUtils.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/UnknownWithUtils.java new file mode 100644 index 0000000000..2a30bcf9ec --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/UnknownWithUtils.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.Unknown; + +public class UnknownWithUtils extends Unknown { + public static interface VTableIndex { + int getIndex(); + + public static & VTableIndex> int follow(Class prev) { + I[] all = prev.getEnumConstants(); + int start = all[0].getIndex() - all[0].ordinal(); + return all.length + start; + } + } + + public UnknownWithUtils() { + } + + public UnknownWithUtils(Pointer pvInstance) { + super(pvInstance); + } + + protected HRESULT _invokeHR(VTableIndex idx, Object... args) { + return (HRESULT) this._invokeNativeObject(idx.getIndex(), args, HRESULT.class); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/WinNTExtra.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/WinNTExtra.java new file mode 100644 index 0000000000..2f47cbbdfd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/WinNTExtra.java @@ -0,0 +1,281 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng; + +import java.util.List; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR; +import com.sun.jna.platform.win32.WinDef.*; + +public class WinNTExtra { + public static enum Machine { + IMAGE_FILE_MACHINE_UNKNOWN(0, "Unknown"), // + IMAGE_FILE_MACHINE_I386(0x014c, "x86"), // + IMAGE_FILE_MACHINE_ARM(0x01c0, "ARM"), // + IMAGE_FILE_MACHINE_IA64(0x0200, "Itanium"), // + IMAGE_FILE_MACHINE_AMD64(0x8664, "AMD64 (K8)"), // + IMAGE_FILE_MACHINE_EBC(0x0EBC, "EFI"), // + ; + + public static Machine getByNumber(int val) { + for (Machine m : Machine.values()) { + if (m.val == val) { + return m; + } + } + return null; + } + + Machine(int val, String description) { + this.val = val; + this.description = description; + } + + public final int val; + public final String description; + } + + public static class M128A extends Structure { + public static class ByReference extends M128A implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("Low", "High"); + + public ULONGLONG Low; + public LONGLONG High; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class CONTEXT_AMD64 extends Structure { + public static class ByReference extends CONTEXT_AMD64 implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder( + + "P1Home", "P2Home", "P3Home", "P4Home", "P5Home", "P6Home", + + "ContextFlags", "MxCsr", + + "SegCs", "SegDs", "SegEs", "SegFs", "SegGs", "SegSs", "EFlags", + + "Dr0", "Dr1", "Dr2", "Dr3", "Dr6", "Dr7", + + "Rax", "Rcx", "Rdx", "Rbx", "Rsp", "Rbp", "Rsi", "Rdi", "R8", "R9", "R10", "R11", "R12", + "R13", "R14", "R15", + + "Rip", + + "Header", "Legacy", "Xmm0", "Xmm1", "Xmm2", "Xmm3", "Xmm4", "Xmm5", "Xmm6", "Xmm7", + "Xmm8", "Xmm9", "Xmm10", "Xmm11", "Xmm12", "Xmm13", "Xmm14", "Xmm15", + + "VectorRegister", "VectorControl", + + "DebugControl", "LastBranchToRip", "LastBranchFromRip", "LastExceptionToRip", + "LastExceptionFromRip"); + + public DWORDLONG P1Home; + public DWORDLONG P2Home; + public DWORDLONG P3Home; + public DWORDLONG P4Home; + public DWORDLONG P5Home; + public DWORDLONG P6Home; + + public DWORD ContextFlags; + public DWORD MxCsr; + + public WORD SegCs; + public WORD SegDs; + public WORD SegEs; + public WORD SegFs; + public WORD SegGs; + public WORD SegSs; + public DWORD EFlags; + + public DWORDLONG Dr0; + public DWORDLONG Dr1; + public DWORDLONG Dr2; + public DWORDLONG Dr3; + public DWORDLONG Dr6; + public DWORDLONG Dr7; + + public DWORDLONG Rax; + public DWORDLONG Rcx; + public DWORDLONG Rdx; + public DWORDLONG Rbx; + public DWORDLONG Rsp; + public DWORDLONG Rbp; + public DWORDLONG Rsi; + public DWORDLONG Rdi; + public DWORDLONG R8; + public DWORDLONG R9; + public DWORDLONG R10; + public DWORDLONG R11; + public DWORDLONG R12; + public DWORDLONG R13; + public DWORDLONG R14; + public DWORDLONG R15; + + public DWORDLONG Rip; + + public M128A[] Header = new M128A[2]; + public M128A[] Legacy = new M128A[8]; + public M128A Xmm0; + public M128A Xmm1; + public M128A Xmm2; + public M128A Xmm3; + public M128A Xmm4; + public M128A Xmm5; + public M128A Xmm6; + public M128A Xmm7; + public M128A Xmm8; + public M128A Xmm9; + public M128A Xmm10; + public M128A Xmm11; + public M128A Xmm12; + public M128A Xmm13; + public M128A Xmm14; + public M128A Xmm15; + + public M128A[] VectorRegister = new M128A[26]; + public DWORDLONG VectorControl; + + public DWORDLONG DebugControl; + public DWORDLONG LastBranchToRip; + public DWORDLONG LastBranchFromRip; + public DWORDLONG LastExceptionToRip; + public DWORDLONG LastExceptionFromRip; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class EXCEPTION_RECORD extends Structure { + public static class ByReference extends EXCEPTION_RECORD implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("ExceptionCode", + "ExceptionFlags", "ExceptionRecord", "ExceptionAddress", "NumberParameters", + "__unusedAlignment", "ExceptionInformation"); + + public DWORD ExceptionCode; + public DWORD ExceptionFlags; + public EXCEPTION_RECORD.ByReference ExceptionRecord; + public Pointer ExceptionAddress; + public DWORD NumberParameters; + public ULONG_PTR ExceptionInformation[] = new ULONG_PTR[15]; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class EXCEPTION_RECORD32 extends Structure { + public static class ByReference extends EXCEPTION_RECORD64 + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("ExceptionCode", + "ExceptionFlags", "ExceptionRecord", "ExceptionAddress", "NumberParameters", + "__unusedAlignment", "ExceptionInformation"); + + public DWORD ExceptionCode; + public DWORD ExceptionFlags; + public DWORD ExceptionRecord; + public DWORD ExceptionAddress; + public DWORD NumberParameters; + public DWORD __unusedAlignment; + public DWORD ExceptionInformation[] = new DWORD[15]; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class EXCEPTION_RECORD64 extends Structure { + public static class ByReference extends EXCEPTION_RECORD64 + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("ExceptionCode", + "ExceptionFlags", "ExceptionRecord", "ExceptionAddress", "NumberParameters", + "__unusedAlignment", "ExceptionInformation"); + + public DWORD ExceptionCode; + public DWORD ExceptionFlags; + public DWORDLONG ExceptionRecord; + public DWORDLONG ExceptionAddress; + public DWORD NumberParameters; + public DWORD __unusedAlignment; + public DWORDLONG ExceptionInformation[] = new DWORDLONG[15]; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class EXCEPTION_POINTERS extends Structure { + public static class ByReference extends EXCEPTION_POINTERS + implements Structure.ByReference { + } + + public static final List FIELDS = + createFieldsOrder("ExceptionRecord", "ContextRecord"); + + public EXCEPTION_RECORD.ByReference ExceptionRecord; + public CONTEXT_AMD64.ByReference ContextRecord; // TODO: This should be fine for now + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class MEMORY_BASIC_INFORMATION64 extends Structure { + public static class ByReference extends MEMORY_BASIC_INFORMATION64 + implements Structure.ByReference { + } + + public static final List FIELDS = + createFieldsOrder("BaseAddress", "AllocationBase", "AllocationProtect", "__alignment1", + "RegionSize", "State", "Protect", "Type", "__alignment2"); + + public ULONGLONG BaseAddress; + public ULONGLONG AllocationBase; + public DWORD AllocationProtect; + public DWORD __alignment1; + public ULONGLONG RegionSize; + public DWORD State; + public DWORD Protect; + public DWORD Type; + public DWORD __alignment2; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced.java new file mode 100644 index 0000000000..666b10eb20 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.advanced; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +import com.sun.jna.platform.win32.COM.IUnknown; + +public interface IDebugAdvanced extends IUnknown { + final IID IID_IDEBUG_ADVANCED = new IID("f2df5f53-071f-47bd-9de6-5734c3fed689"); + + enum VTIndices implements VTableIndex { + GET_THREAD_CONTEXT, // + SET_THREAD_CONTEXT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetThreadContext(Pointer Context, ULONG ContextSize); + + HRESULT SetThreadContext(Pointer Context, ULONG ContextSize); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced2.java new file mode 100644 index 0000000000..900fb5c531 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced2.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.advanced; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugAdvanced2 extends IDebugAdvanced { + final IID IID_IDEBUG_ADVANCED2 = new IID("716d14c9-119b-4ba5-af1f-0890e672416a"); + + enum VTIndices2 implements VTableIndex { + REQUEST, // + GET_SOURCE_FILE_INFORMATION, // + FIND_SOURCE_FILE_AND_TOKEN, // + GET_SYMBOL_INFORMATION, // + GET_SYSTEM_OBJECT_INFORMATION, // + ; + + static int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT Request(ULONG Request, Pointer InBuffer, ULONG InBuffserSize, Pointer OutBuffer, + ULONG OutBufferSize, ULONGByReference OutSize); + + HRESULT GetSourceFileInformation(ULONG Which, String SourceFile, ULONGLONG Arg64, ULONG Arg32, + Pointer Buffer, ULONG BufferSize, ULONGByReference InfoSize); + + HRESULT FindSourceFileAndToken(ULONG StartElement, ULONGLONG ModAddr, String File, ULONG Flags, + Pointer FileToken, ULONG FileTokenSize, ULONGByReference FoundElement, byte[] Buffer, + ULONG BufferSize, ULONGByReference FoundSize); + + HRESULT GetSymbolInformation(ULONG Which, ULONGLONG Arg64, ULONG Arg32, Pointer Buffer, + ULONG BufferSize, ULONGByReference InfoSize, byte[] StringBuffer, + ULONG StringBufferSize, ULONGByReference StringSize); + + HRESULT GetSystemObjectInformation(ULONG Which, ULONGLONG Arg64, ULONG Arg32, Pointer Buffer, + ULONG BufferSize, ULONGByReference InfoSize); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced3.java new file mode 100644 index 0000000000..3a5a3ac18a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/IDebugAdvanced3.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.advanced; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugAdvanced3 extends IDebugAdvanced2 { + final IID IID_IDEBUG_ADVANCED3 = new IID("cba4abb4-84c4-444d-87ca-a04e13286739"); + + enum VTIndices3 implements VTableIndex { + GET_SOURCE_FILE_INFORMATION_WIDE, // + FIND_SOURCE_FILE_AND_TOKEN_WIDE, // + GET_SYMBOL_INFORMATION_WIDE, // + ; + + static int start = VTableIndex.follow(VTIndices2.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetSourceFileInformationWide(ULONG Which, WString SourceFile, ULONGLONG Arg64, + ULONG Arg32, Pointer Buffer, ULONG BufferSize, ULONGByReference InfoSize); + + HRESULT FindSourceFileAndTokenWide(ULONG StartElement, ULONGLONG ModAddr, String File, + ULONG Flags, Pointer FileToken, ULONG FileTokenSize, ULONGByReference FoundElement, + char[] Buffer, ULONG BufferSize, ULONGByReference FoundSize); + + HRESULT GetSymbolInformationWide(ULONG Which, ULONGLONG Arg64, ULONG Arg32, Pointer Buffer, + ULONG BufferSize, ULONGByReference InfoSize, char[] StringBuffer, + ULONG StringBufferSize, ULONGByReference StringSize); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced.java new file mode 100644 index 0000000000..e693f964c6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.advanced; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils; + +public class WrapIDebugAdvanced extends UnknownWithUtils implements IDebugAdvanced { + public static class ByReference extends WrapIDebugAdvanced implements Structure.ByReference { + } + + public WrapIDebugAdvanced() { + } + + public WrapIDebugAdvanced(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetThreadContext(Pointer Context, ULONG ContextSize) { + return _invokeHR(VTIndices.GET_THREAD_CONTEXT, getPointer(), Context, ContextSize); + } + + @Override + public HRESULT SetThreadContext(Pointer Context, ULONG ContextSize) { + return _invokeHR(VTIndices.SET_THREAD_CONTEXT, getPointer(), Context, ContextSize); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced2.java new file mode 100644 index 0000000000..4c70d828a9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced2.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.advanced; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class WrapIDebugAdvanced2 extends WrapIDebugAdvanced implements IDebugAdvanced2 { + public static class ByReference extends WrapIDebugAdvanced2 implements Structure.ByReference { + } + + public WrapIDebugAdvanced2() { + } + + public WrapIDebugAdvanced2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT Request(ULONG Request, Pointer InBuffer, ULONG InBuffserSize, Pointer OutBuffer, + ULONG OutBufferSize, ULONGByReference OutSize) { + return _invokeHR(VTIndices2.REQUEST, getPointer(), Request, InBuffer, InBuffserSize, + OutBuffer, OutBufferSize, OutSize); + } + + @Override + public HRESULT GetSourceFileInformation(ULONG Which, String SourceFile, ULONGLONG Arg64, + ULONG Arg32, Pointer Buffer, ULONG BufferSize, ULONGByReference InfoSize) { + return _invokeHR(VTIndices2.GET_SOURCE_FILE_INFORMATION, getPointer(), Which, SourceFile, + Arg64, Arg32, Buffer, BufferSize, InfoSize); + } + + @Override + public HRESULT FindSourceFileAndToken(ULONG StartElement, ULONGLONG ModAddr, String File, + ULONG Flags, Pointer FileToken, ULONG FileTokenSize, ULONGByReference FoundElement, + byte[] Buffer, ULONG BufferSize, ULONGByReference FoundSize) { + return _invokeHR(VTIndices2.FIND_SOURCE_FILE_AND_TOKEN, getPointer(), StartElement, ModAddr, + File, Flags, FileToken, FileTokenSize, FoundElement, Buffer, BufferSize, FoundSize); + } + + @Override + public HRESULT GetSymbolInformation(ULONG Which, ULONGLONG Arg64, ULONG Arg32, Pointer Buffer, + ULONG BufferSize, ULONGByReference InfoSize, byte[] StringBuffer, + ULONG StringBufferSize, ULONGByReference StringSize) { + return _invokeHR(VTIndices2.GET_SYMBOL_INFORMATION, getPointer(), Which, Arg64, Arg32, + Buffer, BufferSize, InfoSize, StringBuffer, StringBufferSize, StringSize); + } + + @Override + public HRESULT GetSystemObjectInformation(ULONG Which, ULONGLONG Arg64, ULONG Arg32, + Pointer Buffer, ULONG BufferSize, ULONGByReference InfoSize) { + return _invokeHR(VTIndices2.GET_SYSTEM_OBJECT_INFORMATION, getPointer(), Which, Arg64, + Arg32, Buffer, BufferSize, InfoSize); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced3.java new file mode 100644 index 0000000000..bf80daa081 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/advanced/WrapIDebugAdvanced3.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.advanced; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class WrapIDebugAdvanced3 extends WrapIDebugAdvanced2 implements IDebugAdvanced3 { + public static class ByReference extends WrapIDebugAdvanced3 implements Structure.ByReference { + } + + public WrapIDebugAdvanced3() { + } + + public WrapIDebugAdvanced3(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetSourceFileInformationWide(ULONG Which, WString SourceFile, ULONGLONG Arg64, + ULONG Arg32, Pointer Buffer, ULONG BufferSize, ULONGByReference InfoSize) { + return _invokeHR(VTIndices3.GET_SOURCE_FILE_INFORMATION_WIDE, getPointer(), Which, + SourceFile, Arg64, Arg32, Buffer, BufferSize, InfoSize); + } + + @Override + public HRESULT FindSourceFileAndTokenWide(ULONG StartElement, ULONGLONG ModAddr, String File, + ULONG Flags, Pointer FileToken, ULONG FileTokenSize, ULONGByReference FoundElement, + char[] Buffer, ULONG BufferSize, ULONGByReference FoundSize) { + return _invokeHR(VTIndices3.FIND_SOURCE_FILE_AND_TOKEN_WIDE, getPointer(), StartElement, + ModAddr, File, Flags, FileToken, FileTokenSize, FoundElement, Buffer, BufferSize, + FoundSize); + } + + @Override + public HRESULT GetSymbolInformationWide(ULONG Which, ULONGLONG Arg64, ULONG Arg32, + Pointer Buffer, ULONG BufferSize, ULONGByReference InfoSize, char[] StringBuffer, + ULONG StringBufferSize, ULONGByReference StringSize) { + return _invokeHR(VTIndices3.GET_SYMBOL_INFORMATION_WIDE, getPointer(), Which, Arg64, Arg32, + Buffer, BufferSize, InfoSize, StringBuffer, StringBufferSize, StringSize); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint.java new file mode 100644 index 0000000000..147f4822cc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint.java @@ -0,0 +1,104 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.breakpoint; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_BREAKPOINT_PARAMETERS; +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +import com.sun.jna.platform.win32.COM.IUnknown; + +public interface IDebugBreakpoint extends IUnknown { + final IID IID_IDEBUG_BREAKPOINT = new IID("5bd9d474-5975-423a-b88b-65a8e7110e65"); + + enum VTIndices implements VTableIndex { + GET_ID, // + GET_TYPE, // + GET_ADDER, // + GET_FLAGS, // + ADD_FLAGS, // + REMOVE_FLAGS, // + SET_FLAGS, // + GET_OFFSET, // + SET_OFFSET, // + GET_DATA_PARAMETERS, // + SET_DATA_PARAMETERS, // + GET_PASS_COUNT, // + SET_PASS_COUNT, // + GET_CURRENT_PASS_COUNT, // + GET_MATCH_THREAD_ID, // + SET_MATCH_THREAD_ID, // + GET_COMMAND, // + SET_COMMAND, // + GET_OFFSET_EXPRESSION, // + SET_OFFSET_EXPRESSION, // + GET_PARAMETERS, // + ; + + public int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetId(ULONGByReference Id); + + HRESULT GetType(ULONGByReference BreakType, ULONGByReference ProcType); + + HRESULT GetAdder(Pointer Adder); + + HRESULT GetFlags(ULONGByReference Flags); + + HRESULT AddFlags(ULONG Flags); + + HRESULT RemoveFlags(ULONG Flags); + + HRESULT SetFlags(ULONG Flags); + + HRESULT GetOffset(ULONGLONGByReference Offset); + + HRESULT SetOffset(ULONGLONG Offset); + + HRESULT GetDataParameters(ULONGByReference Size, ULONGByReference AcessType); + + HRESULT SetDataParameters(ULONG Size, ULONG AccessType); + + HRESULT GetPassCount(ULONGByReference Count); + + HRESULT SetPassCount(ULONG Count); + + HRESULT GetCurrentPassCount(ULONGByReference Count); + + HRESULT GetMatchThreadId(ULONGByReference Id); + + HRESULT SetMatchThreadId(ULONG Thread); + + HRESULT GetCommand(byte[] Buffer, ULONG BufferSize, ULONGByReference CommandSize); + + HRESULT SetCommand(String Command); + + HRESULT GetOffsetExpression(byte[] Buffer, ULONG BufferSize, ULONGByReference ExpressionSize); + + HRESULT SetOffsetExpression(String Expression); + + HRESULT GetParameters(DEBUG_BREAKPOINT_PARAMETERS.ByReference Params); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint2.java new file mode 100644 index 0000000000..3671586ae0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint2.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.breakpoint; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugBreakpoint2 extends IDebugBreakpoint { + final IID IID_IDEBUG_BREAKPOINT2 = new IID("1b278d20-79f2-426e-a3f9-c1ddf375d48e"); + + enum VTIndices2 implements VTableIndex { + GET_COMMAND_WIDE, // + SET_COMMAND_WIDE, // + GET_OFFSET_EXPRESSION_WIDE, // + SET_OFFSET_EXPRESSION_WIDE, // + ; + + public int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetCommandWide(char[] Buffer, ULONG BufferSize, ULONGByReference CommandSize); + + HRESULT SetComamndWide(WString Command); + + HRESULT GetOffsetExpressionWide(char[] Buffer, ULONG BufferSize, + ULONGByReference ExpressionSize); + + HRESULT SetOffsetExpressionWide(WString Expression); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint3.java new file mode 100644 index 0000000000..14d2403813 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/IDebugBreakpoint3.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.breakpoint; + +import com.sun.jna.platform.win32.Guid.GUID; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugBreakpoint3 extends IDebugBreakpoint2 { + final IID IID_IDEBUG_BREAKPOINT3 = new IID("38f5c249-b448-43bb-9835-579d4ec02249"); + + enum VTIndices3 implements VTableIndex { + GET_GUID, // + ; + + static int start = VTableIndex.follow(VTIndices2.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetGuid(GUID.ByReference Guid); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint.java new file mode 100644 index 0000000000..dbfba1d2ce --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint.java @@ -0,0 +1,143 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.breakpoint; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_BREAKPOINT_PARAMETERS; + +public class WrapIDebugBreakpoint extends UnknownWithUtils implements IDebugBreakpoint { + public static class ByReference extends WrapIDebugBreakpoint implements Structure.ByReference { + } + + public WrapIDebugBreakpoint() { + } + + public WrapIDebugBreakpoint(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetId(ULONGByReference Id) { + return _invokeHR(VTIndices.GET_ID, getPointer(), Id); + } + + @Override + public HRESULT GetType(ULONGByReference BreakType, ULONGByReference ProcType) { + return _invokeHR(VTIndices.GET_TYPE, getPointer(), BreakType, ProcType); + } + + @Override + public HRESULT GetAdder(Pointer Adder) { + return _invokeHR(VTIndices.GET_ADDER, getPointer(), Adder); + } + + @Override + public HRESULT GetFlags(ULONGByReference Flags) { + return _invokeHR(VTIndices.GET_FLAGS, getPointer(), Flags); + } + + @Override + public HRESULT AddFlags(ULONG Flags) { + return _invokeHR(VTIndices.ADD_FLAGS, getPointer(), Flags); + } + + @Override + public HRESULT RemoveFlags(ULONG Flags) { + return _invokeHR(VTIndices.REMOVE_FLAGS, getPointer(), Flags); + } + + @Override + public HRESULT SetFlags(ULONG Flags) { + return _invokeHR(VTIndices.SET_FLAGS, getPointer(), Flags); + } + + @Override + public HRESULT GetOffset(ULONGLONGByReference Offset) { + return _invokeHR(VTIndices.GET_OFFSET, getPointer(), Offset); + } + + @Override + public HRESULT SetOffset(ULONGLONG Offset) { + return _invokeHR(VTIndices.SET_OFFSET, getPointer(), Offset); + } + + @Override + public HRESULT GetDataParameters(ULONGByReference Size, ULONGByReference AccessType) { + return _invokeHR(VTIndices.GET_DATA_PARAMETERS, getPointer(), Size, AccessType); + } + + @Override + public HRESULT SetDataParameters(ULONG Size, ULONG AccessType) { + return _invokeHR(VTIndices.SET_DATA_PARAMETERS, getPointer(), Size, AccessType); + } + + @Override + public HRESULT GetPassCount(ULONGByReference Count) { + return _invokeHR(VTIndices.GET_PASS_COUNT, getPointer(), Count); + } + + @Override + public HRESULT SetPassCount(ULONG Count) { + return _invokeHR(VTIndices.SET_PASS_COUNT, getPointer(), Count); + } + + @Override + public HRESULT GetCurrentPassCount(ULONGByReference Count) { + return _invokeHR(VTIndices.GET_CURRENT_PASS_COUNT, getPointer(), Count); + } + + @Override + public HRESULT GetMatchThreadId(ULONGByReference Id) { + return _invokeHR(VTIndices.GET_MATCH_THREAD_ID, getPointer(), Id); + } + + @Override + public HRESULT SetMatchThreadId(ULONG Thread) { + return _invokeHR(VTIndices.SET_MATCH_THREAD_ID, getPointer(), Thread); + } + + @Override + public HRESULT GetCommand(byte[] Buffer, ULONG BufferSize, ULONGByReference CommandSize) { + return _invokeHR(VTIndices.GET_COMMAND, getPointer(), Buffer, BufferSize, CommandSize); + } + + @Override + public HRESULT SetCommand(String Command) { + return _invokeHR(VTIndices.SET_COMMAND, getPointer(), Command); + } + + @Override + public HRESULT GetOffsetExpression(byte[] Buffer, ULONG BufferSize, + ULONGByReference ExpressionSize) { + return _invokeHR(VTIndices.GET_OFFSET_EXPRESSION, getPointer(), Buffer, BufferSize, + ExpressionSize); + } + + @Override + public HRESULT SetOffsetExpression(String Expression) { + return _invokeHR(VTIndices.SET_OFFSET_EXPRESSION, getPointer(), Expression); + } + + @Override + public HRESULT GetParameters(DEBUG_BREAKPOINT_PARAMETERS.ByReference Params) { + return _invokeHR(VTIndices.GET_PARAMETERS, getPointer(), Params); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint2.java new file mode 100644 index 0000000000..a43f456ae9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint2.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.breakpoint; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class WrapIDebugBreakpoint2 extends WrapIDebugBreakpoint implements IDebugBreakpoint2 { + public static class ByReference extends WrapIDebugBreakpoint2 implements Structure.ByReference { + } + + public WrapIDebugBreakpoint2() { + } + + public WrapIDebugBreakpoint2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetCommandWide(char[] Buffer, ULONG BufferSize, ULONGByReference CommandSize) { + return _invokeHR(VTIndices2.GET_COMMAND_WIDE, getPointer(), Buffer, BufferSize, + CommandSize); + } + + @Override + public HRESULT SetComamndWide(WString Command) { + return _invokeHR(VTIndices2.SET_COMMAND_WIDE, getPointer(), Command); + } + + @Override + public HRESULT GetOffsetExpressionWide(char[] Buffer, ULONG BufferSize, + ULONGByReference ExpressionSize) { + return _invokeHR(VTIndices2.GET_OFFSET_EXPRESSION_WIDE, getPointer(), Buffer, BufferSize, + ExpressionSize); + } + + @Override + public HRESULT SetOffsetExpressionWide(WString Expression) { + return _invokeHR(VTIndices2.SET_COMMAND_WIDE, getPointer(), Expression); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint3.java new file mode 100644 index 0000000000..f4f0b7b6e3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/breakpoint/WrapIDebugBreakpoint3.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.breakpoint; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.Guid.GUID; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class WrapIDebugBreakpoint3 extends WrapIDebugBreakpoint2 implements IDebugBreakpoint3 { + public static class ByReference extends WrapIDebugBreakpoint3 implements Structure.ByReference { + } + + public WrapIDebugBreakpoint3() { + } + + public WrapIDebugBreakpoint3(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetGuid(GUID.ByReference Guid) { + return _invokeHR(VTIndices3.GET_GUID, getPointer(), Guid); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient.java new file mode 100644 index 0000000000..7fab436858 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient.java @@ -0,0 +1,184 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.IUnknown; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; +import agent.dbgeng.jna.dbgeng.event.IDebugEventCallbacks; +import agent.dbgeng.jna.dbgeng.io.IDebugInputCallbacks; +import agent.dbgeng.jna.dbgeng.io.IDebugOutputCallbacks; + +public interface IDebugClient extends IUnknown { + final IID IID_IDEBUG_CLIENT = new IID("27fe5639-8407-4f47-8364-ee118fb08ac8"); + + enum VTIndices implements VTableIndex { + ATTACH_KERNEL, // + GET_KERNEL_CONNECTION_OPTIONS, // + SET_KERNEL_CONNECTION_OPTIONS, // + START_PROCESS_SERVER, // + CONNECT_PROCESS_SERVER, // + DISCONNECT_PROCESS_SERVER, // + GET_RUNNING_PROCESS_SYSTEM_IDS, // + GET_RUNNING_PROCESS_SYSTEM_ID_BY_EXECUTABLE_NAME, // + GET_RUNNING_PROCESS_DESCRIPTION, // + ATTACH_PROCESS, // + CREATE_PROCESS, // + CREATE_PROCESS_AND_ATTACH, // + GET_PROCESS_OPTIONS, // + ADD_PROCESS_OPTIONS, // + REMOVE_PROCESS_OPTIONS, // + SET_PROCESS_OPTIONS, // + OPEN_DUMP_FILE, // + WRITE_DUMP_FILE, // + CONNECTION_SESSION, // + START_SERVER, // + OUTPUT_SERVERS, // + TERMINATE_PROCESSES, // + DETACH_PROCESSES, // + END_SESSION, // + GET_EXIT_CODE, // + DISPATCH_CALLBACKS, // + EXIT_DISPATCH, // + CREATE_CLIENT, // + GET_INPUT_CALLBACKS, // + SET_INPUT_CALLBACKS, // + GET_OUTPUT_CALLBACKS, // + SET_OUTPUT_CALLBACKS, // + GET_OUTPUT_MASK, // + SET_OUTPUT_MASK, // + GET_OTHER_OUTPUT_MASK, // + SET_OTHER_OUTPUT_MASK, // + GET_OUTPUT_WIDTH, // + SET_OUTPUT_WIDTH, // + GET_OUTPUT_LINE_PREFIX, // + SET_OUTPUT_LINE_PREFIX, // + GET_IDENTITY, // + OUTPUT_IDENTITY, // + GET_EVENT_CALLBACKS, // + SET_EVENT_CALLBACKS, // + FLUSH_CALLBACKS, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT AttachKernel(ULONG Flags, String ConnectOptions); + + HRESULT GetKernelConnectionOptions(byte[] Buffer, ULONG BufferSize, + ULONGByReference OptionsSize); + + HRESULT SetKernelConnectionOptions(String Options); + + HRESULT StartProcessServer(ULONG Flags, String Options, Pointer Reserved); + + HRESULT ConnectProcessServer(String RemoteOptions, ULONGLONGByReference Server); + + HRESULT DisconnectProcessServer(ULONGLONG Server); + + HRESULT GetRunningProcessSystemIds(ULONGLONG Server, int[] Ids, ULONG Count, + ULONGByReference ActualCount); + + HRESULT GetRunningProcessSystemIdByExecutableName(ULONGLONG Server, String ExeName, ULONG Flags, + ULONGByReference Id); + + HRESULT GetRunningProcessDescription(ULONGLONG Server, ULONG SystemId, ULONG Flags, + byte[] ExeName, ULONG ExeNameSize, ULONGByReference ActualExeNameSize, + byte[] Description, ULONG DescriptionSize, ULONGByReference ActualDescriptionSize); + + HRESULT AttachProcess(ULONGLONG Server, ULONG ProcessId, ULONG AttachFlags); + + HRESULT CreateProcess(ULONGLONG Server, String CommandLine, ULONG CreateFlags); + + HRESULT CreateProcessAndAttach(ULONGLONG Server, String CommandLine, ULONG CreateFlags, + ULONG pid, ULONG AttachFlags); + + HRESULT GetProcessOptions(ULONGByReference Options); + + HRESULT AddProcessOptions(ULONG Options); + + HRESULT RemoveProcessOptions(ULONG Options); + + HRESULT SetProcessOptions(ULONG Options); + + HRESULT OpenDumpFile(String DumpFile); + + HRESULT WriteDumpFile(String DumpFile, ULONG Qualifier); + + HRESULT ConnectSession(ULONG Flags, ULONG HistoryLimit); + + HRESULT StartServer(String Options); + + HRESULT OutputServers(ULONG OutputControl, String Machine, ULONG Flags); + + HRESULT TerminateProcesses(); + + HRESULT DetachProcesses(); + + HRESULT EndSession(ULONG Flags); + + HRESULT GetExitCode(ULONGByReference Code); + + HRESULT DispatchCallbacks(ULONG Timeout); + + HRESULT ExitDispatch(IDebugClient Client); + + HRESULT CreateClient(PointerByReference Client); + + HRESULT GetInputCallbacks(PointerByReference Callbacks); + + HRESULT SetInputCallbacks(IDebugInputCallbacks Callbacks); + + HRESULT GetOutputCallbacks(Pointer Callbacks); + + HRESULT SetOutputCallbacks(IDebugOutputCallbacks Callbacks); + + HRESULT GetOutputMask(ULONGByReference Mask); + + HRESULT SetOutputMask(ULONG Mask); + + HRESULT GetOtherOutputMask(IDebugClient Client, ULONGByReference Mask); + + HRESULT SetOtherOutputMask(IDebugClient Client, ULONG Mask); + + HRESULT GetOutputWidth(ULONGByReference Columns); + + HRESULT SetOutputWidth(ULONG Columns); + + HRESULT GetOutputLinePrefix(byte[] Buffer, ULONG BufferSize, ULONGByReference PrefixSize); + + HRESULT SetOutputLinePrefix(String Prefix); + + HRESULT GetIdentity(byte[] Buffer, ULONG BufferSize, ULONGByReference IdentitySize); + + HRESULT OutputIdentity(ULONG OutputControl, ULONG Flags, String Format); + + HRESULT GetEventCallbacks(Pointer Callbacks); + + HRESULT SetEventCallbacks(IDebugEventCallbacks Callbacks); + + HRESULT FlushCallbacks(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient2.java new file mode 100644 index 0000000000..d66a6eaa5b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient2.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugClient2 extends IDebugClient { + final IID IID_IDEBUG_CLIENT2 = new IID("edbed635-372e-4dab-bbfe-ed0d2f63be81"); + + enum VTIndices2 implements VTableIndex { + WRITE_DUMP_FILE2, // + ADD_DUMP_INFORMATION_FILE, // + END_PROCESS_SERVER, // + WAIT_FOR_PROCESS_SERVER_END, // + IS_KERNEL_DEBUGGER_ENABLED, // + TERMINATE_CURRENT_PROCESS, // + DETACH_CURRENT_PROCESS, // + ABANDON_CURRENT_PROCESS, // + ; + + static int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT WriteDumpFile2(String DumpFile, ULONG Qualifier, ULONG FormatFlags, String Comment); + + HRESULT AddDumpInformationFile(String InfoFile, ULONG Type); + + HRESULT EndProcessServer(ULONGLONG Server); + + HRESULT WaitForProcessServerEnd(ULONG Timeout); + + HRESULT IsKernelDebuggerEnabled(); + + HRESULT TerminateCurrentProcess(); + + HRESULT DetachCurrentProcess(); + + HRESULT AbandonCurrentProcess(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient3.java new file mode 100644 index 0000000000..c260e62013 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient3.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugClient3 extends IDebugClient2 { + final IID IID_IDEBUG_CLIENT3 = new IID("dd492d7f-71b8-4ad6-a8dc-1c887479ff91"); + + enum VTIndices3 implements VTableIndex { + GET_RUNNING_PROCESS_SYSTEM_ID_BY_EXECUTABLE_NAME_WIDE, // + GET_RUNNING_PROCESS_DESCRIPTION_WIDE, // + CREATE_PROCESS_WIDE, // + CREATE_PROCESS_AND_ATTACH_WIDE, // + ; + + static int start = VTableIndex.follow(VTIndices2.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetRunningProcessSystemIdByExecutableNameWide(ULONGLONG Server, WString ExeName, + ULONG Flags, ULONGByReference Id); + + HRESULT GetRunningProcessDescriptionWide(ULONGLONG Server, ULONG SystemId, ULONG Flags, + char[] ExeName, ULONG ExeNameSize, ULONGByReference ActualExeNameSize, + char[] Description, ULONG DescriptionSize, ULONGByReference ActualDescriptionSize); + + HRESULT CreateProcessWide(ULONGLONG Server, WString CommandLine, ULONG CreateFlags); + + HRESULT CreateProcessAndAttachWide(ULONGLONG Server, WString CommandLine, ULONG CreateFlags, + ULONG ProcessId, ULONG AttachFlags); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient4.java new file mode 100644 index 0000000000..8df1da1f88 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient4.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugClient4 extends IDebugClient3 { + final IID IID_IDEBUG_CLIENT4 = new IID("ca83c3de-5089-4cf8-93c8-d892387f2a5e"); + + enum VTIndices4 implements VTableIndex { + OPEN_DUMP_FILE_WIDE, // + WRITE_DUMP_FILE_WIDE, // + ADD_DUMP_INFORMATION_FILE_WIDE, // + GET_NUMBER_DUMP_FILES, // + GET_DUMP_FILE, // + GET_DUMP_FILE_WIDE, // + ; + + static int start = VTableIndex.follow(VTIndices3.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT OpenDumpFileWide(WString FileName, ULONGLONG FileHandle); + + HRESULT WriteDumpFileWide(WString FileName, ULONGLONG FileHandle, ULONG Qualifier, + ULONG FormatFlags, WString Comment); + + HRESULT AddDumpInformationFileWide(WString FileName, ULONGLONG FileHandle, ULONG Type); + + HRESULT GetNumberDumpFiles(ULONGByReference Number); + + HRESULT GetDumpFile(ULONG Index, byte[] Buffer, ULONG BufferSize, ULONGByReference NameSize, + ULONGLONGByReference Handle, ULONGByReference Type); + + HRESULT GetDumpFileWide(ULONG Index, char[] Buffer, ULONG BufferSize, ULONGByReference NameSize, + ULONGLONGByReference Handle, ULONGByReference Type); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient5.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient5.java new file mode 100644 index 0000000000..e340f65ef8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient5.java @@ -0,0 +1,135 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; +import agent.dbgeng.jna.dbgeng.event.IDebugEventCallbacksWide; +import agent.dbgeng.jna.dbgeng.io.IDebugOutputCallbacksWide; + +public interface IDebugClient5 extends IDebugClient4 { + final IID IID_IDEBUG_CLIENT5 = new IID("e3acb9d7-7ec2-4f0c-a0da-e81e0cbbe628"); + + enum VTIndices5 implements VTableIndex { + ATTACH_KERNEL_WIDE, // + GET_KERNEL_CONNECTION_OPTIONS_WIDE, // + SET_KERNEL_CONNECTION_OPTIONS_WIDE, // + START_PROCESS_SERVER_WIDE, // + CONNECT_PROCESS_SERVER_WIDE, // + START_SERVER_WIDE, // + OUTPUT_SERVERS_WIDE, // + GET_OUTPUT_CALLBACKS_WIDE, // + SET_OUTPUT_CALLBACKS_WIDE, // + GET_OUTPUT_LINE_PREFIX_WIDE, // + SET_OUTPUT_LINE_PREFIX_WIDE, // + GET_IDENTITY_WIDE, // + OUTPUT_IDENTITY_WIDE, // + GET_EVENT_CALLBACKS_WIDE, // + SET_EVENT_CALLBACKS_WIDE, // + CREATE_PROCESS2, // + CREATE_PROCESS2_WIDE, // + CREATE_PROCESS_AND_ATTACH2, // + CREATE_PROCESS_AND_ATTACH2_WIDE, // + PUSH_OUTPUT_LINE_PREFIX, // + PUSH_OUTPUT_LINE_PREFIX_WIDE, // + POP_OUTPUT_LINE_PREFIX, // + GET_NUMBER_INPUT_CALLBACKS, // + GET_NUMBER_OUTPUT_CALLBACKS, // + GET_NUMBER_EVENT_CALLBACKS, // + GET_QUIT_LOCK_STRING, // + SET_QUIT_LOCK_STRING, // + GET_QUIT_LOCK_STRING_WIDE, // + SET_QUIT_LOCK_STRING_WIDE, // + ; + + static int start = VTableIndex.follow(VTIndices4.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT AttachKernelWide(ULONG Flags, WString ConnectOptions); + + HRESULT GetKernelConnectionOptionsWide(char[] Buffer, ULONG BufferSize, + ULONGByReference OptionsSize); + + HRESULT SetKernelConnectionOptionsWide(WString Options); + + HRESULT StartProcessServerWide(ULONG Flags, WString Options, Pointer Reserved); + + HRESULT ConnectProcessServerWide(WString RemoteOptions, ULONGLONGByReference Server); + + HRESULT StartServerWide(WString Options); + + HRESULT OutputServersWide(WString Options); + + HRESULT GetOutputCallbacksWide(Pointer Callbacks); + + HRESULT SetOutputCallbacksWide(IDebugOutputCallbacksWide Callbacks); + + HRESULT GetOutputLinePrefixWide(char[] Buffer, ULONG BufferSize, ULONGByReference PrefixSize); + + HRESULT SetOuutputLinePrefixWide(WString Prefix); + + HRESULT GetIdentityWide(char[] Buffer, ULONG BufferSize, ULONGByReference IdentitySize); + + HRESULT OutputIdentityWide(ULONG OutputControl, ULONG Flags, WString Format); + + HRESULT GetEventCallbacksWide(Pointer Callbacks); + + HRESULT SetEventCallbacksWide(IDebugEventCallbacksWide Callbacks); + + HRESULT CreateProcess2(ULONGLONG Server, String CommandLine, Pointer OptionsBuffer, + ULONG OptionsBufferSize, String InitialDirectory, String Environment); + + HRESULT CreateProcess2Wide(ULONGLONG Server, WString CommandLine, Pointer OptionsBuffer, + ULONG OptionsBufferSize, WString InitialDirectory, WString Environment); + + HRESULT CreateProcessAndAttach2(ULONGLONG Server, String CommandLine, Pointer OptionsBuffer, + ULONG OptionsBufferSize, String InitialDirectory, String Environment, ULONG ProcessId, + ULONG AttachFlags); + + HRESULT CreateProcessAndAttach2Wide(ULONGLONG Server, WString CommandLine, + Pointer OptionsBuffer, ULONG OptionsBufferSize, WString InitialDirectory, + WString Environment, ULONG ProcessId, ULONG AttachFlags); + + HRESULT PushOutputLinePrefix(String NewPrefix, ULONGLONGByReference Handle); + + HRESULT PushOutputLinePrefixWide(WString NewPrefix, ULONGLONGByReference Handle); + + HRESULT PopOutputLinePrefix(ULONGLONG Handle); + + HRESULT GetNumberInputCallbacks(ULONGByReference Count); + + HRESULT GetNumberOutputCallbacks(ULONGByReference Count); + + HRESULT GetNumberEventCallbacks(ULONG EventFlags, ULONGByReference Count); + + HRESULT GetQuitLockString(byte[] Buffer, ULONG BufferSize, ULONGByReference StringSize); + + HRESULT SetQuitLockString(String String); + + HRESULT GetQuitLockStringWide(char[] Buffer, ULONG BufferSize, ULONGByReference StringSize); + + HRESULT SetQuitLockStringWide(WString String); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient6.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient6.java new file mode 100644 index 0000000000..b0dcd39598 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient6.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; +import agent.dbgeng.jna.dbgeng.event.IDebugEventContextCallbacks; + +public interface IDebugClient6 extends IDebugClient5 { + final IID IID_IDEBUG_CLIENT6 = new IID("fd28b4c5-c498-4686-a28e-62cad2154eb3"); + + enum VTIndices6 implements VTableIndex { + SET_EVENT_CONTEXT_CALLBACKS, // + ; + + static int start = VTableIndex.follow(VTIndices5.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT SetEventContextCallbacks(IDebugEventContextCallbacks Callbacks); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient7.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient7.java new file mode 100644 index 0000000000..3d662d9ef4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/IDebugClient7.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugClient7 extends IDebugClient6 { + final IID IID_IDEBUG_CLIENT7 = new IID("13586be3-542e-481e-b1f2-8497ba74f9a9"); + + enum VTIndices7 implements VTableIndex { + SET_CLIENT_CONTEXT, // + ; + + static int start = VTableIndex.follow(VTIndices6.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT SetClientContext(Pointer Context, ULONG ContextSize); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient.java new file mode 100644 index 0000000000..745b9dd7c1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient.java @@ -0,0 +1,281 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils; +import agent.dbgeng.jna.dbgeng.event.IDebugEventCallbacks; +import agent.dbgeng.jna.dbgeng.io.IDebugInputCallbacks; +import agent.dbgeng.jna.dbgeng.io.IDebugOutputCallbacks; + +/** + * Wrapper class for the IDebugClient interface + */ +public class WrapIDebugClient extends UnknownWithUtils implements IDebugClient { + public static class ByReference extends WrapIDebugClient implements Structure.ByReference { + } + + public WrapIDebugClient() { + } + + public WrapIDebugClient(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT AttachKernel(ULONG Flags, String ConnectOptions) { + return _invokeHR(VTIndices.ATTACH_KERNEL, getPointer(), Flags, ConnectOptions); + } + + @Override + public HRESULT GetKernelConnectionOptions(byte[] Buffer, ULONG BufferSize, + ULONGByReference OptionsSize) { + return _invokeHR(VTIndices.GET_KERNEL_CONNECTION_OPTIONS, getPointer(), Buffer, BufferSize, + OptionsSize); + } + + @Override + public HRESULT SetKernelConnectionOptions(String Options) { + return _invokeHR(VTIndices.SET_KERNEL_CONNECTION_OPTIONS, getPointer(), Options); + } + + @Override + public HRESULT StartProcessServer(ULONG Flags, String Options, Pointer Reserved) { + return _invokeHR(VTIndices.START_PROCESS_SERVER, getPointer(), Flags, Options, Reserved); + } + + @Override + public HRESULT ConnectProcessServer(String RemoteOptions, ULONGLONGByReference Server) { + return _invokeHR(VTIndices.CONNECT_PROCESS_SERVER, getPointer(), RemoteOptions, Server); + } + + @Override + public HRESULT DisconnectProcessServer(ULONGLONG Server) { + return _invokeHR(VTIndices.DISCONNECT_PROCESS_SERVER, getPointer(), Server); + } + + @Override + public HRESULT GetRunningProcessSystemIds(ULONGLONG Server, int[] Ids, ULONG Count, + ULONGByReference ActualCount) { + return _invokeHR(VTIndices.GET_RUNNING_PROCESS_SYSTEM_IDS, getPointer(), Server, Ids, Count, + ActualCount); + } + + @Override + public HRESULT GetRunningProcessSystemIdByExecutableName(ULONGLONG Server, String ExeName, + ULONG Flags, ULONGByReference Id) { + return _invokeHR(VTIndices.GET_RUNNING_PROCESS_SYSTEM_ID_BY_EXECUTABLE_NAME, getPointer(), + Server, ExeName, Flags, Id); + } + + @Override + public HRESULT GetRunningProcessDescription(ULONGLONG Server, ULONG SystemId, ULONG Flags, + byte[] ExeName, ULONG ExeNameSize, ULONGByReference ActualExeNameSize, + byte[] Description, ULONG DescriptionSize, ULONGByReference ActualDescriptionSize) { + return _invokeHR(VTIndices.GET_RUNNING_PROCESS_DESCRIPTION, getPointer(), Server, SystemId, + Flags, ExeName, ExeNameSize, ActualExeNameSize, Description, DescriptionSize, + ActualDescriptionSize); + } + + @Override + public HRESULT AttachProcess(ULONGLONG Server, ULONG ProcessId, ULONG AttachFlags) { + return _invokeHR(VTIndices.ATTACH_PROCESS, getPointer(), Server, ProcessId, AttachFlags); + } + + @Override + public HRESULT CreateProcess(ULONGLONG Server, String CommandLine, ULONG CreateFlags) { + return _invokeHR(VTIndices.CREATE_PROCESS, getPointer(), Server, CommandLine, CreateFlags); + } + + @Override + public HRESULT CreateProcessAndAttach(ULONGLONG Server, String CommandLine, ULONG CreateFlags, + ULONG ProcessId, ULONG AttachFlags) { + return _invokeHR(VTIndices.CREATE_PROCESS_AND_ATTACH, getPointer(), Server, CommandLine, + CreateFlags, ProcessId, AttachFlags); + } + + @Override + public HRESULT GetProcessOptions(ULONGByReference Options) { + return _invokeHR(VTIndices.GET_PROCESS_OPTIONS, getPointer(), Options); + } + + @Override + public HRESULT AddProcessOptions(ULONG Options) { + return _invokeHR(VTIndices.ADD_PROCESS_OPTIONS, getPointer(), Options); + } + + @Override + public HRESULT RemoveProcessOptions(ULONG Options) { + return _invokeHR(VTIndices.REMOVE_PROCESS_OPTIONS, getPointer(), Options); + } + + @Override + public HRESULT SetProcessOptions(ULONG Options) { + return _invokeHR(VTIndices.SET_PROCESS_OPTIONS, getPointer(), Options); + } + + @Override + public HRESULT OpenDumpFile(String DumpFile) { + return _invokeHR(VTIndices.OPEN_DUMP_FILE, getPointer(), DumpFile); + } + + @Override + public HRESULT WriteDumpFile(String DumpFile, ULONG Qualifier) { + return _invokeHR(VTIndices.WRITE_DUMP_FILE, getPointer(), DumpFile, Qualifier); + } + + @Override + public HRESULT ConnectSession(ULONG Flags, ULONG HistoryLimit) { + return _invokeHR(VTIndices.CONNECTION_SESSION, getPointer(), Flags, HistoryLimit); + } + + @Override + public HRESULT StartServer(String Options) { + return _invokeHR(VTIndices.START_SERVER, getPointer(), Options); + } + + @Override + public HRESULT OutputServers(ULONG OutputControl, String Machine, ULONG Flags) { + return _invokeHR(VTIndices.OUTPUT_SERVERS, getPointer(), OutputControl, Machine, Flags); + } + + @Override + public HRESULT TerminateProcesses() { + return _invokeHR(VTIndices.TERMINATE_PROCESSES, getPointer()); + } + + @Override + public HRESULT DetachProcesses() { + return _invokeHR(VTIndices.DETACH_PROCESSES, getPointer()); + } + + @Override + public HRESULT EndSession(ULONG Flags) { + return _invokeHR(VTIndices.END_SESSION, getPointer(), Flags); + } + + @Override + public HRESULT GetExitCode(ULONGByReference Code) { + return _invokeHR(VTIndices.GET_EXIT_CODE, getPointer(), Code); + } + + @Override + public HRESULT DispatchCallbacks(ULONG Timeout) { + return _invokeHR(VTIndices.DISPATCH_CALLBACKS, getPointer(), Timeout); + } + + @Override + public HRESULT ExitDispatch(IDebugClient Client) { + return _invokeHR(VTIndices.EXIT_DISPATCH, getPointer(), Client); + } + + @Override + public HRESULT CreateClient(PointerByReference Client) { + return _invokeHR(VTIndices.CREATE_CLIENT, getPointer(), Client); + } + + @Override + public HRESULT GetInputCallbacks(PointerByReference Callbacks) { + return _invokeHR(VTIndices.GET_INPUT_CALLBACKS, getPointer(), Callbacks); + } + + @Override + public HRESULT SetInputCallbacks(IDebugInputCallbacks Callbacks) { + return _invokeHR(VTIndices.SET_INPUT_CALLBACKS, getPointer(), Callbacks); + } + + @Override + public HRESULT GetOutputCallbacks(Pointer Callbacks) { + return _invokeHR(VTIndices.GET_OUTPUT_CALLBACKS, getPointer(), Callbacks); + } + + @Override + public HRESULT SetOutputCallbacks(IDebugOutputCallbacks Callbacks) { + return _invokeHR(VTIndices.SET_OUTPUT_CALLBACKS, getPointer(), Callbacks); + } + + @Override + public HRESULT GetOutputMask(ULONGByReference Mask) { + return _invokeHR(VTIndices.GET_OUTPUT_MASK, getPointer(), Mask); + } + + @Override + public HRESULT SetOutputMask(ULONG Mask) { + return _invokeHR(VTIndices.SET_OUTPUT_MASK, getPointer(), Mask); + } + + @Override + public HRESULT GetOtherOutputMask(IDebugClient Client, ULONGByReference Mask) { + return _invokeHR(VTIndices.GET_OTHER_OUTPUT_MASK, getPointer(), Client, Mask); + } + + @Override + public HRESULT SetOtherOutputMask(IDebugClient Client, ULONG Mask) { + return _invokeHR(VTIndices.SET_OTHER_OUTPUT_MASK, getPointer(), Client, Mask); + } + + @Override + public HRESULT GetOutputWidth(ULONGByReference Columns) { + return _invokeHR(VTIndices.GET_OUTPUT_WIDTH, getPointer(), Columns); + } + + @Override + public HRESULT SetOutputWidth(ULONG Columns) { + return _invokeHR(VTIndices.SET_OUTPUT_WIDTH, getPointer(), Columns); + } + + @Override + public HRESULT GetOutputLinePrefix(byte[] Buffer, ULONG BufferSize, + ULONGByReference PrefixSize) { + return _invokeHR(VTIndices.GET_OUTPUT_LINE_PREFIX, getPointer(), Buffer, BufferSize, + PrefixSize); + } + + @Override + public HRESULT SetOutputLinePrefix(String Prefix) { + return _invokeHR(VTIndices.SET_OUTPUT_LINE_PREFIX, getPointer(), Prefix); + } + + @Override + public HRESULT GetIdentity(byte[] Buffer, ULONG BufferSize, ULONGByReference IdentitySize) { + return _invokeHR(VTIndices.GET_IDENTITY, getPointer(), Buffer, BufferSize, IdentitySize); + } + + @Override + public HRESULT OutputIdentity(ULONG OutputControl, ULONG Flags, String Format) { + return _invokeHR(VTIndices.OUTPUT_IDENTITY, getPointer(), OutputControl, Flags, Format); + } + + @Override + public HRESULT GetEventCallbacks(Pointer Callbacks) { + return _invokeHR(VTIndices.GET_EVENT_CALLBACKS, getPointer(), Callbacks); + } + + @Override + public HRESULT SetEventCallbacks(IDebugEventCallbacks Callbacks) { + return _invokeHR(VTIndices.SET_EVENT_CALLBACKS, getPointer(), Callbacks); + } + + @Override + public HRESULT FlushCallbacks() { + return _invokeHR(VTIndices.FLUSH_CALLBACKS, getPointer()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient2.java new file mode 100644 index 0000000000..f23e64542a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient2.java @@ -0,0 +1,79 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +/** + * Wrapper class for the IDebugClient interface + */ +public class WrapIDebugClient2 extends WrapIDebugClient implements IDebugClient2 { + public static class ByReference extends WrapIDebugClient2 implements Structure.ByReference { + } + + public WrapIDebugClient2() { + } + + public WrapIDebugClient2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT WriteDumpFile2(String DumpFile, ULONG Qualifier, ULONG FormatFlags, + String Comment) { + return _invokeHR(VTIndices2.WRITE_DUMP_FILE2, getPointer(), DumpFile, Qualifier, + FormatFlags, Comment); + } + + @Override + public HRESULT AddDumpInformationFile(String InfoFile, ULONG Type) { + return _invokeHR(VTIndices2.ADD_DUMP_INFORMATION_FILE, getPointer(), InfoFile, Type); + } + + @Override + public HRESULT EndProcessServer(ULONGLONG Server) { + return _invokeHR(VTIndices2.END_PROCESS_SERVER, getPointer(), Server); + } + + @Override + public HRESULT WaitForProcessServerEnd(ULONG Timeout) { + return _invokeHR(VTIndices2.WAIT_FOR_PROCESS_SERVER_END, getPointer(), Timeout); + } + + @Override + public HRESULT IsKernelDebuggerEnabled() { + return _invokeHR(VTIndices2.IS_KERNEL_DEBUGGER_ENABLED, getPointer()); + } + + @Override + public HRESULT TerminateCurrentProcess() { + return _invokeHR(VTIndices2.TERMINATE_CURRENT_PROCESS, getPointer()); + } + + @Override + public HRESULT DetachCurrentProcess() { + return _invokeHR(VTIndices2.DETACH_CURRENT_PROCESS, getPointer()); + } + + @Override + public HRESULT AbandonCurrentProcess() { + return _invokeHR(VTIndices2.ABANDON_CURRENT_PROCESS, getPointer()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient3.java new file mode 100644 index 0000000000..8fbba8675b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient3.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +/** + * Wrapper class for the IDebugClient interface + */ +public class WrapIDebugClient3 extends WrapIDebugClient2 implements IDebugClient3 { + public static class ByReference extends WrapIDebugClient3 implements Structure.ByReference { + } + + public WrapIDebugClient3() { + } + + public WrapIDebugClient3(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetRunningProcessSystemIdByExecutableNameWide(ULONGLONG Server, WString ExeName, + ULONG Flags, ULONGByReference Id) { + return _invokeHR(VTIndices3.GET_RUNNING_PROCESS_SYSTEM_ID_BY_EXECUTABLE_NAME_WIDE, + getPointer(), Server, ExeName, Flags, Id); + } + + @Override + public HRESULT GetRunningProcessDescriptionWide(ULONGLONG Server, ULONG SystemId, ULONG Flags, + char[] ExeName, ULONG ExeNameSize, ULONGByReference ActualExeNameSize, + char[] Description, ULONG DescriptionSize, ULONGByReference ActualDescriptionSize) { + return _invokeHR(VTIndices3.GET_RUNNING_PROCESS_DESCRIPTION_WIDE, getPointer(), Server, + SystemId, Flags, ExeName, ExeNameSize, ActualExeNameSize, Description, DescriptionSize, + ActualDescriptionSize); + } + + @Override + public HRESULT CreateProcessWide(ULONGLONG Server, WString CommandLine, ULONG CreateFlags) { + return _invokeHR(VTIndices3.CREATE_PROCESS_WIDE, getPointer(), Server, CommandLine, + CreateFlags); + } + + @Override + public HRESULT CreateProcessAndAttachWide(ULONGLONG Server, WString CommandLine, + ULONG CreateFlags, ULONG ProcessId, ULONG AttachFlags) { + return _invokeHR(VTIndices3.CREATE_PROCESS_AND_ATTACH_WIDE, getPointer(), Server, + CommandLine, CreateFlags, ProcessId, AttachFlags); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient4.java new file mode 100644 index 0000000000..ccadf743f3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient4.java @@ -0,0 +1,72 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +/** + * Wrapper class for the IDebugClient interface + */ +public class WrapIDebugClient4 extends WrapIDebugClient3 implements IDebugClient4 { + public static class ByReference extends WrapIDebugClient4 implements Structure.ByReference { + } + + public WrapIDebugClient4() { + } + + public WrapIDebugClient4(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT OpenDumpFileWide(WString FileName, ULONGLONG FileHandle) { + return _invokeHR(VTIndices4.OPEN_DUMP_FILE_WIDE, getPointer(), FileName, FileHandle); + } + + @Override + public HRESULT WriteDumpFileWide(WString FileName, ULONGLONG FileHandle, ULONG Qualifier, + ULONG FormatFlags, WString Comment) { + return _invokeHR(VTIndices4.WRITE_DUMP_FILE_WIDE, getPointer(), FileName, FileHandle, + Qualifier, FormatFlags, Comment); + } + + @Override + public HRESULT AddDumpInformationFileWide(WString FileName, ULONGLONG FileHandle, ULONG Type) { + return _invokeHR(VTIndices4.ADD_DUMP_INFORMATION_FILE_WIDE, getPointer(), FileName, + FileHandle, Type); + } + + @Override + public HRESULT GetNumberDumpFiles(ULONGByReference Number) { + return _invokeHR(VTIndices4.GET_NUMBER_DUMP_FILES, getPointer(), Number); + } + + @Override + public HRESULT GetDumpFile(ULONG Index, byte[] Buffer, ULONG BufferSize, + ULONGByReference NameSize, ULONGLONGByReference Handle, ULONGByReference Type) { + return _invokeHR(VTIndices4.GET_DUMP_FILE, getPointer(), Index, Buffer, BufferSize, + NameSize, Handle, Type); + } + + @Override + public HRESULT GetDumpFileWide(ULONG Index, char[] Buffer, ULONG BufferSize, + ULONGByReference NameSize, ULONGLONGByReference Handle, ULONGByReference Type) { + return _invokeHR(VTIndices4.GET_DUMP_FILE_WIDE, getPointer(), Index, Buffer, BufferSize, + NameSize, Handle, Type); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient5.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient5.java new file mode 100644 index 0000000000..9c5ccda1fa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient5.java @@ -0,0 +1,206 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.event.IDebugEventCallbacksWide; +import agent.dbgeng.jna.dbgeng.io.IDebugOutputCallbacksWide; + +/** + * Wrapper class for the IDebugClient interface + */ +public class WrapIDebugClient5 extends WrapIDebugClient4 implements IDebugClient5 { + public static class ByReference extends WrapIDebugClient5 implements Structure.ByReference { + } + + public WrapIDebugClient5() { + } + + public WrapIDebugClient5(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT AttachKernelWide(ULONG Flags, WString ConnectOptions) { + return _invokeHR(VTIndices5.ATTACH_KERNEL_WIDE, getPointer(), Flags, ConnectOptions); + } + + @Override + public HRESULT GetKernelConnectionOptionsWide(char[] Buffer, ULONG BufferSize, + ULONGByReference OptionsSize) { + return _invokeHR(VTIndices5.GET_KERNEL_CONNECTION_OPTIONS_WIDE, getPointer(), Buffer, + BufferSize, OptionsSize); + } + + @Override + public HRESULT SetKernelConnectionOptionsWide(WString Options) { + return _invokeHR(VTIndices5.SET_KERNEL_CONNECTION_OPTIONS_WIDE, getPointer(), Options); + } + + @Override + public HRESULT StartProcessServerWide(ULONG Flags, WString Options, Pointer Reserved) { + return _invokeHR(VTIndices5.START_PROCESS_SERVER_WIDE, getPointer(), Flags, Options, + Reserved); + } + + @Override + public HRESULT ConnectProcessServerWide(WString RemoteOptions, ULONGLONGByReference Server) { + return _invokeHR(VTIndices5.CONNECT_PROCESS_SERVER_WIDE, getPointer(), RemoteOptions, + Server); + } + + @Override + public HRESULT StartServerWide(WString Options) { + return _invokeHR(VTIndices5.START_SERVER_WIDE, getPointer(), Options); + } + + @Override + public HRESULT OutputServersWide(WString Options) { + return _invokeHR(VTIndices5.OUTPUT_SERVERS_WIDE, getPointer(), Options); + } + + @Override + public HRESULT GetOutputCallbacksWide(Pointer Callbacks) { + return _invokeHR(VTIndices5.GET_OUTPUT_CALLBACKS_WIDE, getPointer(), Callbacks); + } + + @Override + public HRESULT SetOutputCallbacksWide(IDebugOutputCallbacksWide Callbacks) { + return _invokeHR(VTIndices5.SET_OUTPUT_CALLBACKS_WIDE, getPointer(), Callbacks); + } + + @Override + public HRESULT GetOutputLinePrefixWide(char[] Buffer, ULONG BufferSize, + ULONGByReference PrefixSize) { + return _invokeHR(VTIndices5.GET_OUTPUT_LINE_PREFIX_WIDE, getPointer(), Buffer, BufferSize, + PrefixSize); + } + + @Override + public HRESULT SetOuutputLinePrefixWide(WString Prefix) { + return _invokeHR(VTIndices5.SET_OUTPUT_LINE_PREFIX_WIDE, getPointer(), Prefix); + } + + @Override + public HRESULT GetIdentityWide(char[] Buffer, ULONG BufferSize, ULONGByReference IdentitySize) { + return _invokeHR(VTIndices5.GET_IDENTITY_WIDE, getPointer(), Buffer, BufferSize, + IdentitySize); + } + + @Override + public HRESULT OutputIdentityWide(ULONG OutputControl, ULONG Flags, WString Format) { + return _invokeHR(VTIndices5.OUTPUT_IDENTITY_WIDE, getPointer(), OutputControl, Flags, + Format); + } + + @Override + public HRESULT GetEventCallbacksWide(Pointer Callbacks) { + return _invokeHR(VTIndices5.GET_EVENT_CALLBACKS_WIDE, getPointer(), Callbacks); + } + + @Override + public HRESULT SetEventCallbacksWide(IDebugEventCallbacksWide Callbacks) { + return _invokeHR(VTIndices5.SET_EVENT_CALLBACKS_WIDE, getPointer(), Callbacks); + } + + @Override + public HRESULT CreateProcess2(ULONGLONG Server, String CommandLine, Pointer OptionsBuffer, + ULONG OptionsBufferSize, String InitialDirectory, String Environment) { + return _invokeHR(VTIndices5.CREATE_PROCESS2, getPointer(), Server, CommandLine, + OptionsBuffer, OptionsBufferSize, InitialDirectory, Environment); + } + + @Override + public HRESULT CreateProcess2Wide(ULONGLONG Server, WString CommandLine, Pointer OptionsBuffer, + ULONG OptionsBufferSize, WString InitialDirectory, WString Environment) { + return _invokeHR(VTIndices5.CREATE_PROCESS2_WIDE, getPointer(), Server, CommandLine, + OptionsBuffer, OptionsBufferSize, InitialDirectory, Environment); + } + + @Override + public HRESULT CreateProcessAndAttach2(ULONGLONG Server, String CommandLine, + Pointer OptionsBuffer, ULONG OptionsBufferSize, String InitialDirectory, + String Environment, ULONG ProcessId, ULONG AttachFlags) { + return _invokeHR(VTIndices5.CREATE_PROCESS_AND_ATTACH2, getPointer(), Server, CommandLine, + OptionsBuffer, OptionsBufferSize, InitialDirectory, Environment, ProcessId, + AttachFlags); + } + + @Override + public HRESULT CreateProcessAndAttach2Wide(ULONGLONG Server, WString CommandLine, + Pointer OptionsBuffer, ULONG OptionsBufferSize, WString InitialDirectory, + WString Environment, ULONG ProcessId, ULONG AttachFlags) { + return _invokeHR(VTIndices5.CREATE_PROCESS_AND_ATTACH2_WIDE, getPointer(), Server, + CommandLine, OptionsBuffer, OptionsBufferSize, InitialDirectory, Environment, ProcessId, + AttachFlags); + } + + @Override + public HRESULT PushOutputLinePrefix(String NewPrefix, ULONGLONGByReference Handle) { + return _invokeHR(VTIndices5.PUSH_OUTPUT_LINE_PREFIX, getPointer(), NewPrefix, Handle); + } + + @Override + public HRESULT PushOutputLinePrefixWide(WString NewPrefix, ULONGLONGByReference Handle) { + return _invokeHR(VTIndices5.PUSH_OUTPUT_LINE_PREFIX_WIDE, getPointer(), NewPrefix, Handle); + } + + @Override + public HRESULT PopOutputLinePrefix(ULONGLONG Handle) { + return _invokeHR(VTIndices5.POP_OUTPUT_LINE_PREFIX, getPointer(), Handle); + } + + @Override + public HRESULT GetNumberInputCallbacks(ULONGByReference Count) { + return _invokeHR(VTIndices5.GET_NUMBER_INPUT_CALLBACKS, getPointer(), Count); + } + + @Override + public HRESULT GetNumberOutputCallbacks(ULONGByReference Count) { + return _invokeHR(VTIndices5.GET_NUMBER_OUTPUT_CALLBACKS, getPointer(), Count); + } + + @Override + public HRESULT GetNumberEventCallbacks(ULONG EventFlags, ULONGByReference Count) { + return _invokeHR(VTIndices5.GET_NUMBER_EVENT_CALLBACKS, getPointer(), Count); + } + + @Override + public HRESULT GetQuitLockString(byte[] Buffer, ULONG BufferSize, ULONGByReference StringSize) { + return _invokeHR(VTIndices5.GET_QUIT_LOCK_STRING, getPointer(), Buffer, BufferSize, + StringSize); + } + + @Override + public HRESULT SetQuitLockString(String String) { + return _invokeHR(VTIndices5.SET_QUIT_LOCK_STRING, getPointer(), String); + } + + @Override + public HRESULT GetQuitLockStringWide(char[] Buffer, ULONG BufferSize, + ULONGByReference StringSize) { + return _invokeHR(VTIndices5.GET_QUIT_LOCK_STRING_WIDE, getPointer(), Buffer, BufferSize, + StringSize); + } + + @Override + public HRESULT SetQuitLockStringWide(WString String) { + return _invokeHR(VTIndices5.SET_QUIT_LOCK_STRING_WIDE, getPointer(), String); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient6.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient6.java new file mode 100644 index 0000000000..a21ae73255 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient6.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.event.IDebugEventContextCallbacks; + +/** + * Wrapper class for the IDebugClient interface + */ +public class WrapIDebugClient6 extends WrapIDebugClient5 implements IDebugClient6 { + public static class ByReference extends WrapIDebugClient6 implements Structure.ByReference { + } + + public WrapIDebugClient6() { + } + + public WrapIDebugClient6(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT SetEventContextCallbacks(IDebugEventContextCallbacks Callbacks) { + return _invokeHR(VTIndices6.SET_EVENT_CONTEXT_CALLBACKS, getPointer(), Callbacks); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient7.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient7.java new file mode 100644 index 0000000000..840d2b88b8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/client/WrapIDebugClient7.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.client; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +/** + * Wrapper class for the IDebugClient interface + */ +public class WrapIDebugClient7 extends WrapIDebugClient6 implements IDebugClient7 { + public static class ByReference extends WrapIDebugClient7 implements Structure.ByReference { + } + + public WrapIDebugClient7() { + } + + public WrapIDebugClient7(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT SetClientContext(Pointer Context, ULONG ContextSize) { + return _invokeHR(VTIndices7.SET_CLIENT_CONTEXT, getPointer(), Context, ContextSize); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl.java new file mode 100644 index 0000000000..665e2493b1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl.java @@ -0,0 +1,187 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.IUnknown; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_STACK_FRAME; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; +import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint; + +public interface IDebugControl extends IUnknown { + final IID IID_IDEBUG_CONTROL = new IID("5182e668-105e-416e-ad92-24ef800424ba"); + + enum VTIndices implements VTableIndex { + GET_INTERRUPT, // + SET_INTERRUPT, // + GET_INTERRUPT_TIMEOUT, // + SET_INTERRUPT_TIMEOUT, // + GET_LOG_FILE, // + OPEN_LOG_FILE, // + CLOSE_LOG_FILE, // + GET_LOG_MASK, // + SET_LOG_MASK, // + INPUT, // + RETURN_INPUT, // + OUTPUT, // + OUTPUT_VA_LIST, // + CONTROLLED_OUTPUT, // + CONTROLLED_OUTPUT_VA_LIST, // + OUTPUT_PROMPT, // + OUTPUT_PROMPT_VA_LIST, // + GET_PROMPT_TEXT, // + OUTPUT_CURRENT_STATE, // + OUTPUT_VERSION_INFORMATION, // + GET_NOTIFY_EVENT_HANDLE, // + SET_NOTIFY_EVENT_HANDLE, // + ASSEMBLE, // + DISASSEMBLE, // + GET_DISASSEMBLE_EFFECTIVE_OFFSET, // + OUTPUT_DISASSEMBLY, // + OUTPUT_DISASSEMBLY_LINES, // + GET_NEAR_INSTRUCTION, // + GET_STACK_TRACE, // + GET_RETURN_OFFSET, // + GET_OUTPUT_STACK_TRACE, // + GET_DEBUGGEE_TYPE, // + GET_ACTUAL_PROCESSOR_TYPE, // + GET_EXECUTING_PROCESSOR_TYPE, // + GET_NUMBER_POSSIBLE_EXECUTING_PROCESSOR_TYPES, // + GET_POSSIBLE_EXECUTING_PROCESSOR_TYPES, // + GET_NUMBER_PROCESSORS, // + GET_SYSTEM_VERSION, // + GET_PAGE_SIZE, // + IS_POINTER_64BIT, // + READ_BUG_CHECK_DATA, // + GET_NUMBER_SUPPORTED_PROCESSOR_TYPES, // + GET_SUPPORTED_PROCESSOR_TYPES, // + GET_PROCESSOR_TYPE_NAMES, // + GET_EFFECTIVE_PROCESSOR_TYPE, // + SET_EFFECTIVE_PROCESSOR_TYPE, // + GET_EXECUTION_STATUS, // + SET_EXECUTION_STATUS, // + GET_CODE_LEVEL, // + SET_CODE_LEVEL, // + GET_ENGINE_OPTIONS, // + ADD_ENGINE_OPTIONS, // + REMOVE_ENGINE_OPTIONS, // + SET_ENGINE_OPTIONS, // + GET_SYSTEM_ERROR_CONTROL, // + SET_SYSTEM_ERROR_CONTROL, // + GET_TEXT_MACRO, // + SET_TEXT_MACRO, // + GET_RADIX, // + SET_RADIX, // + EVALUATE, // + COERCE_VALUE, // + COERCE_VALUES, // + EXECUTE, // + EXECUTE_COMMAND_FILE, // + GET_NUMBER_BREAKPOINTS, // + GET_BREAKPOINT_BY_INDEX, // + GET_BREAKPOINT_BY_ID, // + GET_BREAKPOINT_PARAMETERS, // + ADD_BREAKPOINT, // + REMOVE_BREAKPOINT, // + ADD_EXTENSION, // + REMOVE_EXTENSION, // + GET_EXTENSION_BY_PATH, // + CALL_EXTENSION, // + GET_EXTENSION_FUNCTION, // + GET_WINDBG_EXTENSION_APIS32, // + GET_WINDBG_EXTENSION_APIS64, // + GET_NUMBER_EVENT_FILTERS, // + GET_EVENT_FILTER_TEXT, // + GET_EVENT_FILTER_COMMAND, // + SET_EVENT_FILTER_COMMAND, // + GET_SPECIFIC_FILTER_PARAMETERS, // + SET_SPECIFIC_FILTER_PARAMETERS, // + GET_SPECIFIC_FILTER_ARGUMENT, // + SET_SPECIFIC_FILTER_ARGUMENT, // + GET_EXCEPTION_FILTER_PARAMETERS, // + SET_EXCEPTION_FILTER_PARAMETERS, // + GET_EXCEPTION_FILTER_SECOND_COMMAND, // + SET_EXCEPTION_FILTER_SECOND_COMMAND, // + WAIT_FOR_EVENT, // + GET_LAST_EVENT_INFORMATION, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetInterrupt(); + + HRESULT SetInterrupt(ULONG Flags); + + HRESULT GetInterruptTimeout(ULONGByReference Seconds); + + HRESULT SetInterruptTimeout(ULONG Seconds); + + HRESULT ReturnInput(String Buffer); + + HRESULT Output(ULONG Mask, String Format, Object... objects); + + HRESULT OutputPrompt(ULONG OutputControl, String Format, Object... objects); + + HRESULT GetPromptText(byte[] Buffer, ULONG BufferSize, ULONGByReference TextSize); + + HRESULT GetExecutionStatus(ULONGByReference Status); + + HRESULT SetExecutionStatus(ULONG Status); + + HRESULT Evaluate(String Expression, ULONG DesiredType, DEBUG_VALUE.ByReference Value, + ULONGByReference RemainderIndex); + + HRESULT Execute(ULONG OutputControl, String Command, ULONG Flags); + + HRESULT GetNumberBreakpoints(ULONGByReference Number); + + HRESULT GetBreakpointByIndex(ULONG Index, PointerByReference Bp); + + HRESULT GetBreakpointById(ULONG Id, PointerByReference Bp); + + HRESULT AddBreakpoint(ULONG Type, ULONG DesiredId, PointerByReference Bp); + + HRESULT RemoveBreakpoint(IDebugBreakpoint Bp); + + HRESULT WaitForEvent(ULONG Flags, ULONG Timeout); + + HRESULT GetLastEventInformation(ULONGByReference pulType, ULONGByReference pulProcessId, + ULONGByReference pulThreadId, PointerByReference pExtraInformation, + ULONG ulExtraInformationSize, ULONGByReference pulExtraInformationUsed, + byte[] pstrDescription, ULONG ulDescriptionSize, ULONGByReference pulDescriptionUsed); + + HRESULT GetStackTrace(ULONGLONG FrameOffset, ULONGLONG StackOffset, ULONGLONG InstructionOffset, + DEBUG_STACK_FRAME[] pParams, ULONG FrameSize, ULONGByReference FramesFilled); + + HRESULT GetActualProcessorType(ULONGByReference Type); + + HRESULT GetEffectiveProcessorType(ULONGByReference Type); + + HRESULT GetExecutingProcessorType(ULONGByReference Type); + + HRESULT GetDebuggeeType(ULONGByReference Class, ULONGByReference Qualifier); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl2.java new file mode 100644 index 0000000000..e2efd771da --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl2.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.platform.win32.Guid.IID; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugControl2 extends IDebugControl { + final IID IID_IDEBUG_CONTROL2 = new IID("d4366723-44df-4bed-8c7e-4c05424f4588"); + + enum VTIndices2 implements VTableIndex { + GET_CURRENT_TIME_DATE, // + GET_CURRENT_SYSTEM_UP_TIME, // + GET_DUMP_FORMAT_FLAGS, // + GET_NUMBER_TEXT_REPLACEMENTS, // + GET_TEXT_REPLACEMENT, // + SET_TEXT_REPLACEMENT, // + REMOVE_TEXT_REPLACEMENTS, // + OUTPUT_TEXT_REPLACEMENTS, // + ; + + static int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl3.java new file mode 100644 index 0000000000..55ddd9fb55 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl3.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.platform.win32.Guid.IID; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugControl3 extends IDebugControl2 { + final IID IID_IDEBUG_CONTROL3 = new IID("7df74a86-b03f-407f-90ab-a20dadcead08"); + + enum VTIndices3 implements VTableIndex { + GET_ASSEMBLY_OPTIONS, // + ADD_ASSEMBLY_OPTIONS, // + REMOVE_ASSEMBLY_OPTIONS, // + SET_ASSEMBLY_OPTIONS, // + GET_EXPRESSION_SYNTAX, // + SET_EXPRESSION_SYNTAX, // + SET_EXPRESSION_SYNTAX_BY_NAME, // + GET_NUMBER_EXPRESSION_SYNTAXES, // + GET_EXPRESSION_SYNTAX_NAMES, // + GET_NUMBER_EVENTS, // + GET_EVENT_INDEX_DESCRIPTION, // + GET_CURRENT_EVENT_INDEX, // + SET_NEXT_EVENT_INDEX, // + ; + + static int start = VTableIndex.follow(VTIndices2.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl4.java new file mode 100644 index 0000000000..48e3b54e1a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl4.java @@ -0,0 +1,106 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugControl4 extends IDebugControl3 { + final IID IID_IDEBUG_CONTROL4 = new IID("94e60ce9-9b41-4b19-9fc0-6d9eb35272b3"); + + enum VTIndices4 implements VTableIndex { + GET_LOG_FILE_WIDE, // + OPEN_LOG_FILE_WIDE, // + INPUT_WIDE, // + RETURN_INPUT_WIDE, // + OUTPUT_WIDE, // + OUTPUT_VA_LIST_WIDE, // + CONTROLLED_OUTPUT_WIDE, // + CONTROLLED_OUTPUT_VA_LIST_WIDE, // + OUTPUT_PROMPT_WIDE, // + OUTPUT_PROMPT_VA_LIST_WIDE, // + GET_PROMPT_TEXT_WIDE, // + ASSEMBLE_WIDE, // + DISASSEMBLE_WIDE, // + GET_PROCESSOR_TYPE_NAMES_WIDE, // + GET_TEXT_MACRO_WIDE, // + SET_TEXT_MACRO_WIDE, // + EVALUATE_WIDE, // + EXECUTE_WIDE, // + EXECUTE_COMMAND_FILE_WIDE, // + GET_BREAKPOINT_BY_INDEX2, // + GET_BREAKPOINT_BY_ID2, // + ADD_BREAKPOINT2, // + REMOVE_BREAKPOINT2, // + ADD_EXTENSION_WIDE, // + GET_EXTENSION_BY_PATH_WIDE, // + CALL_EXTENSION_WIDE, // + GET_EXTENSION_FUNCTION_WIDE, // + GET_EVENT_FILTER_TEXT_WIDE, // + GET_EVENT_FILTER_COMMAND_WIDE, // + SET_EVENT_FILTER_COMMAND_WIDE, // + GET_SPECIFIC_FILTER_ARGUMENT_WIDE, // + SET_SPECIFIC_FILTER_ARGUMENT_WIDE, // + GET_EXCEPTION_FILTER_SECOND_COMMAND_WIDE, // + SET_EXCEPTION_FILTER_SECOND_COMMAND_WIDE, // + GET_LAST_EVENT_INFORMATINO_WIDE, // + GET_TEXT_REPLACEMENT_WIDE, // + SET_TEXT_REPLACEMENT_WIDE, // + SET_EXPRESSION_SYNTAX_BY_NAME_WIDE, // + GET_EXPRESSION_SYNTAX_NAMES_WIDE, // + GET_EVENT_INDEX_DESCRIPTION_WIDE, // + GET_LOG_FILE2, // + OPEN_LOG_FILE2, // + GET_LOG_FILE2_WIDE, // + OPEN_LOG_FILE2_WIDE, // + GET_SYSTEM_VERSION_VALUES, // + GET_SYSTEM_VERSION_STRING, // + GET_SYSTEM_VERSION_STRING_WIDE, // + GET_CONTEXT_STACK_TRACE, // + OUTPUT_CONTEXT_STACK_TRACE, // + GET_STORED_EVENT_INFORMATION, // + GET_MANAGED_STATUS, // + GET_MANAGED_STATUS_WIDE, // + RESET_MANAGED_STATUS, // + ; + + static int start = VTableIndex.follow(VTIndices3.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT ReturnInputWide(WString Buffer); + + HRESULT OutputWide(ULONG Mask, WString Format, Object... objects); + + HRESULT OutputPromptWide(ULONG OutputControl, WString Format, Object... objects); + + HRESULT GetPromptTextWide(char[] Buffer, ULONG BufferSize, ULONGByReference TextSize); + + HRESULT EvaluateWide(WString Expression, ULONG DesiredType, DEBUG_VALUE.ByReference Value, + ULONGByReference RemainderIndex); + + HRESULT ExecuteWide(ULONG OutputControl, WString Command, ULONG Flags); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl5.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl5.java new file mode 100644 index 0000000000..6f3e4b20f6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl5.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.platform.win32.Guid.IID; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugControl5 extends IDebugControl4 { + final IID IID_IDEBUG_CONTROL5 = new IID("b2ffe162-2412-429f-8d1d-5bf6dd824696"); + + enum VTIndices5 implements VTableIndex { + GET_STACK_TRACE_EX, // + OUTPUT_STACK_TRACE_EX, // + GET_CONTEXT_STACK_TRACE_EX, // + OUTPUT_CONTEXT_STACK_TRACE_EX, // + GET_BREAKPOINT_BY_GUID, // + ; + + static int start = VTableIndex.follow(VTIndices4.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl6.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl6.java new file mode 100644 index 0000000000..acd7c00753 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl6.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugControl6 extends IDebugControl5 { + final IID IID_IDEBUG_CONTROL6 = new IID("bc0d583f-126d-43a1-9cc4-a860ab1d537b"); + + enum VTIndices6 implements VTableIndex { + GET_EXECUTABLE_STATUS_EX, // + GET_SYNCHRONIZATION_STATUS, // + ; + + static int start = VTableIndex.follow(VTIndices5.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetExecutionStatusEx(ULONGByReference Status); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl7.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl7.java new file mode 100644 index 0000000000..100b984b87 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl7.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.platform.win32.Guid.IID; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugControl7 extends IDebugControl6 { + final IID IID_IDEBUG_CONTROL7 = new IID("b86fb3b1-80d4-475b-aea3-cf06539cf63a"); + + enum VTIndices7 implements VTableIndex { + GET_DEBUGGEE_TYPE2, // + ; + + static int start = VTableIndex.follow(VTIndices6.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl.java new file mode 100644 index 0000000000..6409d93929 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl.java @@ -0,0 +1,181 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import java.util.*; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_STACK_FRAME; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.UnknownWithUtils; +import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint; + +public class WrapIDebugControl extends UnknownWithUtils implements IDebugControl { + public static class ByReference extends WrapIDebugControl implements Structure.ByReference { + } + + public WrapIDebugControl() { + } + + public WrapIDebugControl(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetInterrupt() { + return _invokeHR(VTIndices.GET_INTERRUPT, getPointer()); + } + + @Override + public HRESULT SetInterrupt(ULONG Flags) { + return _invokeHR(VTIndices.SET_INTERRUPT, getPointer(), Flags); + } + + @Override + public HRESULT GetInterruptTimeout(ULONGByReference Seconds) { + return _invokeHR(VTIndices.GET_INTERRUPT_TIMEOUT, getPointer(), Seconds); + } + + @Override + public HRESULT SetInterruptTimeout(ULONG Seconds) { + return _invokeHR(VTIndices.SET_INTERRUPT_TIMEOUT, getPointer(), Seconds); + } + + @Override + public HRESULT ReturnInput(String Buffer) { + return _invokeHR(VTIndices.RETURN_INPUT, getPointer(), Buffer); + } + + @Override + public HRESULT Output(ULONG Mask, String Format, Object... objects) { + List args = new ArrayList<>(); + args.add(getPointer()); + args.add(Format); + args.addAll(Arrays.asList(objects)); + return _invokeHR(VTIndices.OUTPUT, args.toArray()); + } + + @Override + public HRESULT OutputPrompt(ULONG OutputControl, String Format, Object... objects) { + List args = new ArrayList<>(); + args.add(getPointer()); + args.add(OutputControl); + args.add(Format); + args.addAll(Arrays.asList(objects)); + return _invokeHR(VTIndices.OUTPUT_PROMPT, args.toArray()); + } + + @Override + public HRESULT GetPromptText(byte[] Buffer, ULONG BufferSize, ULONGByReference TextSize) { + return _invokeHR(VTIndices.GET_PROMPT_TEXT, getPointer(), Buffer, BufferSize, TextSize); + } + + @Override + public HRESULT GetExecutionStatus(ULONGByReference Status) { + return _invokeHR(VTIndices.GET_EXECUTION_STATUS, getPointer(), Status); + } + + @Override + public HRESULT SetExecutionStatus(ULONG Status) { + return _invokeHR(VTIndices.SET_EXECUTION_STATUS, getPointer(), Status); + } + + @Override + public HRESULT Evaluate(String Expression, ULONG DesiredType, DEBUG_VALUE.ByReference Value, + ULONGByReference RemainderIndex) { + return _invokeHR(VTIndices.EVALUATE, getPointer(), Expression, DesiredType, Value, + RemainderIndex); + } + + @Override + public HRESULT Execute(ULONG OutputControl, String Command, ULONG Flags) { + return _invokeHR(VTIndices.EXECUTE, getPointer(), OutputControl, Command, Flags); + } + + @Override + public HRESULT GetNumberBreakpoints(ULONGByReference Number) { + return _invokeHR(VTIndices.GET_NUMBER_BREAKPOINTS, getPointer(), Number); + } + + @Override + public HRESULT GetBreakpointByIndex(ULONG Index, PointerByReference Bp) { + return _invokeHR(VTIndices.GET_BREAKPOINT_BY_INDEX, getPointer(), Index, Bp); + } + + @Override + public HRESULT GetBreakpointById(ULONG Id, PointerByReference Bp) { + return _invokeHR(VTIndices.GET_BREAKPOINT_BY_ID, getPointer(), Id, Bp); + } + + @Override + public HRESULT AddBreakpoint(ULONG Type, ULONG DesiredId, PointerByReference Bp) { + return _invokeHR(VTIndices.ADD_BREAKPOINT, getPointer(), Type, DesiredId, Bp); + } + + @Override + public HRESULT RemoveBreakpoint(IDebugBreakpoint Bp) { + return _invokeHR(VTIndices.REMOVE_BREAKPOINT, getPointer(), Bp); + } + + @Override + public HRESULT WaitForEvent(ULONG Flags, ULONG Timeout) { + return _invokeHR(VTIndices.WAIT_FOR_EVENT, getPointer(), Flags, Timeout); + } + + @Override + public HRESULT GetLastEventInformation(ULONGByReference Type, ULONGByReference ProcessId, + ULONGByReference ThreadId, PointerByReference ExtraInformation, + ULONG ExtraInformationSize, ULONGByReference ExtraInformationUsed, byte[] Description, + ULONG DescriptionSize, ULONGByReference DescriptionUsed) { + return _invokeHR(VTIndices.GET_LAST_EVENT_INFORMATION, getPointer(), Type, ProcessId, + ThreadId, ExtraInformation, ExtraInformationSize, ExtraInformationUsed, Description, + DescriptionSize, DescriptionUsed); + } + + @Override + public HRESULT GetStackTrace(ULONGLONG FrameOffset, ULONGLONG StackOffset, + ULONGLONG InstructionOffset, DEBUG_STACK_FRAME[] Params, ULONG FrameSize, + ULONGByReference FramesFilled) { + return _invokeHR(VTIndices.GET_STACK_TRACE, getPointer(), FrameOffset, StackOffset, + InstructionOffset, Params, FrameSize, FramesFilled); + } + + @Override + public HRESULT GetActualProcessorType(ULONGByReference Type) { + return _invokeHR(VTIndices.GET_ACTUAL_PROCESSOR_TYPE, getPointer(), Type); + } + + @Override + public HRESULT GetEffectiveProcessorType(ULONGByReference Type) { + return _invokeHR(VTIndices.GET_EFFECTIVE_PROCESSOR_TYPE, getPointer(), Type); + } + + @Override + public HRESULT GetExecutingProcessorType(ULONGByReference Type) { + return _invokeHR(VTIndices.GET_EXECUTING_PROCESSOR_TYPE, getPointer(), Type); + } + + @Override + public HRESULT GetDebuggeeType(ULONGByReference Class, ULONGByReference Qualifier) { + return _invokeHR(VTIndices.GET_DEBUGGEE_TYPE, getPointer(), Class, Qualifier); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl2.java new file mode 100644 index 0000000000..d622597009 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl2.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public class WrapIDebugControl2 extends WrapIDebugControl implements IDebugControl2 { + public static class ByReference extends WrapIDebugControl2 implements Structure.ByReference { + } + + public WrapIDebugControl2() { + } + + public WrapIDebugControl2(Pointer pvInstance) { + super(pvInstance); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl3.java new file mode 100644 index 0000000000..04a84e11e5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl3.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public class WrapIDebugControl3 extends WrapIDebugControl2 implements IDebugControl3 { + public static class ByReference extends WrapIDebugControl3 implements Structure.ByReference { + } + + public WrapIDebugControl3() { + } + + public WrapIDebugControl3(Pointer pvInstance) { + super(pvInstance); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl4.java new file mode 100644 index 0000000000..7446371a3b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl4.java @@ -0,0 +1,79 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import java.util.*; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; + +public class WrapIDebugControl4 extends WrapIDebugControl3 implements IDebugControl4 { + public static class ByReference extends WrapIDebugControl4 implements Structure.ByReference { + } + + public WrapIDebugControl4() { + } + + public WrapIDebugControl4(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT ReturnInputWide(WString Buffer) { + return _invokeHR(VTIndices4.RETURN_INPUT_WIDE, getPointer(), Buffer); + } + + @Override + public HRESULT OutputWide(ULONG Mask, WString Format, Object... objects) { + List args = new ArrayList<>(); + args.add(getPointer()); + args.add(Format); + args.addAll(Arrays.asList(objects)); + return _invokeHR(VTIndices4.OUTPUT_WIDE, args.toArray()); + } + + @Override + public HRESULT OutputPromptWide(ULONG OutputControl, WString Format, Object... objects) { + List args = new ArrayList<>(); + args.add(getPointer()); + args.add(OutputControl); + args.add(Format); + args.addAll(Arrays.asList(objects)); + return _invokeHR(VTIndices4.OUTPUT_PROMPT_WIDE, args.toArray()); + } + + @Override + public HRESULT GetPromptTextWide(char[] Buffer, ULONG BufferSize, ULONGByReference TextSize) { + return _invokeHR(VTIndices4.GET_PROMPT_TEXT_WIDE, getPointer(), Buffer, BufferSize, + TextSize); + } + + @Override + public HRESULT EvaluateWide(WString Expression, ULONG DesiredType, + DEBUG_VALUE.ByReference Value, ULONGByReference RemainderIndex) { + return _invokeHR(VTIndices4.EVALUATE_WIDE, getPointer(), Expression, DesiredType, Value, + RemainderIndex); + } + + @Override + public HRESULT ExecuteWide(ULONG OutputControl, WString Command, ULONG Flags) { + return _invokeHR(VTIndices4.EXECUTE_WIDE, getPointer(), OutputControl, Command, Flags); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl5.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl5.java new file mode 100644 index 0000000000..b2ae0cb2bc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl5.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public class WrapIDebugControl5 extends WrapIDebugControl4 implements IDebugControl5 { + public static class ByReference extends WrapIDebugControl5 implements Structure.ByReference { + } + + public WrapIDebugControl5() { + } + + public WrapIDebugControl5(Pointer pvInstance) { + super(pvInstance); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl6.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl6.java new file mode 100644 index 0000000000..67c3f64484 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl6.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class WrapIDebugControl6 extends WrapIDebugControl5 implements IDebugControl6 { + public static class ByReference extends WrapIDebugControl6 implements Structure.ByReference { + } + + public WrapIDebugControl6() { + } + + public WrapIDebugControl6(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetExecutionStatusEx(ULONGByReference Status) { + return _invokeHR(VTIndices6.GET_EXECUTABLE_STATUS_EX, getPointer(), Status); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl7.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl7.java new file mode 100644 index 0000000000..52ef3db0b4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl7.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.control; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public class WrapIDebugControl7 extends WrapIDebugControl6 implements IDebugControl7 { + public static class ByReference extends WrapIDebugControl7 implements Structure.ByReference { + } + + public WrapIDebugControl7() { + } + + public WrapIDebugControl7(Pointer pvInstance) { + super(pvInstance); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces.java new file mode 100644 index 0000000000..39432d0574 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces.java @@ -0,0 +1,104 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.dataspaces; + +import java.nio.ByteBuffer; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.IUnknown; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugDataSpaces extends IUnknown { + final IID IID_IDEBUG_DATA_SPACES = new IID("88f7dfab-3ea7-4c3a-aefb-c4e8106173aa"); + + enum VTIndices implements VTableIndex { + READ_VIRTUAL, // + WRITE_VIRTUAL, // + SEARCH_VIRTUAL, // + READ_VIRTUAL_UNCACHED, // + WRITE_VIRTUAL_UNCACHED, // + READ_POINTERS_VIRTUAL, // + WRITE_POINTERS_VIRTUAL, // + READ_PHYSICAL, // + WRITE_PHYSICAL, // + READ_CONTROL, // + WRITE_CONTROL, // + READ_IO, // + WRITE_IO, // + READ_MSR, // + WRITE_MSR, // + READ_BUS_DATA, // + WRITE_BUS_DATA, // + CHECK_LOW_MEMORY, // + READ_DEBUGGER_DATA, // + READ_PROCESSOR_SYSTEM_DATA, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT ReadVirtual(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesRead); + + HRESULT WriteVirtual(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesWritten); + + HRESULT ReadVirtualUncached(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesRead); + + HRESULT WriteVirtualUncached(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesWritten); + + HRESULT ReadPhysical(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesRead); + + HRESULT WritePhysical(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesWritten); + + HRESULT ReadControl(ULONG Processor, ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesRead); + + HRESULT WriteControl(ULONG Processor, ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesWritten); + + HRESULT ReadBusData(ULONG BusDataType, ULONG BusNumber, ULONG SlotNumber, ULONGLONG Offset, + ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesRead); + + HRESULT WriteBusData(ULONG BusDataType, ULONG BusNumber, ULONG SlotNumber, ULONGLONG Offset, + ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesWritten); + + HRESULT ReadIo(ULONG InterfaceType, ULONG BusNumber, ULONG AddressSpace, ULONGLONG Offset, + ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesRead); + + HRESULT WriteIo(ULONG InterfaceType, ULONG BusNumber, ULONG AddressSpace, ULONGLONG Offset, + ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesWritten); + + HRESULT ReadMsr(ULONG Msr, ULONGLONGByReference Value); + + HRESULT WriteMsr(ULONG Msr, ULONGLONG Value); + + HRESULT ReadDebuggerData(ULONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesRead); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces2.java new file mode 100644 index 0000000000..2931c94b0b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces2.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.dataspaces; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; +import agent.dbgeng.jna.dbgeng.WinNTExtra.MEMORY_BASIC_INFORMATION64; + +public interface IDebugDataSpaces2 extends IDebugDataSpaces { + final IID IID_IDEBUG_DATA_SPACES2 = new IID("7a5e852f-96e9-468f-ac1b-0b3addc4a049"); + + enum VTIndices2 implements VTableIndex { + VIRTUAL_TO_PHYSICAL, // + GET_VIRTUAL_TRANSLATION_PHYSICAL_OFFSETS, // + READ_HANDLE_DATA, // + FILL_VIRTUAL, // + FILL_PHYSICAL, // + QUERY_VIRTUAL, // + ; + + static int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT QueryVirtual(ULONGLONG Offset, MEMORY_BASIC_INFORMATION64.ByReference Info); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces3.java new file mode 100644 index 0000000000..cc0b1d84a3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces3.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.dataspaces; + +import com.sun.jna.platform.win32.Guid.IID; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugDataSpaces3 extends IDebugDataSpaces2 { + final IID IID_IDEBUG_DATA_SPACES3 = new IID("23f79d6c-8aaf-4f7c-a607-9995f5407e63"); + + enum VTIndices3 implements VTableIndex { + READ_IMAGE_NT_HEADERS, // + READ_TAGGED, // + START_ENUM_TAGGED, // + GET_NEXT_TAGGED, // + END_ENUM_TAGGED, // + ; + + static int start = VTableIndex.follow(VTIndices2.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces4.java new file mode 100644 index 0000000000..92e55c2428 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/IDebugDataSpaces4.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.dataspaces; + +import com.sun.jna.platform.win32.Guid.IID; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugDataSpaces4 extends IDebugDataSpaces3 { + final IID IID_IDEBUG_DATA_SPACES4 = new IID("d98ada1f-29e9-4ef5-a6c0-e53349883212"); + + enum VTIndices4 implements VTableIndex { + GET_OFFSET_INFORMATION, // + GET_NEXT_DIFFERENTLY_VALID_OFFSET_VIRTUAL, // + GET_VALID_REGION_VIRTUAL, // + SEARCH_VIRTUAL2, // + READ_MULTI_BYTE_STRING_VIRTUAL, // + READ_MULTI_BYTE_STRING_VIRTUAL_WIDE, // + READ_UNICODE_STRING_VIRTUAL, // + READ_UNICODE_STRING_VIRTUAL_WIDE, // + READ_PHYSICAL2, // + WRITE_PHYSICAL2, // + ; + + static int start = VTableIndex.follow(VTIndices3.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces.java new file mode 100644 index 0000000000..015755d22a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces.java @@ -0,0 +1,141 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.dataspaces; + +import java.nio.ByteBuffer; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils; +import agent.dbgeng.jna.dbgeng.sysobj.WrapIDebugSystemObjects; + +public class WrapIDebugDataSpaces extends UnknownWithUtils implements IDebugDataSpaces { + public static class ByReference extends WrapIDebugSystemObjects + implements Structure.ByReference { + } + + public WrapIDebugDataSpaces() { + } + + public WrapIDebugDataSpaces(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT ReadVirtual(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesRead) { + return _invokeHR(VTIndices.READ_VIRTUAL, getPointer(), Offset, Buffer, BufferSize, + BytesRead); + } + + @Override + public HRESULT WriteVirtual(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesWritten) { + return _invokeHR(VTIndices.WRITE_VIRTUAL, getPointer(), Offset, Buffer, BufferSize, + BytesWritten); + } + + @Override + public HRESULT ReadVirtualUncached(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesRead) { + return _invokeHR(VTIndices.READ_VIRTUAL_UNCACHED, getPointer(), Offset, Buffer, BufferSize, + BytesRead); + } + + @Override + public HRESULT WriteVirtualUncached(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesWritten) { + return _invokeHR(VTIndices.WRITE_VIRTUAL_UNCACHED, getPointer(), Offset, Buffer, BufferSize, + BytesWritten); + } + + @Override + public HRESULT ReadPhysical(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesRead) { + return _invokeHR(VTIndices.READ_PHYSICAL, getPointer(), Offset, Buffer, BufferSize, + BytesRead); + } + + @Override + public HRESULT WritePhysical(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesWritten) { + return _invokeHR(VTIndices.WRITE_PHYSICAL, getPointer(), Offset, Buffer, BufferSize, + BytesWritten); + } + + @Override + public HRESULT ReadControl(ULONG Processor, ULONGLONG Offset, ByteBuffer Buffer, + ULONG BufferSize, ULONGByReference BytesRead) { + return _invokeHR(VTIndices.READ_CONTROL, getPointer(), Processor, Processor, Offset, Buffer, + BufferSize, BytesRead); + } + + @Override + public HRESULT WriteControl(ULONG Processor, ULONGLONG Offset, ByteBuffer Buffer, + ULONG BufferSize, ULONGByReference BytesWritten) { + return _invokeHR(VTIndices.WRITE_CONTROL, getPointer(), Offset, Buffer, BufferSize, + BytesWritten); + } + + @Override + public HRESULT ReadBusData(ULONG BusDataType, ULONG BusNumber, ULONG SlotNumber, + ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesRead) { + return _invokeHR(VTIndices.READ_BUS_DATA, getPointer(), BusDataType, BusNumber, SlotNumber, + Offset, Buffer, BufferSize, BytesRead); + } + + @Override + public HRESULT WriteBusData(ULONG BusDataType, ULONG BusNumber, ULONG SlotNumber, + ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesWritten) { + return _invokeHR(VTIndices.WRITE_BUS_DATA, getPointer(), BusDataType, BusNumber, SlotNumber, + Offset, Buffer, BufferSize, BytesWritten); + } + + @Override + public HRESULT ReadIo(ULONG InterfaceType, ULONG BusNumber, ULONG AddressSpace, + ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesRead) { + return _invokeHR(VTIndices.READ_IO, getPointer(), InterfaceType, BusNumber, AddressSpace, + Offset, Buffer, BufferSize, BytesRead); + } + + @Override + public HRESULT WriteIo(ULONG InterfaceType, ULONG BusNumber, ULONG AddressSpace, + ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesWritten) { + return _invokeHR(VTIndices.WRITE_IO, getPointer(), InterfaceType, BusNumber, AddressSpace, + Offset, Buffer, BufferSize, BytesWritten); + } + + @Override + public HRESULT ReadMsr(ULONG Msr, ULONGLONGByReference Value) { + return _invokeHR(VTIndices.READ_MSR, getPointer(), Msr, Value); + } + + @Override + public HRESULT WriteMsr(ULONG Msr, ULONGLONG Value) { + return _invokeHR(VTIndices.WRITE_MSR, getPointer(), Msr, Value); + } + + @Override + public HRESULT ReadDebuggerData(ULONG Offset, ByteBuffer Buffer, ULONG BufferSize, + ULONGByReference BytesRead) { + return _invokeHR(VTIndices.READ_DEBUGGER_DATA, getPointer(), Offset, Buffer, BufferSize, + BytesRead); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces2.java new file mode 100644 index 0000000000..ff37757742 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces2.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.dataspaces; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.MEMORY_BASIC_INFORMATION64; +import agent.dbgeng.jna.dbgeng.sysobj.WrapIDebugSystemObjects2; + +public class WrapIDebugDataSpaces2 extends WrapIDebugDataSpaces implements IDebugDataSpaces2 { + public static class ByReference extends WrapIDebugSystemObjects2 + implements Structure.ByReference { + } + + public WrapIDebugDataSpaces2() { + } + + public WrapIDebugDataSpaces2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT QueryVirtual(ULONGLONG Offset, MEMORY_BASIC_INFORMATION64.ByReference Info) { + return _invokeHR(VTIndices2.QUERY_VIRTUAL, getPointer(), Offset, Info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces3.java new file mode 100644 index 0000000000..0b643ee8d6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces3.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.dataspaces; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +import agent.dbgeng.jna.dbgeng.sysobj.WrapIDebugSystemObjects3; + +public class WrapIDebugDataSpaces3 extends WrapIDebugDataSpaces2 implements IDebugDataSpaces3 { + public static class ByReference extends WrapIDebugSystemObjects3 + implements Structure.ByReference { + } + + public WrapIDebugDataSpaces3() { + } + + public WrapIDebugDataSpaces3(Pointer pvInstance) { + super(pvInstance); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces4.java new file mode 100644 index 0000000000..b188307a40 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/dataspaces/WrapIDebugDataSpaces4.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.dataspaces; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +import agent.dbgeng.jna.dbgeng.sysobj.WrapIDebugSystemObjects4; + +public class WrapIDebugDataSpaces4 extends WrapIDebugDataSpaces3 implements IDebugDataSpaces4 { + public static class ByReference extends WrapIDebugSystemObjects4 + implements Structure.ByReference { + } + + public WrapIDebugDataSpaces4() { + } + + public WrapIDebugDataSpaces4(Pointer pvInstance) { + super(pvInstance); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventCallbacks.java new file mode 100644 index 0000000000..fe9adc2bb5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventCallbacks.java @@ -0,0 +1,21 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import com.sun.jna.platform.win32.COM.IUnknownCallback; + +public interface CallbackIDebugEventCallbacks extends IDebugEventCallbacks, IUnknownCallback { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventCallbacksWide.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventCallbacksWide.java new file mode 100644 index 0000000000..8db971286e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventCallbacksWide.java @@ -0,0 +1,22 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import com.sun.jna.platform.win32.COM.IUnknownCallback; + +public interface CallbackIDebugEventCallbacksWide + extends IDebugEventCallbacksWide, IUnknownCallback { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventContextCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventContextCallbacks.java new file mode 100644 index 0000000000..d85a5c9316 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/CallbackIDebugEventContextCallbacks.java @@ -0,0 +1,22 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import com.sun.jna.platform.win32.COM.IUnknownCallback; + +public interface CallbackIDebugEventContextCallbacks + extends IDebugEventContextCallbacks, IUnknownCallback { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventCallbacks.java new file mode 100644 index 0000000000..65302dbe7f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventCallbacks.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint; + +public interface IDebugEventCallbacks { + final IID IID_IDEBUG_EVENT_CALLBACKS = new IID("337be28b-5036-4d72-b6bf-c45fbb9f2eaa"); + + HRESULT GetInterestMask(ULONGByReference Mask); + + HRESULT Breakpoint(WrapIDebugBreakpoint.ByReference Bp); + + HRESULT Exception(EXCEPTION_RECORD64.ByReference Exception, ULONG FirstChance); + + HRESULT CreateThread(ULONGLONG Handle, ULONGLONG DataOffset, ULONGLONG StartOffset); + + HRESULT ExitThread(ULONG ExitCode); + + HRESULT CreateProcess(ULONGLONG ImageFileHandle, ULONGLONG Handle, ULONGLONG BaseOffset, + ULONG ModuleSize, String ModuleName, String ImageName, ULONG CheckSum, + ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, ULONGLONG ThreadDataOffset, + ULONGLONG StartOffset); + + HRESULT ExitProcess(ULONG ExitCode); + + HRESULT LoadModule(ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, ULONG ModuleSize, + String ModuleName, String ImageName, ULONG CheckSum, ULONG TimeDateStamp); + + HRESULT UnloadModule(String ImageBaseName, ULONGLONG BaseOffset); + + HRESULT SystemError(ULONG Error, ULONG Level); + + HRESULT SessionStatus(ULONG Status); + + HRESULT ChangeDebuggeeState(ULONG Flags, ULONGLONG Argument); + + HRESULT ChangeEngineState(ULONG Flags, ULONGLONG Argument); + + HRESULT ChangeSymbolState(ULONG Flags, ULONGLONG Argument); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventCallbacksWide.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventCallbacksWide.java new file mode 100644 index 0000000000..09d274eb88 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventCallbacksWide.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint; + +public interface IDebugEventCallbacksWide { + final IID IID_IDEBUG_EVENT_CALLBACKS_WIDE = new IID("0690e046-9c23-45ac-a04f-987ac29ad0d3"); + + HRESULT GetInterestMask(ULONGByReference Mask); + + HRESULT Breakpoint(WrapIDebugBreakpoint.ByReference Bp); + + HRESULT Exception(EXCEPTION_RECORD64.ByReference Exception, ULONG FirstChance); + + HRESULT CreateThread(ULONGLONG Handle, ULONGLONG DataOffset, ULONGLONG StartOffset); + + HRESULT ExitThread(ULONG ExitCode); + + HRESULT CreateProcess(ULONGLONG ImageFileHandle, ULONGLONG Handle, ULONGLONG BaseOffset, + ULONG ModuleSize, WString ModuleName, WString ImageName, ULONG CheckSum, + ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, ULONGLONG ThreadDataOffset, + ULONGLONG StartOffset); + + HRESULT ExitProcess(ULONG ExitCode); + + HRESULT LoadModule(ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, ULONG ModuleSize, + WString ModuleName, WString ImageName, ULONG CheckSum, ULONG TimeDateStamp); + + HRESULT UnloadModule(WString ImageBaseName, ULONGLONG BaseOffset); + + HRESULT SystemError(ULONG Error, ULONG Level); + + HRESULT SessionStatus(ULONG Status); + + HRESULT ChangeDebuggeeState(ULONG Flags, ULONGLONG Argument); + + HRESULT ChangeEngineState(ULONG Flags, ULONGLONG Argument); + + HRESULT ChangeSymbolState(ULONG Flags, ULONGLONG Argument); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventContextCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventContextCallbacks.java new file mode 100644 index 0000000000..b75a457da2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/IDebugEventContextCallbacks.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint2; + +public interface IDebugEventContextCallbacks { + final IID IID_IDEBUG_EVENT_CONTEXT_CALLBACKS = new IID("61a4905b-23f9-4247-b3c5-53d087529ab7"); + + HRESULT GetInterestMask(ULONGByReference Mask); + + HRESULT Breakpoint(WrapIDebugBreakpoint2.ByReference Bp, Pointer Context, ULONG ContextSize); + + HRESULT Exception(EXCEPTION_RECORD64.ByReference Exception, ULONG FirstChance, Pointer Context, + ULONG ContextSize); + + HRESULT CreateThread(ULONGLONG Handle, ULONGLONG DataOffset, ULONGLONG StartOffset, + Pointer Context, ULONG ContextSize); + + HRESULT ExitThread(ULONG ExitCode, Pointer Context, ULONG ContextSize); + + HRESULT CreateProcess(ULONGLONG ImageFileHandle, ULONGLONG Handle, ULONGLONG BaseOffset, + ULONG ModuleSize, WString ModuleName, WString ImageName, ULONG CheckSum, + ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, ULONGLONG ThreadDataOffset, + ULONGLONG StartOffset, Pointer Context, ULONG ContextSize); + + HRESULT ExitProcess(ULONG ExitCode, Pointer Context, ULONG ContextSize); + + HRESULT LoadModule(ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, ULONG ModuleSize, + WString ModuleName, WString ImageName, ULONG CheckSum, ULONG TimeDateStamp, + Pointer Context, ULONG ContextSize); + + HRESULT UnloadModule(WString ImageBaseName, ULONGLONG BaseOffset, Pointer Context, + ULONG ContextSize); + + HRESULT SystemError(ULONG Error, ULONG Level, Pointer Context, ULONG ContextSize); + + HRESULT SessionStatus(ULONG Status); + + HRESULT ChangeDebuggeeState(ULONG Flags, ULONGLONG Argument, Pointer Context, + ULONG ContextSize); + + HRESULT ChangeEngineState(ULONG Flags, ULONGLONG Argument, Pointer Context, ULONG ContextSize); + + HRESULT ChangeSymbolState(ULONG Flags, ULONGLONG Argument); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventCallbacks.java new file mode 100644 index 0000000000..e7b656fbc5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventCallbacks.java @@ -0,0 +1,184 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import java.util.List; + +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint; + +public class ListenerIDebugEventCallbacks extends Structure + implements IDebugEventCallbacks, MarkerEventCallbacks { + public static final List FIELDS = createFieldsOrder("vtbl"); + + public ListenerIDebugEventCallbacks(CallbackIDebugEventCallbacks callback) { + this.vtbl = this.constructVTable(); + this.initVTable(callback); + super.write(); + } + + public VTableIDebugEventCallbacks.ByReference vtbl; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + protected VTableIDebugEventCallbacks.ByReference constructVTable() { + return new VTableIDebugEventCallbacks.ByReference(); + } + + protected void initVTable(final CallbackIDebugEventCallbacks callback) { + vtbl.QueryInterfaceCallback = (thisPointer, refid, ppvObject) -> { + return callback.QueryInterface(refid, ppvObject); + }; + vtbl.AddRefCallback = (thisPointer) -> { + return callback.AddRef(); + }; + vtbl.ReleaseCallback = (thisPointer) -> { + return callback.Release(); + }; + vtbl.GetInterestMaskCallback = (thisPointer, Mask) -> { + return callback.GetInterestMask(Mask); + }; + vtbl.BreakpointCallback = (thisPointer, Bp) -> { + return callback.Breakpoint(Bp); + }; + vtbl.ExceptionCallback = (thisPointer, Exception, FirstChance) -> { + return callback.Exception(Exception, FirstChance); + }; + vtbl.CreateThreadCallback = (thisPointer, Handle, DataOffset, StartOffset) -> { + return callback.CreateThread(Handle, DataOffset, StartOffset); + }; + vtbl.ExitThreadCallback = (thisPointer, ExitCode) -> { + return callback.ExitThread(ExitCode); + }; + vtbl.CreateProcessCallback = (thisPointer, ImageFileHandle, Handle, BaseOffset, ModuleSize, + ModuleName, ImageName, CheckSum, TimeDateStamp, InitialThreadHandle, + ThreadDataOffset, StartOffset) -> { + return callback.CreateProcess(ImageFileHandle, Handle, BaseOffset, ModuleSize, + ModuleName, ImageName, CheckSum, TimeDateStamp, InitialThreadHandle, + ThreadDataOffset, StartOffset); + }; + vtbl.ExitProcessCallback = (thisPointer, ExitCode) -> { + return callback.ExitProcess(ExitCode); + }; + vtbl.LoadModuleCallback = (thisPointer, ImageFileHandle, BaseOffset, ModuleSize, ModuleName, + ImageName, CheckSum, TimeDateStamp) -> { + return callback.LoadModule(ImageFileHandle, BaseOffset, ModuleSize, ModuleName, + ImageName, CheckSum, TimeDateStamp); + }; + vtbl.UnloadModuleCallback = (thisPointer, ImageBaseName, BaseOffset) -> { + return callback.UnloadModule(ImageBaseName, BaseOffset); + }; + vtbl.SystemErrorCallback = (thisPointer, Error, Level) -> { + return callback.SystemError(Error, Level); + }; + vtbl.SessionStatusCallback = (thisPointer, Status) -> { + return callback.SessionStatus(Status); + }; + vtbl.ChangeDebuggeeStateCallback = (thisPointer, Flags, Argument) -> { + return callback.ChangeDebuggeeState(Flags, Argument); + }; + vtbl.ChangeEngineStateCallback = (thisPointer, Flags, Argument) -> { + return callback.ChangeEngineState(Flags, Argument); + }; + vtbl.ChangeSymbolStateCallback = (thisPointer, Flags, Argument) -> { + return callback.ChangeSymbolState(Flags, Argument); + }; + } + + @Override + public HRESULT GetInterestMask(ULONGByReference Mask) { + return vtbl.GetInterestMaskCallback.invoke(getPointer(), Mask); + } + + @Override + public HRESULT Breakpoint(WrapIDebugBreakpoint.ByReference Bp) { + return vtbl.BreakpointCallback.invoke(getPointer(), Bp); + } + + @Override + public HRESULT Exception(EXCEPTION_RECORD64.ByReference Exception, ULONG FirstChance) { + return vtbl.ExceptionCallback.invoke(getPointer(), Exception, FirstChance); + } + + @Override + public HRESULT CreateThread(ULONGLONG Handle, ULONGLONG DataOffset, ULONGLONG StartOffset) { + return vtbl.CreateThreadCallback.invoke(getPointer(), Handle, DataOffset, StartOffset); + } + + @Override + public HRESULT ExitThread(ULONG ExitCode) { + return vtbl.ExitThreadCallback.invoke(getPointer(), ExitCode); + } + + @Override + public HRESULT CreateProcess(ULONGLONG ImageFileHandle, ULONGLONG Handle, ULONGLONG BaseOffset, + ULONG ModuleSize, String ModuleName, String ImageName, ULONG CheckSum, + ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, ULONGLONG ThreadDataOffset, + ULONGLONG StartOffset) { + return vtbl.CreateProcessCallback.invoke(getPointer(), ImageFileHandle, Handle, BaseOffset, + ModuleSize, ModuleName, ImageName, CheckSum, TimeDateStamp, InitialThreadHandle, + ThreadDataOffset, StartOffset); + } + + @Override + public HRESULT ExitProcess(ULONG ExitCode) { + return vtbl.ExitProcessCallback.invoke(getPointer(), ExitCode); + } + + @Override + public HRESULT LoadModule(ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, ULONG ModuleSize, + String ModuleName, String ImageName, ULONG CheckSum, ULONG TimeDateStamp) { + return vtbl.LoadModuleCallback.invoke(getPointer(), ImageFileHandle, BaseOffset, ModuleSize, + ModuleName, ImageName, CheckSum, TimeDateStamp); + } + + @Override + public HRESULT UnloadModule(String ImageBaseName, ULONGLONG BaseOffset) { + return vtbl.UnloadModuleCallback.invoke(getPointer(), ImageBaseName, BaseOffset); + } + + @Override + public HRESULT SystemError(ULONG Error, ULONG Level) { + return vtbl.SystemErrorCallback.invoke(getPointer(), Error, Level); + } + + @Override + public HRESULT SessionStatus(ULONG Status) { + return vtbl.SessionStatusCallback.invoke(getPointer(), Status); + } + + @Override + public HRESULT ChangeDebuggeeState(ULONG Flags, ULONGLONG Argument) { + return vtbl.ChangeDebuggeeStateCallback.invoke(getPointer(), Flags, Argument); + } + + @Override + public HRESULT ChangeEngineState(ULONG Flags, ULONGLONG Argument) { + return vtbl.ChangeEngineStateCallback.invoke(getPointer(), Flags, Argument); + } + + @Override + public HRESULT ChangeSymbolState(ULONG Flags, ULONGLONG Argument) { + return vtbl.ChangeSymbolStateCallback.invoke(getPointer(), Flags, Argument); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventCallbacksWide.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventCallbacksWide.java new file mode 100644 index 0000000000..c693094386 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventCallbacksWide.java @@ -0,0 +1,185 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import java.util.List; + +import com.sun.jna.Structure; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint; + +public class ListenerIDebugEventCallbacksWide extends Structure + implements IDebugEventCallbacksWide, MarkerEventCallbacks { + public static final List FIELDS = createFieldsOrder("vtbl"); + + public ListenerIDebugEventCallbacksWide(CallbackIDebugEventCallbacksWide callback) { + this.vtbl = this.constructVTable(); + this.initVTable(callback); + super.write(); + } + + public VTableIDebugEventCallbacksWide.ByReference vtbl; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + protected VTableIDebugEventCallbacksWide.ByReference constructVTable() { + return new VTableIDebugEventCallbacksWide.ByReference(); + } + + protected void initVTable(final CallbackIDebugEventCallbacksWide callback) { + vtbl.QueryInterfaceCallback = (thisPointer, refid, ppvObject) -> { + return callback.QueryInterface(refid, ppvObject); + }; + vtbl.AddRefCallback = (thisPointer) -> { + return callback.AddRef(); + }; + vtbl.ReleaseCallback = (thisPointer) -> { + return callback.Release(); + }; + vtbl.GetInterestMaskCallback = (thisPointer, Mask) -> { + return callback.GetInterestMask(Mask); + }; + vtbl.BreakpointCallback = (thisPointer, Bp) -> { + return callback.Breakpoint(Bp); + }; + vtbl.ExceptionCallback = (thisPointer, Exception, FirstChance) -> { + return callback.Exception(Exception, FirstChance); + }; + vtbl.CreateThreadCallback = (thisPointer, Handle, DataOffset, StartOffset) -> { + return callback.CreateThread(Handle, DataOffset, StartOffset); + }; + vtbl.ExitThreadCallback = (thisPointer, ExitCode) -> { + return callback.ExitThread(ExitCode); + }; + vtbl.CreateProcessCallback = (thisPointer, ImageFileHandle, Handle, BaseOffset, ModuleSize, + ModuleName, ImageName, CheckSum, TimeDateStamp, InitialThreadHandle, + ThreadDataOffset, StartOffset) -> { + return callback.CreateProcess(ImageFileHandle, Handle, BaseOffset, ModuleSize, + ModuleName, ImageName, CheckSum, TimeDateStamp, InitialThreadHandle, + ThreadDataOffset, StartOffset); + }; + vtbl.ExitProcessCallback = (thisPointer, ExitCode) -> { + return callback.ExitProcess(ExitCode); + }; + vtbl.LoadModuleCallback = (thisPointer, ImageFileHandle, BaseOffset, ModuleSize, ModuleName, + ImageName, CheckSum, TimeDateStamp) -> { + return callback.LoadModule(ImageFileHandle, BaseOffset, ModuleSize, ModuleName, + ImageName, CheckSum, TimeDateStamp); + }; + vtbl.UnloadModuleCallback = (thisPointer, ImageBaseName, BaseOffset) -> { + return callback.UnloadModule(ImageBaseName, BaseOffset); + }; + vtbl.SystemErrorCallback = (thisPointer, Error, Level) -> { + return callback.SystemError(Error, Level); + }; + vtbl.SessionStatusCallback = (thisPointer, Status) -> { + return callback.SessionStatus(Status); + }; + vtbl.ChangeDebuggeeStateCallback = (thisPointer, Flags, Argument) -> { + return callback.ChangeDebuggeeState(Flags, Argument); + }; + vtbl.ChangeEngineStateCallback = (thisPointer, Flags, Argument) -> { + return callback.ChangeEngineState(Flags, Argument); + }; + vtbl.ChangeSymbolStateCallback = (thisPointer, Flags, Argument) -> { + return callback.ChangeSymbolState(Flags, Argument); + }; + } + + @Override + public HRESULT GetInterestMask(ULONGByReference Mask) { + return vtbl.GetInterestMaskCallback.invoke(getPointer(), Mask); + } + + @Override + public HRESULT Breakpoint(WrapIDebugBreakpoint.ByReference Bp) { + return vtbl.BreakpointCallback.invoke(getPointer(), Bp); + } + + @Override + public HRESULT Exception(EXCEPTION_RECORD64.ByReference Exception, ULONG FirstChance) { + return vtbl.ExceptionCallback.invoke(getPointer(), Exception, FirstChance); + } + + @Override + public HRESULT CreateThread(ULONGLONG Handle, ULONGLONG DataOffset, ULONGLONG StartOffset) { + return vtbl.CreateThreadCallback.invoke(getPointer(), Handle, DataOffset, StartOffset); + } + + @Override + public HRESULT ExitThread(ULONG ExitCode) { + return vtbl.ExitThreadCallback.invoke(getPointer(), ExitCode); + } + + @Override + public HRESULT CreateProcess(ULONGLONG ImageFileHandle, ULONGLONG Handle, ULONGLONG BaseOffset, + ULONG ModuleSize, WString ModuleName, WString ImageName, ULONG CheckSum, + ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, ULONGLONG ThreadDataOffset, + ULONGLONG StartOffset) { + return vtbl.CreateProcessCallback.invoke(getPointer(), ImageFileHandle, Handle, BaseOffset, + ModuleSize, ModuleName, ImageName, CheckSum, TimeDateStamp, InitialThreadHandle, + ThreadDataOffset, StartOffset); + } + + @Override + public HRESULT ExitProcess(ULONG ExitCode) { + return vtbl.ExitProcessCallback.invoke(getPointer(), ExitCode); + } + + @Override + public HRESULT LoadModule(ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, ULONG ModuleSize, + WString ModuleName, WString ImageName, ULONG CheckSum, ULONG TimeDateStamp) { + return vtbl.LoadModuleCallback.invoke(getPointer(), ImageFileHandle, BaseOffset, ModuleSize, + ModuleName, ImageName, CheckSum, TimeDateStamp); + } + + @Override + public HRESULT UnloadModule(WString ImageBaseName, ULONGLONG BaseOffset) { + return vtbl.UnloadModuleCallback.invoke(getPointer(), ImageBaseName, BaseOffset); + } + + @Override + public HRESULT SystemError(ULONG Error, ULONG Level) { + return vtbl.SystemErrorCallback.invoke(getPointer(), Error, Level); + } + + @Override + public HRESULT SessionStatus(ULONG Status) { + return vtbl.SessionStatusCallback.invoke(getPointer(), Status); + } + + @Override + public HRESULT ChangeDebuggeeState(ULONG Flags, ULONGLONG Argument) { + return vtbl.ChangeDebuggeeStateCallback.invoke(getPointer(), Flags, Argument); + } + + @Override + public HRESULT ChangeEngineState(ULONG Flags, ULONGLONG Argument) { + return vtbl.ChangeEngineStateCallback.invoke(getPointer(), Flags, Argument); + } + + @Override + public HRESULT ChangeSymbolState(ULONG Flags, ULONGLONG Argument) { + return vtbl.ChangeSymbolStateCallback.invoke(getPointer(), Flags, Argument); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventContextCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventContextCallbacks.java new file mode 100644 index 0000000000..22df5c5ac8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/ListenerIDebugEventContextCallbacks.java @@ -0,0 +1,198 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import java.util.List; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint2; + +public class ListenerIDebugEventContextCallbacks extends Structure + implements IDebugEventContextCallbacks, MarkerEventCallbacks { + public static final List FIELDS = createFieldsOrder("vtbl"); + + public ListenerIDebugEventContextCallbacks(CallbackIDebugEventContextCallbacks callback) { + this.vtbl = this.constructVTable(); + this.initVTable(callback); + super.write(); + } + + public VTableIDebugEventContextCallbacks.ByReference vtbl; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + protected VTableIDebugEventContextCallbacks.ByReference constructVTable() { + return new VTableIDebugEventContextCallbacks.ByReference(); + } + + protected void initVTable(final CallbackIDebugEventContextCallbacks callback) { + vtbl.QueryInterfaceCallback = (thisPointer, refid, ppvObject) -> { + return callback.QueryInterface(refid, ppvObject); + }; + vtbl.AddRefCallback = (thisPointer) -> { + return callback.AddRef(); + }; + vtbl.ReleaseCallback = (thisPointer) -> { + return callback.Release(); + }; + vtbl.GetInterestMaskCallback = (thisPointer, Mask) -> { + return callback.GetInterestMask(Mask); + }; + vtbl.BreakpointCallback = (thisPointer, Bp, Context, ContextSize) -> { + return callback.Breakpoint(Bp, Context, ContextSize); + }; + vtbl.ExceptionCallback = (thisPointer, Exception, FirstChance, Context, ContextSize) -> { + return callback.Exception(Exception, FirstChance, Context, ContextSize); + }; + vtbl.CreateThreadCallback = + (thisPointer, Handle, DataOffset, StartOffset, Context, ContextSize) -> { + return callback.CreateThread(Handle, DataOffset, StartOffset, Context, ContextSize); + }; + vtbl.ExitThreadCallback = (thisPointer, ExitCode, Context, ContextSize) -> { + return callback.ExitThread(ExitCode, Context, ContextSize); + }; + vtbl.CreateProcessCallback = (thisPointer, ImageFileHandle, Handle, BaseOffset, ModuleSize, + ModuleName, ImageName, CheckSum, TimeDateStamp, InitialThreadHandle, + ThreadDataOffset, StartOffset, Context, ContextSize) -> { + return callback.CreateProcess(ImageFileHandle, Handle, BaseOffset, ModuleSize, + ModuleName, ImageName, CheckSum, TimeDateStamp, InitialThreadHandle, + ThreadDataOffset, StartOffset, Context, ContextSize); + }; + vtbl.ExitProcessCallback = (thisPointer, ExitCode, Context, ContextSize) -> { + return callback.ExitProcess(ExitCode, Context, ContextSize); + }; + vtbl.LoadModuleCallback = (thisPointer, ImageFileHandle, BaseOffset, ModuleSize, ModuleName, + ImageName, CheckSum, TimeDateStamp, Context, ContextSize) -> { + return callback.LoadModule(ImageFileHandle, BaseOffset, ModuleSize, ModuleName, + ImageName, CheckSum, TimeDateStamp, Context, ContextSize); + }; + vtbl.UnloadModuleCallback = + (thisPointer, ImageBaseName, BaseOffset, Context, ContextSize) -> { + return callback.UnloadModule(ImageBaseName, BaseOffset, Context, ContextSize); + }; + vtbl.SystemErrorCallback = (thisPointer, Error, Level, Context, ContextSize) -> { + return callback.SystemError(Error, Level, Context, ContextSize); + }; + vtbl.SessionStatusCallback = (thisPointer, Status) -> { + return callback.SessionStatus(Status); + }; + vtbl.ChangeDebuggeeStateCallback = (thisPointer, Flags, Argument, Context, ContextSize) -> { + return callback.ChangeDebuggeeState(Flags, Argument, Context, ContextSize); + }; + vtbl.ChangeEngineStateCallback = (thisPointer, Flags, Argument, Context, ContextSize) -> { + return callback.ChangeEngineState(Flags, Argument, Context, ContextSize); + }; + vtbl.ChangeSymbolStateCallback = (thisPointer, Flags, Argument) -> { + return callback.ChangeSymbolState(Flags, Argument); + }; + } + + @Override + public HRESULT GetInterestMask(ULONGByReference Mask) { + return vtbl.GetInterestMaskCallback.invoke(getPointer(), Mask); + } + + @Override + public HRESULT Breakpoint(WrapIDebugBreakpoint2.ByReference Bp, Pointer Context, + ULONG ContextSize) { + return vtbl.BreakpointCallback.invoke(getPointer(), Bp, Context, ContextSize); + } + + @Override + public HRESULT Exception(EXCEPTION_RECORD64.ByReference Exception, ULONG FirstChance, + Pointer Context, ULONG ContextSize) { + return vtbl.ExceptionCallback.invoke(getPointer(), Exception, FirstChance, Context, + ContextSize); + } + + @Override + public HRESULT CreateThread(ULONGLONG Handle, ULONGLONG DataOffset, ULONGLONG StartOffset, + Pointer Context, ULONG ContextSize) { + return vtbl.CreateThreadCallback.invoke(getPointer(), Handle, DataOffset, StartOffset, + Context, ContextSize); + } + + @Override + public HRESULT ExitThread(ULONG ExitCode, Pointer Context, ULONG ContextSize) { + return vtbl.ExitThreadCallback.invoke(getPointer(), ExitCode, Context, ContextSize); + } + + @Override + public HRESULT CreateProcess(ULONGLONG ImageFileHandle, ULONGLONG Handle, ULONGLONG BaseOffset, + ULONG ModuleSize, WString ModuleName, WString ImageName, ULONG CheckSum, + ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, ULONGLONG ThreadDataOffset, + ULONGLONG StartOffset, Pointer Context, ULONG ContextSize) { + return vtbl.CreateProcessCallback.invoke(getPointer(), ImageFileHandle, Handle, BaseOffset, + ModuleSize, ModuleName, ImageName, CheckSum, TimeDateStamp, InitialThreadHandle, + ThreadDataOffset, StartOffset, Context, ContextSize); + } + + @Override + public HRESULT ExitProcess(ULONG ExitCode, Pointer Context, ULONG ContextSize) { + return vtbl.ExitProcessCallback.invoke(getPointer(), ExitCode, Context, ContextSize); + } + + @Override + public HRESULT LoadModule(ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, ULONG ModuleSize, + WString ModuleName, WString ImageName, ULONG CheckSum, ULONG TimeDateStamp, + Pointer Context, ULONG ContextSize) { + return vtbl.LoadModuleCallback.invoke(getPointer(), ImageFileHandle, BaseOffset, ModuleSize, + ModuleName, ImageName, CheckSum, TimeDateStamp, Context, ContextSize); + } + + @Override + public HRESULT UnloadModule(WString ImageBaseName, ULONGLONG BaseOffset, Pointer Context, + ULONG ContextSize) { + return vtbl.UnloadModuleCallback.invoke(getPointer(), ImageBaseName, BaseOffset, Context, + ContextSize); + } + + @Override + public HRESULT SystemError(ULONG Error, ULONG Level, Pointer Context, ULONG ContextSize) { + return vtbl.SystemErrorCallback.invoke(getPointer(), Error, Level, Context, ContextSize); + } + + @Override + public HRESULT SessionStatus(ULONG Status) { + return vtbl.SessionStatusCallback.invoke(getPointer(), Status); + } + + @Override + public HRESULT ChangeDebuggeeState(ULONG Flags, ULONGLONG Argument, Pointer Context, + ULONG ContextSize) { + return vtbl.ChangeDebuggeeStateCallback.invoke(getPointer(), Flags, Argument, Context, + ContextSize); + } + + @Override + public HRESULT ChangeEngineState(ULONG Flags, ULONGLONG Argument, Pointer Context, + ULONG ContextSize) { + return vtbl.ChangeEngineStateCallback.invoke(getPointer(), Flags, Argument, Context, + ContextSize); + } + + @Override + public HRESULT ChangeSymbolState(ULONG Flags, ULONGLONG Argument) { + return vtbl.ChangeSymbolStateCallback.invoke(getPointer(), Flags, Argument); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/MarkerEventCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/MarkerEventCallbacks.java new file mode 100644 index 0000000000..16bbf7593c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/MarkerEventCallbacks.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +public interface MarkerEventCallbacks { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventCallbacks.java new file mode 100644 index 0000000000..89c1504d6d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventCallbacks.java @@ -0,0 +1,127 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import java.util.List; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.win32.StdCallLibrary; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint; +import agent.dbgeng.jna.dbgeng.io.VTableIDebugInputCallbacks.*; + +public class VTableIDebugEventCallbacks extends Structure { + public static class ByReference extends VTableIDebugEventCallbacks + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("QueryInterfaceCallback", + "AddRefCallback", "ReleaseCallback", "GetInterestMaskCallback", "BreakpointCallback", + "ExceptionCallback", "CreateThreadCallback", "ExitThreadCallback", "CreateProcessCallback", + "ExitProcessCallback", "LoadModuleCallback", "UnloadModuleCallback", "SystemErrorCallback", + "SessionStatusCallback", "ChangeDebuggeeStateCallback", "ChangeEngineStateCallback", + "ChangeSymbolStateCallback"); + + public QueryInterfaceCallback QueryInterfaceCallback; + public AddRefCallback AddRefCallback; + public ReleaseCallback ReleaseCallback; + public GetInterestMaskCallback GetInterestMaskCallback; + public BreakpointCallback BreakpointCallback; + public ExceptionCallback ExceptionCallback; + public CreateThreadCallback CreateThreadCallback; + public ExitThreadCallback ExitThreadCallback; + public CreateProcessCallback CreateProcessCallback; + public ExitProcessCallback ExitProcessCallback; + public LoadModuleCallback LoadModuleCallback; + public UnloadModuleCallback UnloadModuleCallback; + public SystemErrorCallback SystemErrorCallback; + public SessionStatusCallback SessionStatusCallback; + public ChangeDebuggeeStateCallback ChangeDebuggeeStateCallback; + public ChangeEngineStateCallback ChangeEngineStateCallback; + public ChangeSymbolStateCallback ChangeSymbolStateCallback; + + @Override + public List getFieldOrder() { + return FIELDS; + } + + public static interface GetInterestMaskCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGByReference Mask); + } + + public static interface BreakpointCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, WrapIDebugBreakpoint.ByReference Bp); + } + + public static interface ExceptionCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, EXCEPTION_RECORD64.ByReference Exception, + ULONG FirstChance); + } + + public static interface CreateThreadCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGLONG Handle, ULONGLONG DataOffset, + ULONGLONG StartOffset); + } + + public static interface ExitThreadCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG ExitCode); + } + + public static interface CreateProcessCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGLONG ImageFileHandle, ULONGLONG Handle, + ULONGLONG BaseOffset, ULONG ModuleSize, String ModuleName, String ImageName, + ULONG CheckSum, ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, + ULONGLONG ThreadDataOffset, ULONGLONG StartOffset); + } + + public static interface ExitProcessCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG ExitCode); + } + + public static interface LoadModuleCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, + ULONG ModuleSize, String ModuleName, String ImageName, ULONG CheckSum, + ULONG TimeDateStamp); + } + + public static interface UnloadModuleCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, String ImageBaseName, ULONGLONG BaseOffset); + } + + public static interface SystemErrorCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Error, ULONG Level); + } + + public static interface SessionStatusCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Status); + } + + public static interface ChangeDebuggeeStateCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Flags, ULONGLONG Argument); + } + + public static interface ChangeEngineStateCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Flags, ULONGLONG Argument); + } + + public static interface ChangeSymbolStateCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Flags, ULONGLONG Argument); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventCallbacksWide.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventCallbacksWide.java new file mode 100644 index 0000000000..385b239e2a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventCallbacksWide.java @@ -0,0 +1,126 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import java.util.List; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.win32.StdCallLibrary; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint; +import agent.dbgeng.jna.dbgeng.io.VTableIDebugInputCallbacks.*; + +public class VTableIDebugEventCallbacksWide extends Structure { + public static class ByReference extends VTableIDebugEventCallbacksWide + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("QueryInterfaceCallback", + "AddRefCallback", "ReleaseCallback", "GetInterestMaskCallback", "BreakpointCallback", + "ExceptionCallback", "CreateThreadCallback", "ExitThreadCallback", "CreateProcessCallback", + "ExitProcessCallback", "LoadModuleCallback", "UnloadModuleCallback", "SystemErrorCallback", + "SessionStatusCallback", "ChangeDebuggeeStateCallback", "ChangeEngineStateCallback", + "ChangeSymbolStateCallback"); + + public QueryInterfaceCallback QueryInterfaceCallback; + public AddRefCallback AddRefCallback; + public ReleaseCallback ReleaseCallback; + public GetInterestMaskCallback GetInterestMaskCallback; + public BreakpointCallback BreakpointCallback; + public ExceptionCallback ExceptionCallback; + public CreateThreadCallback CreateThreadCallback; + public ExitThreadCallback ExitThreadCallback; + public CreateProcessCallback CreateProcessCallback; + public ExitProcessCallback ExitProcessCallback; + public LoadModuleCallback LoadModuleCallback; + public UnloadModuleCallback UnloadModuleCallback; + public SystemErrorCallback SystemErrorCallback; + public SessionStatusCallback SessionStatusCallback; + public ChangeDebuggeeStateCallback ChangeDebuggeeStateCallback; + public ChangeEngineStateCallback ChangeEngineStateCallback; + public ChangeSymbolStateCallback ChangeSymbolStateCallback; + + @Override + public List getFieldOrder() { + return FIELDS; + } + + public static interface GetInterestMaskCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGByReference Mask); + } + + public static interface BreakpointCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, WrapIDebugBreakpoint.ByReference Bp); + } + + public static interface ExceptionCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, EXCEPTION_RECORD64.ByReference Exception, + ULONG FirstChance); + } + + public static interface CreateThreadCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGLONG Handle, ULONGLONG DataOffset, + ULONGLONG StartOffset); + } + + public static interface ExitThreadCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG ExitCode); + } + + public static interface CreateProcessCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGLONG ImageFileHandle, ULONGLONG Handle, + ULONGLONG BaseOffset, ULONG ModuleSize, WString ModuleName, WString ImageName, + ULONG CheckSum, ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, + ULONGLONG ThreadDataOffset, ULONGLONG StartOffset); + } + + public static interface ExitProcessCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG ExitCode); + } + + public static interface LoadModuleCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, + ULONG ModuleSize, WString ModuleName, WString ImageName, ULONG CheckSum, + ULONG TimeDateStamp); + } + + public static interface UnloadModuleCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, WString ImageBaseName, ULONGLONG BaseOffset); + } + + public static interface SystemErrorCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Error, ULONG Level); + } + + public static interface SessionStatusCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Status); + } + + public static interface ChangeDebuggeeStateCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Flags, ULONGLONG Argument); + } + + public static interface ChangeEngineStateCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Flags, ULONGLONG Argument); + } + + public static interface ChangeSymbolStateCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Flags, ULONGLONG Argument); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventContextCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventContextCallbacks.java new file mode 100644 index 0000000000..45046f4635 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/event/VTableIDebugEventContextCallbacks.java @@ -0,0 +1,132 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.event; + +import java.util.List; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.win32.StdCallLibrary; + +import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64; +import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint2; +import agent.dbgeng.jna.dbgeng.io.VTableIDebugInputCallbacks.*; + +public class VTableIDebugEventContextCallbacks extends Structure { + public static class ByReference extends VTableIDebugEventContextCallbacks + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("QueryInterfaceCallback", + "AddRefCallback", "ReleaseCallback", "GetInterestMaskCallback", "BreakpointCallback", + "ExceptionCallback", "CreateThreadCallback", "ExitThreadCallback", "CreateProcessCallback", + "ExitProcessCallback", "LoadModuleCallback", "UnloadModuleCallback", "SystemErrorCallback", + "SessionStatusCallback", "ChangeDebuggeeStateCallback", "ChangeEngineStateCallback", + "ChangeSymbolStateCallback"); + + public QueryInterfaceCallback QueryInterfaceCallback; + public AddRefCallback AddRefCallback; + public ReleaseCallback ReleaseCallback; + public GetInterestMaskCallback GetInterestMaskCallback; + public BreakpointCallback BreakpointCallback; + public ExceptionCallback ExceptionCallback; + public CreateThreadCallback CreateThreadCallback; + public ExitThreadCallback ExitThreadCallback; + public CreateProcessCallback CreateProcessCallback; + public ExitProcessCallback ExitProcessCallback; + public LoadModuleCallback LoadModuleCallback; + public UnloadModuleCallback UnloadModuleCallback; + public SystemErrorCallback SystemErrorCallback; + public SessionStatusCallback SessionStatusCallback; + public ChangeDebuggeeStateCallback ChangeDebuggeeStateCallback; + public ChangeEngineStateCallback ChangeEngineStateCallback; + public ChangeSymbolStateCallback ChangeSymbolStateCallback; + + @Override + public List getFieldOrder() { + return FIELDS; + } + + public static interface GetInterestMaskCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGByReference Mask); + } + + public static interface BreakpointCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, WrapIDebugBreakpoint2.ByReference Bp, Pointer Context, + ULONG ContextSize); + } + + public static interface ExceptionCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, EXCEPTION_RECORD64.ByReference Exception, + ULONG FirstChance, Pointer Context, ULONG ContextSize); + } + + public static interface CreateThreadCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGLONG Handle, ULONGLONG DataOffset, + ULONGLONG StartOffset, Pointer Context, ULONG ContextSize); + } + + public static interface ExitThreadCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG ExitCode, Pointer Context, ULONG ContextSize); + } + + public static interface CreateProcessCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGLONG ImageFileHandle, ULONGLONG Handle, + ULONGLONG BaseOffset, ULONG ModuleSize, WString ModuleName, WString ImageName, + ULONG CheckSum, ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, + ULONGLONG ThreadDataOffset, ULONGLONG StartOffset, Pointer Context, + ULONG ContextSize); + } + + public static interface ExitProcessCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG ExitCode, Pointer Context, ULONG ContextSize); + } + + public static interface LoadModuleCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, + ULONG ModuleSize, WString ModuleName, WString ImageName, ULONG CheckSum, + ULONG TimeDateStamp, Pointer Context, ULONG ContextSize); + } + + public static interface UnloadModuleCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, WString ImageBaseName, ULONGLONG BaseOffset, + Pointer Context, ULONG ContextSize); + } + + public static interface SystemErrorCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Error, ULONG Level, Pointer Context, + ULONG ContextSize); + } + + public static interface SessionStatusCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Status); + } + + public static interface ChangeDebuggeeStateCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Flags, ULONGLONG Argument, Pointer Context, + ULONG ContextSize); + } + + public static interface ChangeEngineStateCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Flags, ULONGLONG Argument, Pointer Context, + ULONG ContextSize); + } + + public static interface ChangeSymbolStateCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Flags, ULONGLONG Argument); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugInputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugInputCallbacks.java new file mode 100644 index 0000000000..9a5b846d01 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugInputCallbacks.java @@ -0,0 +1,21 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import com.sun.jna.platform.win32.COM.IUnknownCallback; + +public interface CallbackIDebugInputCallbacks extends IDebugInputCallbacks, IUnknownCallback { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacks.java new file mode 100644 index 0000000000..ce6d65ba18 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacks.java @@ -0,0 +1,21 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import com.sun.jna.platform.win32.COM.IUnknownCallback; + +public interface CallbackIDebugOutputCallbacks extends IDebugOutputCallbacks, IUnknownCallback { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacks2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacks2.java new file mode 100644 index 0000000000..8a4331800c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacks2.java @@ -0,0 +1,21 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import com.sun.jna.platform.win32.COM.IUnknownCallback; + +public interface CallbackIDebugOutputCallbacks2 extends IDebugOutputCallbacks2, IUnknownCallback { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacksWide.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacksWide.java new file mode 100644 index 0000000000..74a951be01 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/CallbackIDebugOutputCallbacksWide.java @@ -0,0 +1,22 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import com.sun.jna.platform.win32.COM.IUnknownCallback; + +public interface CallbackIDebugOutputCallbacksWide + extends IDebugOutputCallbacksWide, IUnknownCallback { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugInputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugInputCallbacks.java new file mode 100644 index 0000000000..99a14d38d1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugInputCallbacks.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public interface IDebugInputCallbacks { + final IID IID_IDEBUG_INPUT_CALLBACKS = new IID("9f50e42c-f136-499e-9a97-73036c94ed2d"); + + HRESULT StartInput(ULONG BufferSize); + + HRESULT EndInput(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacks.java new file mode 100644 index 0000000000..80aa42a0c5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacks.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public interface IDebugOutputCallbacks { + final IID IID_IDEBUG_OUTPUT_CALLBACKS = new IID("4bf58045-d654-4c40-b0af-683090f356dc"); + + HRESULT Output(ULONG Mask, String Text); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacks2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacks2.java new file mode 100644 index 0000000000..eb637fa4b1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacks2.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public interface IDebugOutputCallbacks2 extends IDebugOutputCallbacks { + final IID IID_IDEBUG_OUTPUT_CALLBACKS2 = new IID("67721fe9-56d2-4a44-a325-2b65513ce6eb"); + + HRESULT GetInterestMask(ULONGByReference Mask); + + HRESULT Output2(ULONG Which, ULONG Flags, ULONGLONG Arg, WString Text); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacksWide.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacksWide.java new file mode 100644 index 0000000000..0d09bd28e9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/IDebugOutputCallbacksWide.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public interface IDebugOutputCallbacksWide { + final IID IID_IDEBUG_OUTPUT_CALLBACKS_WIDE = new IID("4c7fd663-c394-4e26-8ef1-34ad5ed3764c"); + + HRESULT Output(ULONG Mask, WString Text); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugInputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugInputCallbacks.java new file mode 100644 index 0000000000..8d6f920f17 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugInputCallbacks.java @@ -0,0 +1,72 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import java.util.List; + +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class ListenerIDebugInputCallbacks extends Structure + implements IDebugInputCallbacks, MarkerInputCallbacks { + public static final List FIELDS = createFieldsOrder("vtbl"); + + public ListenerIDebugInputCallbacks(CallbackIDebugInputCallbacks callback) { + this.vtbl = this.constructVTable(); + this.initVTable(callback); + super.write(); + } + + public VTableIDebugInputCallbacks.ByReference vtbl; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + protected VTableIDebugInputCallbacks.ByReference constructVTable() { + return new VTableIDebugInputCallbacks.ByReference(); + } + + protected void initVTable(final CallbackIDebugInputCallbacks callback) { + vtbl.QueryInterfaceCallback = (thisPointer, refid, ppvObject) -> { + return callback.QueryInterface(refid, ppvObject); + }; + vtbl.AddRefCallback = (thisPointer) -> { + return callback.AddRef(); + }; + vtbl.ReleaseCallback = (thisPointer) -> { + return callback.Release(); + }; + vtbl.StartInputCallback = (thisPointer, BufferSize) -> { + return callback.StartInput(BufferSize); + }; + vtbl.EndInputCallback = (thisPointer) -> { + return callback.EndInput(); + }; + } + + @Override + public HRESULT StartInput(ULONG BufferSize) { + return this.vtbl.StartInputCallback.invoke(getPointer(), BufferSize); + } + + @Override + public HRESULT EndInput() { + return this.vtbl.EndInputCallback.invoke(getPointer()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacks.java new file mode 100644 index 0000000000..a6cb9aad6d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacks.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import java.util.List; + +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class ListenerIDebugOutputCallbacks extends Structure + implements IDebugOutputCallbacks, MarkerOutputCallbacks { + public static final List FIELDS = createFieldsOrder("vtbl"); + + public ListenerIDebugOutputCallbacks(CallbackIDebugOutputCallbacks callback) { + this.vtbl = this.constructVTable(); + this.initVTable(callback); + super.write(); + } + + public VTableIDebugOutputCallbacks.ByReference vtbl; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + protected VTableIDebugOutputCallbacks.ByReference constructVTable() { + return new VTableIDebugOutputCallbacks.ByReference(); + } + + protected void initVTable(final CallbackIDebugOutputCallbacks callback) { + vtbl.QueryInterfaceCallback = (thisPointer, refid, ppvObject) -> { + return callback.QueryInterface(refid, ppvObject); + }; + vtbl.AddRefCallback = (thisPointer) -> { + return callback.AddRef(); + }; + vtbl.ReleaseCallback = (thisPointer) -> { + return callback.Release(); + }; + vtbl.OutputCallback = (thisPointer, Mask, Text) -> { + return callback.Output(Mask, Text); + }; + } + + @Override + public HRESULT Output(ULONG Mask, String Text) { + return vtbl.OutputCallback.invoke(getPointer(), Mask, Text); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacks2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacks2.java new file mode 100644 index 0000000000..58ca63ce88 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacks2.java @@ -0,0 +1,81 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import java.util.List; + +import com.sun.jna.Structure; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class ListenerIDebugOutputCallbacks2 extends Structure + implements IDebugOutputCallbacks2, MarkerOutputCallbacks { + public static final List FIELDS = createFieldsOrder("vtbl"); + + public ListenerIDebugOutputCallbacks2(CallbackIDebugOutputCallbacks2 callback) { + this.vtbl = this.constructVTable(); + this.initVTable(callback); + super.write(); + } + + public VTableIDebugOutputCallbacks2.ByReference vtbl; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + protected VTableIDebugOutputCallbacks2.ByReference constructVTable() { + return new VTableIDebugOutputCallbacks2.ByReference(); + } + + protected void initVTable(final CallbackIDebugOutputCallbacks2 callback) { + vtbl.QueryInterfaceCallback = (thisPointer, refid, ppvObject) -> { + return callback.QueryInterface(refid, ppvObject); + }; + vtbl.AddRefCallback = (thisPointer) -> { + return callback.AddRef(); + }; + vtbl.ReleaseCallback = (thisPointer) -> { + return callback.Release(); + }; + vtbl.OutputCallback = (thisPointer, Mask, Text) -> { + return callback.Output(Mask, Text); + }; + vtbl.GetInterestMaskCallback = (thisPointer, Mask) -> { + return callback.GetInterestMask(Mask); + }; + vtbl.Output2Callback = (thisPointer, Which, Flags, Arg, Text) -> { + return callback.Output2(Which, Flags, Arg, Text); + }; + } + + @Override + public HRESULT Output(ULONG Mask, String Text) { + return vtbl.OutputCallback.invoke(getPointer(), Mask, Text); + } + + @Override + public HRESULT GetInterestMask(ULONGByReference Mask) { + return vtbl.GetInterestMaskCallback.invoke(getPointer(), Mask); + } + + @Override + public HRESULT Output2(ULONG Which, ULONG Flags, ULONGLONG Arg, WString Text) { + return vtbl.Output2Callback.invoke(getPointer(), Which, Flags, Arg, Text); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacksWide.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacksWide.java new file mode 100644 index 0000000000..6f88d324af --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/ListenerIDebugOutputCallbacksWide.java @@ -0,0 +1,65 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import java.util.List; + +import com.sun.jna.Structure; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class ListenerIDebugOutputCallbacksWide extends Structure + implements IDebugOutputCallbacksWide, MarkerOutputCallbacks { + public static final List FIELDS = createFieldsOrder("vtbl"); + + public ListenerIDebugOutputCallbacksWide(CallbackIDebugOutputCallbacksWide callback) { + this.vtbl = this.constructVTable(); + this.initVTable(callback); + super.write(); + } + + public VTableIDebugOutputCallbacksWide.ByReference vtbl; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + protected VTableIDebugOutputCallbacksWide.ByReference constructVTable() { + return new VTableIDebugOutputCallbacksWide.ByReference(); + } + + protected void initVTable(final CallbackIDebugOutputCallbacksWide callback) { + vtbl.QueryInterfaceCallback = (thisPointer, refid, ppvObject) -> { + return callback.QueryInterface(refid, ppvObject); + }; + vtbl.AddRefCallback = (thisPointer) -> { + return callback.AddRef(); + }; + vtbl.ReleaseCallback = (thisPointer) -> { + return callback.Release(); + }; + vtbl.OutputCallback = (thisPointer, Mask, Text) -> { + return callback.Output(Mask, Text); + }; + } + + @Override + public HRESULT Output(ULONG Mask, WString Text) { + return vtbl.OutputCallback.invoke(getPointer(), Mask, Text); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/MarkerInputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/MarkerInputCallbacks.java new file mode 100644 index 0000000000..dfd8167b3c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/MarkerInputCallbacks.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +public interface MarkerInputCallbacks { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/MarkerOutputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/MarkerOutputCallbacks.java new file mode 100644 index 0000000000..6650813e5c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/MarkerOutputCallbacks.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +public interface MarkerOutputCallbacks { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugInputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugInputCallbacks.java new file mode 100644 index 0000000000..c84c3e78b6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugInputCallbacks.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import java.util.List; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.win32.StdCallLibrary; + +public class VTableIDebugInputCallbacks extends Structure { + public static class ByReference extends VTableIDebugInputCallbacks + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("QueryInterfaceCallback", + "AddRefCallback", "ReleaseCallback", "StartInputCallback", "EndInputCallback"); + + public QueryInterfaceCallback QueryInterfaceCallback; + public AddRefCallback AddRefCallback; + public ReleaseCallback ReleaseCallback; + public StartInputCallback StartInputCallback; + public EndInputCallback EndInputCallback; + + @Override + public List getFieldOrder() { + return FIELDS; + } + + public static interface QueryInterfaceCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, REFIID refid, PointerByReference ppvObject); + } + + public static interface AddRefCallback extends StdCallLibrary.StdCallCallback { + int invoke(Pointer thisPointer); + } + + public static interface ReleaseCallback extends StdCallLibrary.StdCallCallback { + int invoke(Pointer thisPointer); + } + + public static interface StartInputCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG BufferSize); + } + + public static interface EndInputCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacks.java new file mode 100644 index 0000000000..0c05469f3b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacks.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import java.util.List; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.win32.StdCallLibrary; + +public class VTableIDebugOutputCallbacks extends Structure { + public static class ByReference extends VTableIDebugOutputCallbacks + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("QueryInterfaceCallback", + "AddRefCallback", "ReleaseCallback", "OutputCallback"); + + public QueryInterfaceCallback QueryInterfaceCallback; + public AddRefCallback AddRefCallback; + public ReleaseCallback ReleaseCallback; + public OutputCallback OutputCallback; + + @Override + public List getFieldOrder() { + return FIELDS; + } + + public static interface QueryInterfaceCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, REFIID refid, PointerByReference ppvObject); + } + + public static interface AddRefCallback extends StdCallLibrary.StdCallCallback { + int invoke(Pointer thisPointer); + } + + public static interface ReleaseCallback extends StdCallLibrary.StdCallCallback { + int invoke(Pointer thisPointer); + } + + public static interface OutputCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Mask, String Text); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacks2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacks2.java new file mode 100644 index 0000000000..b8afc09848 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacks2.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import java.util.List; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.win32.StdCallLibrary; + +public class VTableIDebugOutputCallbacks2 extends VTableIDebugOutputCallbacks { + public static class ByReference extends VTableIDebugOutputCallbacks2 + implements Structure.ByReference { + } + + @SuppressWarnings("hiding") + public static final List FIELDS = + createFieldsOrder("QueryInterfaceCallback", "AddRefCallback", "ReleaseCallback", + "OutputCallback", "GetInterestMaskCallback", "Output2Callback"); + + public GetInterestMaskCallback GetInterestMaskCallback; + public Output2Callback Output2Callback; + + @Override + public List getFieldOrder() { + return FIELDS; + } + + public static interface GetInterestMaskCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONGByReference Mask); + } + + public static interface Output2Callback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Which, ULONG Flags, ULONGLONG Arg, WString Text); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacksWide.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacksWide.java new file mode 100644 index 0000000000..bc1484ef77 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/io/VTableIDebugOutputCallbacksWide.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.io; + +import java.util.List; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.win32.StdCallLibrary; + +public class VTableIDebugOutputCallbacksWide extends Structure { + public static class ByReference extends VTableIDebugOutputCallbacksWide + implements Structure.ByReference { + } + + public static final List FIELDS = createFieldsOrder("QueryInterfaceCallback", + "AddRefCallback", "ReleaseCallback", "OutputCallback"); + + public QueryInterfaceCallback QueryInterfaceCallback; + public AddRefCallback AddRefCallback; + public ReleaseCallback ReleaseCallback; + public OutputCallback OutputCallback; + + @Override + public List getFieldOrder() { + return FIELDS; + } + + public static interface QueryInterfaceCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, REFIID refid, PointerByReference ppvObject); + } + + public static interface AddRefCallback extends StdCallLibrary.StdCallCallback { + int invoke(Pointer thisPointer); + } + + public static interface ReleaseCallback extends StdCallLibrary.StdCallCallback { + int invoke(Pointer thisPointer); + } + + public static interface OutputCallback extends StdCallLibrary.StdCallCallback { + HRESULT invoke(Pointer thisPointer, ULONG Mask, WString Text); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/IDebugRegisters.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/IDebugRegisters.java new file mode 100644 index 0000000000..b928556307 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/IDebugRegisters.java @@ -0,0 +1,75 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.registers; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_REGISTER_DESCRIPTION; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +import com.sun.jna.platform.win32.COM.IUnknown; + +public interface IDebugRegisters extends IUnknown { + final IID IID_IDEBUG_REGISTERS = new IID("ce289126-9e84-45a7-937e-67bb18691493"); + + enum VTIndices implements VTableIndex { + GET_NUMBER_REGISTERS, // + GET_DESCRIPTION, // + GET_INDEX_BY_NAME, // + GET_VALUE, // + SET_VALUE, // + GET_VALUES, // + SET_VALUES, // + OUTPUT_REGISTERS, // + GET_INSTRUCTION_OFFSET, // + GET_STACK_OFFSET, // + GET_FRAME_OFFSET, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetNumberRegisters(ULONGByReference Number); + + HRESULT GetDescription(ULONG Register, byte[] NameBuffer, ULONG NameBufferSize, + ULONGByReference NameSize, DEBUG_REGISTER_DESCRIPTION.ByReference Desc); + + HRESULT GetIndexByName(String Name, ULONGByReference Index); + + HRESULT GetValue(ULONG Register, DEBUG_VALUE.ByReference Value); + + HRESULT SetValue(ULONG Register, DEBUG_VALUE.ByReference Value); + + HRESULT GetValues(ULONG Count, ULONG[] Indices, ULONG Start, DEBUG_VALUE[] Values); + + HRESULT SetValues(ULONG Count, ULONG[] Indices, ULONG Start, DEBUG_VALUE[] Values); + + HRESULT OutputRegisters(ULONG OutputControl, ULONG Flags); + + HRESULT GetInstructionOffset(ULONGLONGByReference Offset); + + HRESULT GetStackOffset(ULONGLONGByReference Offset); + + HRESULT GetFrameOffset(ULONGLONGByReference Offset); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/IDebugRegisters2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/IDebugRegisters2.java new file mode 100644 index 0000000000..629b5a7e0f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/IDebugRegisters2.java @@ -0,0 +1,92 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.registers; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_REGISTER_DESCRIPTION; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugRegisters2 extends IDebugRegisters { + final IID IID_IDEBUG_REGISTERS2 = new IID("1656afa9-19c6-4e3a-97e7-5dc9160cf9c4"); + + enum VTIndices2 implements VTableIndex { + GET_DESCRIPTION_WIDE, // + GET_INDEX_BY_NAME_WIDE, // + GET_NUMBER_PSEUDO_REGISTERS, // + GET_PSEUDO_DESCRIPTION, // + GET_PSEUDO_DESCRIPTION_WIDE, // + GET_PSEUDO_INDEX_BY_NAME, // + GET_PSEUDO_INDEX_BY_NAME_WIDE, // + GET_PSEUDO_VALUES, // + SET_PSEUDO_VALUES, // + GET_VALUES2, // + SET_VALUES2, // + OUTPUT_REGISTERS2, // + GET_INSTRUCTION_OFFSET2, // + GET_STACK_OFFSET2, // + GET_FRAME_OFFSET2, // + ; + + static int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetDescriptionWide(ULONG Register, char[] NameBuffer, ULONG NameBufferSize, + ULONGByReference NameSize, DEBUG_REGISTER_DESCRIPTION.ByReference Desc); + + HRESULT GetIndexByNameWide(WString Name, ULONGByReference Index); + + HRESULT GetNumberPseudoRegisters(ULONGByReference Number); + + HRESULT GetPseudoDescription(ULONG Register, byte[] NameBuffer, ULONG NameBufferSize, + ULONGByReference NameSize, ULONGLONGByReference TypeModule, ULONGByReference TypeId); + + HRESULT GetPseudoDescriptionWide(ULONG Register, char[] NameBuffer, ULONG NameBufferSize, + ULONGByReference NameSize, ULONGLONGByReference TypeModule, ULONGByReference TypeId); + + HRESULT GetPseudoIndexByName(String Name, ULONGByReference Index); + + HRESULT GetPseudoIndexByNameWide(WString Name, ULONGByReference Index); + + HRESULT GetPseudoValues(ULONG Source, ULONG Count, ULONG[] Indices, ULONG Start, + DEBUG_VALUE[] Values); + + HRESULT SetPseudoValues(ULONG Source, ULONG Count, ULONG[] Indices, ULONG Start, + DEBUG_VALUE[] Values); + + HRESULT GetValues2(ULONG Source, ULONG Count, ULONG[] Indices, ULONG Start, + DEBUG_VALUE[] Values); + + HRESULT SetValues2(ULONG Source, ULONG Count, ULONG[] Indices, ULONG Start, + DEBUG_VALUE[] Values); + + HRESULT OutputRegisters2(ULONG OutputControl, ULONG Source, ULONG Flags); + + HRESULT GetInstructionOffset2(ULONG Source, ULONGLONGByReference Offset); + + HRESULT GetStackOffset2(ULONG Source, ULONGLONGByReference Offset); + + HRESULT GetFrameOffset2(ULONG Source, ULONGLONGByReference Offset); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/WrapIDebugRegisters.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/WrapIDebugRegisters.java new file mode 100644 index 0000000000..74543c7291 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/WrapIDebugRegisters.java @@ -0,0 +1,94 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.registers; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_REGISTER_DESCRIPTION; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; + +public class WrapIDebugRegisters extends UnknownWithUtils implements IDebugRegisters { + public static class ByReference extends WrapIDebugRegisters implements Structure.ByReference { + } + + public WrapIDebugRegisters() { + } + + public WrapIDebugRegisters(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetNumberRegisters(ULONGByReference Number) { + return _invokeHR(VTIndices.GET_NUMBER_REGISTERS, getPointer(), Number); + } + + @Override + public HRESULT GetDescription(ULONG Register, byte[] NameBuffer, ULONG NameBufferSize, + ULONGByReference NameSize, DEBUG_REGISTER_DESCRIPTION.ByReference Desc) { + return _invokeHR(VTIndices.GET_DESCRIPTION, getPointer(), Register, NameBuffer, + NameBufferSize, NameSize, Desc); + } + + @Override + public HRESULT GetIndexByName(String Name, ULONGByReference Index) { + return _invokeHR(VTIndices.GET_INDEX_BY_NAME, getPointer(), Name, Index); + } + + @Override + public HRESULT GetValue(ULONG Register, DEBUG_VALUE.ByReference Value) { + return _invokeHR(VTIndices.GET_VALUE, getPointer(), Register, Value); + } + + @Override + public HRESULT SetValue(ULONG Register, DEBUG_VALUE.ByReference Value) { + return _invokeHR(VTIndices.SET_VALUE, getPointer(), Register, Value); + } + + @Override + public HRESULT GetValues(ULONG Count, ULONG[] Indices, ULONG Start, DEBUG_VALUE[] Values) { + return _invokeHR(VTIndices.GET_VALUES, getPointer(), Count, Indices, Start, Values); + } + + @Override + public HRESULT SetValues(ULONG Count, ULONG[] Indices, ULONG Start, DEBUG_VALUE[] Values) { + return _invokeHR(VTIndices.SET_VALUES, getPointer(), Count, Indices, Start, Values); + } + + @Override + public HRESULT OutputRegisters(ULONG OutputControl, ULONG Flags) { + return _invokeHR(VTIndices.OUTPUT_REGISTERS, getPointer(), OutputControl, Flags); + } + + @Override + public HRESULT GetInstructionOffset(ULONGLONGByReference Offset) { + return _invokeHR(VTIndices.GET_INSTRUCTION_OFFSET, getPointer(), Offset); + } + + @Override + public HRESULT GetStackOffset(ULONGLONGByReference Offset) { + return _invokeHR(VTIndices.GET_STACK_OFFSET, getPointer(), Offset); + } + + @Override + public HRESULT GetFrameOffset(ULONGLONGByReference Offset) { + return _invokeHR(VTIndices.GET_FRAME_OFFSET, getPointer(), Offset); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/WrapIDebugRegisters2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/WrapIDebugRegisters2.java new file mode 100644 index 0000000000..ea414f067b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/registers/WrapIDebugRegisters2.java @@ -0,0 +1,124 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.registers; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_REGISTER_DESCRIPTION; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; + +public class WrapIDebugRegisters2 extends WrapIDebugRegisters implements IDebugRegisters2 { + public static class ByReference extends WrapIDebugRegisters2 implements Structure.ByReference { + } + + public WrapIDebugRegisters2() { + } + + public WrapIDebugRegisters2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetDescriptionWide(ULONG Register, char[] NameBuffer, ULONG NameBufferSize, + ULONGByReference NameSize, DEBUG_REGISTER_DESCRIPTION.ByReference Desc) { + return _invokeHR(VTIndices2.GET_DESCRIPTION_WIDE, getPointer(), Register, NameBuffer, + NameBufferSize, NameSize, Desc); + } + + @Override + public HRESULT GetIndexByNameWide(WString Name, ULONGByReference Index) { + return _invokeHR(VTIndices2.GET_INDEX_BY_NAME_WIDE, getPointer(), Name, Index); + } + + @Override + public HRESULT GetNumberPseudoRegisters(ULONGByReference Number) { + return _invokeHR(VTIndices2.GET_NUMBER_PSEUDO_REGISTERS, getPointer(), Number); + } + + @Override + public HRESULT GetPseudoDescription(ULONG Register, byte[] NameBuffer, ULONG NameBufferSize, + ULONGByReference NameSize, ULONGLONGByReference TypeModule, ULONGByReference TypeId) { + return _invokeHR(VTIndices2.GET_PSEUDO_DESCRIPTION, getPointer(), Register, NameBuffer, + NameBufferSize, NameSize, TypeModule, TypeId); + } + + @Override + public HRESULT GetPseudoDescriptionWide(ULONG Register, char[] NameBuffer, ULONG NameBufferSize, + ULONGByReference NameSize, ULONGLONGByReference TypeModule, ULONGByReference TypeId) { + return _invokeHR(VTIndices2.GET_PSEUDO_DESCRIPTION_WIDE, getPointer(), Register, NameBuffer, + NameBufferSize, NameSize, TypeModule, TypeId); + } + + @Override + public HRESULT GetPseudoIndexByName(String Name, ULONGByReference Index) { + return _invokeHR(VTIndices2.GET_PSEUDO_INDEX_BY_NAME, getPointer(), Name, Index); + } + + @Override + public HRESULT GetPseudoIndexByNameWide(WString Name, ULONGByReference Index) { + return _invokeHR(VTIndices2.GET_PSEUDO_INDEX_BY_NAME_WIDE, getPointer(), Name, Index); + } + + @Override + public HRESULT GetPseudoValues(ULONG Source, ULONG Count, ULONG[] Indices, ULONG Start, + DEBUG_VALUE[] Values) { + return _invokeHR(VTIndices2.GET_PSEUDO_VALUES, getPointer(), Source, Count, Indices, Start, + Values); + } + + @Override + public HRESULT SetPseudoValues(ULONG Source, ULONG Count, ULONG[] Indices, ULONG Start, + DEBUG_VALUE[] Values) { + return _invokeHR(VTIndices2.SET_PSEUDO_VALUES, getPointer(), Source, Count, Indices, Start, + Values); + } + + @Override + public HRESULT GetValues2(ULONG Source, ULONG Count, ULONG[] Indices, ULONG Start, + DEBUG_VALUE[] Values) { + return _invokeHR(VTIndices2.GET_VALUES2, getPointer(), Source, Count, Indices, Start, + Values); + } + + @Override + public HRESULT SetValues2(ULONG Source, ULONG Count, ULONG[] Indices, ULONG Start, + DEBUG_VALUE[] Values) { + return _invokeHR(VTIndices2.SET_VALUES2, getPointer(), Source, Count, Indices, Start, + Values); + } + + @Override + public HRESULT OutputRegisters2(ULONG OutputControl, ULONG Source, ULONG Flags) { + return _invokeHR(VTIndices2.OUTPUT_REGISTERS2, getPointer(), OutputControl, Source, Flags); + } + + @Override + public HRESULT GetInstructionOffset2(ULONG Source, ULONGLONGByReference Offset) { + return _invokeHR(VTIndices2.GET_INSTRUCTION_OFFSET2, getPointer(), Source, Offset); + } + + @Override + public HRESULT GetStackOffset2(ULONG Source, ULONGLONGByReference Offset) { + return _invokeHR(VTIndices2.GET_STACK_OFFSET2, getPointer(), Source, Offset); + } + + @Override + public HRESULT GetFrameOffset2(ULONG Source, ULONGLONGByReference Offset) { + return _invokeHR(VTIndices2.GET_FRAME_OFFSET2, getPointer(), Source, Offset); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbolGroup.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbolGroup.java new file mode 100644 index 0000000000..e19111c88a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbolGroup.java @@ -0,0 +1,22 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.platform.win32.Guid.IID; + +public interface IDebugSymbolGroup { + final IID IID_IDEBUG_SYMBOL_GROUP = new IID("f2528316-0f1a-4431-aeed-11d096e1e2ab"); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbolGroup2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbolGroup2.java new file mode 100644 index 0000000000..179a5686a4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbolGroup2.java @@ -0,0 +1,22 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.platform.win32.Guid.IID; + +public interface IDebugSymbolGroup2 { + final IID IID_IDEBUG_SYMBOL_GROUP2 = new IID("6a7ccc5f-fb5e-4dcc-b41c-6c20307bccc7"); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols.java new file mode 100644 index 0000000000..491c8bf2fb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols.java @@ -0,0 +1,120 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +import com.sun.jna.platform.win32.COM.IUnknown; + +public interface IDebugSymbols extends IUnknown { + final IID IID_IDEBUG_SYMBOLS = new IID("8c31e98c-983a-48a5-9016-6fe5d667a950"); + + enum VTIndices implements VTableIndex { + GET_SYMBOL_OPTIONS, // + ADD_SYMBOL_OPTIONS, // + REMOVE_SYMBOL_OPTIONS, // + SET_SYMBOL_OPTIONS, // + GET_NAME_BY_OFFSET, // + GET_OFFSET_BY_NAME, // + GET_NEAR_NAME_BY_OFFSET, // + GET_LINE_BY_OFFSET, // + GET_OFFSET_BY_LINE, // + GET_NUMBER_MODULES, // + GET_MODULE_BY_INDEX, // + GET_MODULE_BY_MODULE_NAME, // + GET_MODULE_BY_OFFSET, // + GET_MODULE_NAMES, // + GET_MODULE_PARAMETERS, // + GET_SYMBOL_MODULE, // + GET_TYPE_NAME, // + GET_TYPE_ID, // + GET_TYPE_SIZE, // + GET_FIELD_OFFSET, // + GET_SYMBOL_TYPE_ID, // + GET_OFFSET_TYPE_ID, // + READ_TYPED_DATA_VIRTUAL, // + WRITE_TYPED_DATA_VIRTUAL, // + OUTPUT_TYPED_DATA_VIRTUAL, // + READ_TYPED_DATA_PHYSICAL, // + WRITE_TYPED_DATA_PHYSICAL, // + OUTPUT_TYPED_DATA_PHYSICAL, // + GET_SCOPE, // + SET_SCOPE, // + RESET_SCOPE, // + GET_SCOPE_SYMBOL_GROUP, // + CREATE_SYMBOL_GROUP, // + START_SYMBOL_MATCH, // + GET_NEXT_SYMBOL_MATCH, // + END_SYMBOL_MATCH, // + RELOAD, // + GET_SYMBOL_PATH, // + SET_SYMBOL_PATH, // + APPEND_SYMBOL_PATH, // + GET_IMAGE_PATH, // + SET_IMAGE_PATH, // + APPEND_IMAGE_PATH, // + GET_SOURCE_PATH, // + GET_SOURCE_PATH_ELEMENT, // + SET_SOURCE_PATH, // + APPEND_SOURCE_PATH, // + FIND_SOURCE_FILE, // + GET_SOURCE_FILE_LINE_OFFSETS, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetNumberModules(ULONGByReference Loaded, ULONGByReference Unloaded); + + HRESULT GetModuleByIndex(ULONG Index, ULONGLONGByReference Base); + + HRESULT GetModuleByModuleName(String Name, ULONG StartIndex, ULONGByReference Index, + ULONGLONGByReference Base); + + HRESULT GetModuleByOffset(ULONGLONG Offset, ULONG StartIndex, ULONGByReference Index, + ULONGLONGByReference Base); + + HRESULT GetModuleNames(ULONG Index, ULONGLONG Base, byte[] ImageNameBuffer, + ULONG ImageNameBufferSize, ULONGByReference ImageNameSize, byte[] ModuleNameBuffer, + ULONG ModuleNameBufferSize, ULONGByReference ModuleNameSize, + byte[] LoadedImageNameBuffer, ULONG LoadedImageNameBufferSize, + ULONGByReference LoadedImageNameSize); + + HRESULT StartSymbolMatch(String Pattern, ULONGLONGByReference Handle); + + HRESULT GetNextSymbolMatch(ULONGLONG Handle, byte[] Buffer, ULONG BufferSize, + ULONGByReference MatchSize, ULONGLONGByReference Offset); + + HRESULT EndSymbolMatch(ULONGLONG Handle); + + HRESULT GetSymbolPath(byte[] aBuffer, ULONG value, Object object); + + HRESULT SetSymbolPath(String Path); + + HRESULT GetSymbolOptions(); + + HRESULT SetSymbolOptions(ULONG Options); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols2.java new file mode 100644 index 0000000000..c6808457aa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols2.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugSymbols2 extends IDebugSymbols { + final IID IID_IDEBUG_SYMBOLS2 = new IID("3a707211-afdd-4495-ad4f-56fecdf8163f"); + + enum VTIndices2 implements VTableIndex { + GET_MODULE_VERSION_INFORMATION, // + GET_MODULE_NAME_STRING, // + GET_CONSTANT_NAME, // + GET_FIELD_NAME, // + GET_TYPE_OPTIONS, // + ADD_TYPE_OPTIONS, // + REMOVE_TYPE_OPTIONS, // + SET_TYPE_OPTIONS, // + ; + + static int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetModuleNameString(ULONG Which, ULONG Index, ULONGLONG Base, byte[] Buffer, + ULONG BufferSize, ULONGByReference NameSize); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols3.java new file mode 100644 index 0000000000..f38f0a487d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols3.java @@ -0,0 +1,126 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_MODULE_AND_ID; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_SYMBOL_ENTRY; +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugSymbols3 extends IDebugSymbols2 { + final IID IID_IDEBUG_SYMBOLS3 = new IID("f02fbecc-50ac-4f36-9ad9-c975e8f32ff8"); + + enum VTIndices3 implements VTableIndex { + GET_NAME_BY_OFFSET_WIDE, // + GET_OFFSET_BY_NAME_WIDE, // + GET_NEAR_NAME_BY_OFFSET_WIDE, // + GET_LINE_BY_OFFSET_WIDE, // + GET_OFFSET_BY_LINE_WIDE, // + GET_MODULE_BY_MODULE_NAME_WIDE, // + GET_SYMBOL_MODULE_WIDE, // + GET_TYPED_NAME_WIDE, // + GET_TYPE_ID_WIDE, // + GET_FIELD_OFFSET_WIDE, // + GET_SYMBOL_TYPE_ID_WIDE, // + GET_SCOPE_SYMBOL_GROUP2, // + CREATE_SYMBOL_GROUP2, // + START_SYMBOL_MATCH_WIDE, // + GET_NEXT_SYMBOL_MATCH_WIDE, // + RELOAD_WIDE, // + GET_SYMBOL_PATH_WIDE, // + SET_SYMBOL_PATH_WIDE, // + APPEND_SYMBOL_PATH_WIDE, // + GET_IMAGE_PATH_WIDE, // + SET_IMAGE_PATH_WIDE, // + APPEND_IMAGE_PATH_WIDE, // + GET_SOURCE_PATH_WIDE, // + GET_SOURCE_PATH_ELEMENT_WIDE, // + SET_SOURCE_PATH_WIDE, // + APPEND_SOURCE_PATH_WIDE, // + FIND_SOURCE_FILE_WIDE, // + GET_SOURCE_FILE_LINE_OFFSETS_WIDE, // + GET_MODULE_VERSION_INFORMATION_WIDE, // + GET_MODULE_NAME_STRING_WIDE, // + GET_CONSTANT_NAME_WIDE, // + GET_FIELD_NAME_WIDE, // + IS_MANAGED_MODULE, // + GET_MODULE_BY_MODULE_NAME2, // + GET_MODULE_BY_MODULE_NAME2_WIDE, // + GET_MODULE_BY_OFFSET2, // + ADD_SYNTHETIC_MODULE, // + ADD_SYNTHETIC_MODULE_WIDE, // + REMOVE_SYNTHETIC_MODULE, // + GET_CURRENT_SCOPE_FRAME_INDEX, // + SET_SCOPE_FRAME_BY_INDEX, // + SET_SCOPE_FROM_JIT_DEBUG_INFO, // + SET_SCOPE_FROM_STORED_EVENT, // + OUTPUT_SYMBOL_BY_OFFSET, // + GET_FUNCTION_ENTRY_BY_OFFSET, // + GET_FIELD_TYPE_AND_OFFSET, // + GET_FIELD_TYPE_AND_OFFSET_WIDE, // + ADD_SYNTHETIC_SYMBOL, // + ADD_SYNTHETIC_SYMBOL_WIDE, // + REMOVE_SYNTHETIC_SYMBOL, // + GET_SYMBOL_ENTRIES_BY_OFFSET, // + GET_SYMBOL_ENTRIES_BY_NAME, // + GET_SYMBOL_ENTRIES_BY_NAME_WIDE, // + GET_SYMBOL_ENTRY_BY_TOKEN, // + GET_SYMBOL_ENTRY_INFORMATION, // + GET_SYMBOL_ENTRY_STRING, // + GET_SYMBOL_ENTRY_STRING_WIDE, // + GET_SYMBOL_ENTRY_OFFSET_REGIONS, // + GET_SYMBOL_ENTRY_BY_SYMBOL_ENTRY, // + GET_SOURCE_ENTRIES_BY_OFFSET, // + GET_SOURCE_ENTRIES_BY_LINE, // + GET_SOURCE_ENTRIES_BY_LINE_WIDE, // + GET_SOURCE_ENTRY_STRING, // + GET_SOURCE_ENTRY_STRING_WIDE, // + GET_SOURCE_ENTRY_OFFSET_REGIONS, // + GET_SOURCE_ENTRY_BY_SOURCE_ENTRY, // + ; + + static int start = VTableIndex.follow(VTIndices2.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetModuleByModuleNameWide(WString Name, ULONG StartIndex, ULONGByReference Index, + ULONGLONGByReference Base); + + HRESULT GetModuleNameStringWide(ULONG Which, ULONG Index, ULONGLONG Base, char[] Buffer, + ULONG BufferSize, ULONGByReference NameSize); + + HRESULT GetSymbolEntriesByName(String Symbol, ULONG Flags, DEBUG_MODULE_AND_ID[] Ids, + ULONG IdsCount, ULONGByReference Entries); + + HRESULT GetSymbolEntriesByNameWide(WString Symbol, ULONG Flags, DEBUG_MODULE_AND_ID[] Ids, + ULONG IdsCount, ULONGByReference Entries); + + HRESULT GetSymbolEntryInformation(DEBUG_MODULE_AND_ID Id, DEBUG_SYMBOL_ENTRY.ByReference Info); + + HRESULT GetSymbolEntryString(DEBUG_MODULE_AND_ID Id, ULONG Which, byte[] Buffer, + ULONG BufferSize, ULONGByReference StringSize); + + HRESULT GetSymbolEntryStringWide(DEBUG_MODULE_AND_ID Id, ULONG Which, char[] Buffer, + ULONG BufferSize, ULONGByReference StringSize); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols4.java new file mode 100644 index 0000000000..8d35a5581c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols4.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.platform.win32.Guid.IID; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugSymbols4 extends IDebugSymbols3 { + final IID IID_IDEBUG_SYMBOLS4 = new IID("e391bbd8-9d8c-4418-840b-c006592a1752"); + + enum VTIndices4 implements VTableIndex { + GET_SCOPE_EX, // + SET_SCOPE_EX, // + GET_NAME_BY_INLINE_CONTEXT, // + GET_NAME_BY_INLINE_CONTEXT_WIDE, // + GET_LINE_BY_INLINE_CONTEXT, // + GET_LINE_BY_INLINE_CONTEXT_WIDE, // + OUTPUT_SYMBOL_BY_INLINE_CONTEXT, // + ; + + static int start = VTableIndex.follow(VTIndices3.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols5.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols5.java new file mode 100644 index 0000000000..d43f4ba75b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/IDebugSymbols5.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.platform.win32.Guid.IID; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugSymbols5 extends IDebugSymbols4 { + final IID IID_IDEBUG_SYMBOLS5 = new IID("c65fa83e-1e69-475e-8e0e-b5d79e9cc17e"); + + enum VTIndices5 implements VTableIndex { + GET_CURRENT_SCOPE_FRAME_INDEX_EX, // + SET_SCOPE_FRAME_BY_INDEX_EX, // + ; + + static int start = VTableIndex.follow(VTIndices4.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols.java new file mode 100644 index 0000000000..2fa0ea9a36 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols.java @@ -0,0 +1,108 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils; + +public class WrapIDebugSymbols extends UnknownWithUtils implements IDebugSymbols { + public static class ByReference extends WrapIDebugSymbols implements Structure.ByReference { + } + + public WrapIDebugSymbols() { + } + + public WrapIDebugSymbols(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetNumberModules(ULONGByReference Loaded, ULONGByReference Unloaded) { + return _invokeHR(VTIndices.GET_NUMBER_MODULES, getPointer(), Loaded, Unloaded); + } + + @Override + public HRESULT GetModuleByIndex(ULONG Index, ULONGLONGByReference Base) { + return _invokeHR(VTIndices.GET_MODULE_BY_INDEX, getPointer(), Index, Base); + } + + @Override + public HRESULT GetModuleByModuleName(String Name, ULONG StartIndex, ULONGByReference Index, + ULONGLONGByReference Base) { + return _invokeHR(VTIndices.GET_MODULE_BY_MODULE_NAME, getPointer(), Name, StartIndex, Index, + Base); + } + + @Override + public HRESULT GetModuleByOffset(ULONGLONG Offset, ULONG StartIndex, ULONGByReference Index, + ULONGLONGByReference Base) { + return _invokeHR(VTIndices.GET_MODULE_BY_OFFSET, getPointer(), Offset, StartIndex, Index, + Base); + } + + @Override + public HRESULT GetModuleNames(ULONG Index, ULONGLONG Base, byte[] ImageNameBuffer, + ULONG ImageNameBufferSize, ULONGByReference ImageNameSize, byte[] ModuleNameBuffer, + ULONG ModuleNameBufferSize, ULONGByReference ModuleNameSize, + byte[] LoadedImageNameBuffer, ULONG LoadedImageNameBufferSize, + ULONGByReference LoadedImageNameSize) { + return _invokeHR(VTIndices.GET_MODULE_NAMES, getPointer(), Index, Base, ImageNameBuffer, + ImageNameBufferSize, ImageNameSize, ModuleNameBuffer, ModuleNameBufferSize, + ModuleNameSize, LoadedImageNameBuffer, LoadedImageNameBufferSize, LoadedImageNameSize); + } + + @Override + public HRESULT StartSymbolMatch(String Pattern, ULONGLONGByReference Handle) { + return _invokeHR(VTIndices.START_SYMBOL_MATCH, getPointer(), Pattern, Handle); + } + + @Override + public HRESULT GetNextSymbolMatch(ULONGLONG Handle, byte[] Buffer, ULONG BufferSize, + ULONGByReference MatchSize, ULONGLONGByReference Offset) { + return _invokeHR(VTIndices.GET_NEXT_SYMBOL_MATCH, getPointer(), Handle, Buffer, BufferSize, + MatchSize, Offset); + } + + @Override + public HRESULT EndSymbolMatch(ULONGLONG Handle) { + return _invokeHR(VTIndices.END_SYMBOL_MATCH, getPointer(), Handle); + } + + @Override + public HRESULT GetSymbolPath(byte[] aBuffer, ULONG value, Object object) { + return _invokeHR(VTIndices.GET_SYMBOL_PATH, getPointer(), aBuffer, value, object); + } + + @Override + public HRESULT SetSymbolPath(String Path) { + return _invokeHR(VTIndices.SET_SYMBOL_PATH, getPointer(), Path); + } + + @Override + public HRESULT GetSymbolOptions() { + return _invokeHR(VTIndices.GET_SYMBOL_OPTIONS, getPointer()); + } + + @Override + public HRESULT SetSymbolOptions(ULONG Options) { + return _invokeHR(VTIndices.SET_SYMBOL_OPTIONS, getPointer(), Options); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols2.java new file mode 100644 index 0000000000..f0bbb77223 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols2.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class WrapIDebugSymbols2 extends WrapIDebugSymbols implements IDebugSymbols2 { + public static class ByReference extends WrapIDebugSymbols2 implements Structure.ByReference { + } + + public WrapIDebugSymbols2() { + } + + public WrapIDebugSymbols2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetModuleNameString(ULONG Which, ULONG Index, ULONGLONG Base, byte[] Buffer, + ULONG BufferSize, ULONGByReference NameSize) { + return _invokeHR(VTIndices2.GET_MODULE_NAME_STRING, getPointer(), Which, Index, Base, + Buffer, BufferSize, NameSize); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols3.java new file mode 100644 index 0000000000..aa5fe348cd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols3.java @@ -0,0 +1,83 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_MODULE_AND_ID; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_SYMBOL_ENTRY; + +public class WrapIDebugSymbols3 extends WrapIDebugSymbols2 implements IDebugSymbols3 { + public static class ByReference extends WrapIDebugSymbols3 implements Structure.ByReference { + } + + public WrapIDebugSymbols3() { + } + + public WrapIDebugSymbols3(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetModuleByModuleNameWide(WString Name, ULONG StartIndex, ULONGByReference Index, + ULONGLONGByReference Base) { + return _invokeHR(VTIndices3.GET_MODULE_BY_MODULE_NAME_WIDE, getPointer(), Name, StartIndex, + Index, Base); + } + + @Override + public HRESULT GetModuleNameStringWide(ULONG Which, ULONG Index, ULONGLONG Base, char[] Buffer, + ULONG BufferSize, ULONGByReference NameSize) { + return _invokeHR(VTIndices3.GET_MODULE_NAME_STRING_WIDE, getPointer(), Which, Index, Base, + Buffer, BufferSize, NameSize); + } + + @Override + public HRESULT GetSymbolEntriesByName(String Symbol, ULONG Flags, DEBUG_MODULE_AND_ID[] Ids, + ULONG IdsCount, ULONGByReference Entries) { + return _invokeHR(VTIndices3.GET_SYMBOL_ENTRIES_BY_NAME, getPointer(), Symbol, Flags, Ids, + IdsCount, Entries); + } + + @Override + public HRESULT GetSymbolEntriesByNameWide(WString Symbol, ULONG Flags, + DEBUG_MODULE_AND_ID[] Ids, ULONG IdsCount, ULONGByReference Entries) { + return _invokeHR(VTIndices3.GET_SYMBOL_ENTRIES_BY_NAME_WIDE, getPointer(), Symbol, Flags, + Ids, IdsCount, Entries); + } + + @Override + public HRESULT GetSymbolEntryInformation(DEBUG_MODULE_AND_ID Id, + DEBUG_SYMBOL_ENTRY.ByReference Info) { + return _invokeHR(VTIndices3.GET_SYMBOL_ENTRY_INFORMATION, getPointer(), Id, Info); + } + + @Override + public HRESULT GetSymbolEntryString(DEBUG_MODULE_AND_ID Id, ULONG Which, byte[] Buffer, + ULONG BufferSize, ULONGByReference StringSize) { + return _invokeHR(VTIndices3.GET_SYMBOL_ENTRY_STRING, getPointer(), Id, Which, Buffer, + BufferSize, StringSize); + } + + @Override + public HRESULT GetSymbolEntryStringWide(DEBUG_MODULE_AND_ID Id, ULONG Which, char[] Buffer, + ULONG BufferSize, ULONGByReference StringSize) { + return _invokeHR(VTIndices3.GET_SYMBOL_ENTRY_STRING_WIDE, getPointer(), Id, Which, Buffer, + BufferSize, StringSize); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols4.java new file mode 100644 index 0000000000..c820f7d346 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols4.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public class WrapIDebugSymbols4 extends WrapIDebugSymbols3 implements IDebugSymbols4 { + public static class ByReference extends WrapIDebugSymbols4 implements Structure.ByReference { + } + + public WrapIDebugSymbols4() { + } + + public WrapIDebugSymbols4(Pointer pvInstance) { + super(pvInstance); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols5.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols5.java new file mode 100644 index 0000000000..21d78a5087 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/symbols/WrapIDebugSymbols5.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.symbols; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public class WrapIDebugSymbols5 extends WrapIDebugSymbols4 implements IDebugSymbols5 { + public static class ByReference extends WrapIDebugSymbols5 implements Structure.ByReference { + } + + public WrapIDebugSymbols5() { + } + + public WrapIDebugSymbols5(Pointer pvInstance) { + super(pvInstance); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects.java new file mode 100644 index 0000000000..9cb2940f16 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects.java @@ -0,0 +1,101 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.sysobj; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.IUnknown; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugSystemObjects extends IUnknown { + final IID IID_IDEBUG_SYSTEM_OBJECTS = new IID("6b86fe2c-2c4f-4f0c-9da2-174311acc327"); + + enum VTIndices implements VTableIndex { + GET_EVENT_THREAD, // + GET_EVENT_PROCESS, // + GET_CURRENT_THREAD_ID, // + SET_CURRENT_THREAD_ID, // + GET_CURRENT_PROCESS_ID, // + SET_CURRENT_PROCESS_ID, // + GET_NUMBER_THREADS, // + GET_TOTAL_NUMBER_THREADS, // + GET_THREAD_IDS_BY_INDEX, // + GET_THREAD_ID_BY_PROCESSOR, // + GET_CURRENT_THREAD_DATA_OFFSET, // + GET_THREAD_ID_BY_DATA_OFFSET, // + GET_CURRENT_THREAD_TEB, // + GET_THREAD_ID_BY_TEB, // + GET_CURRENT_THREAD_SYSTEM_ID, // + GET_THREAD_ID_BY_SYSTEM_ID, // + GET_CURRENT_THREAD_HANDLE, // + GET_THREAD_ID_BY_HANDLE, // + GET_NUMBER_PROCESSES, // + GET_PROCESS_IDS_BY_INDEX, // + GET_CURRENT_PROCESS_DATA_OFFSET, // + GET_PROCESS_ID_BY_DATA_OFFSET, // + GET_CURRENT_PROCESS_PEB, // + GET_PROCESS_ID_BY_PEB, // + GET_CURRENT_PROCESS_SYSTEM_ID, // + GET_PROCESS_ID_BY_SYSTEM_ID, // + GET_CURRENT_PROCESS_HANDLE, // + GET_PROCESS_ID_BY_HANDLE, // + GET_CURRENT_PROCESS_EXECUTABLE_NAME, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetEventThread(ULONGByReference Id); + + HRESULT GetEventProcess(ULONGByReference Id); + + HRESULT GetCurrentThreadId(ULONGByReference Id); + + HRESULT SetCurrentThreadId(ULONG Id); + + HRESULT GetCurrentProcessId(ULONGByReference Id); + + HRESULT SetCurrentProcessId(ULONG Id); + + HRESULT GetNumberThreads(ULONGByReference Number); + + HRESULT GetTotalNumberThreads(ULONGByReference Total, ULONGByReference LargestProcess); + + HRESULT GetThreadIdsByIndex(ULONG Start, ULONG Count, ULONG[] Ids, ULONG[] SysIds); + + HRESULT GetThreadIdByHandle(ULONGLONG Handle, ULONGByReference Id); + + HRESULT GetNumberProcesses(ULONGByReference Number); + + HRESULT GetProcessIdsByIndex(ULONG Start, ULONG Count, ULONG[] Ids, ULONG[] SysIds); + + HRESULT GetProcessIdByHandle(ULONGLONG Handle, ULONGByReference Id); + + HRESULT GetCurrentThreadSystemId(ULONGByReference SysId); + + HRESULT GetCurrentProcessSystemId(ULONGByReference SysId); + + HRESULT GetThreadIdBySystemId(ULONG SystemId, ULONGByReference Id); + + HRESULT GetProcessIdBySystemId(ULONG SystemId, ULONGByReference Id); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects2.java new file mode 100644 index 0000000000..2a1fdfe3c8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects2.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.sysobj; + +import com.sun.jna.platform.win32.Guid.IID; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugSystemObjects2 extends IDebugSystemObjects { + final IID IID_IDEBUG_SYSTEM_OBJECTS2 = new IID("0ae9f5ff-1852-4679-b055-494bee6407ee"); + + enum VTIndices2 implements VTableIndex { + GET_CURRENT_PROCESS_UP_TIME, // + GET_IMPLICIT_THREAD_DATA_OFFSET, // + SET_IMPLICIT_THREAD_DATA_OFFSET, // + GET_IMPLICIT_PROCESS_DATA_OFFSET, // + SET_IMPLICIT_PROCESS_DATA_OFFSET, // + ; + + static int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects3.java new file mode 100644 index 0000000000..971584b399 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects3.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.sysobj; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugSystemObjects3 extends IDebugSystemObjects2 { + final IID IID_IDEBUG_SYSTEM_OBJECTS3 = new IID("e9676e2f-e286-4ea3-b0f9-dfe5d9fc330e"); + + enum VTIndices3 implements VTableIndex { + GET_EVENT_SYSTEM, // + GET_CURRENT_SYSTEM_ID, // + SET_CURRENT_SYSTEM_ID, // + GET_NUMBER_SYSTEMS, // + GET_SYSTEM_IDS_BY_INDEX, // + GET_TOTAL_NUMBER_THREADS_AND_PROCESSES, // + GET_CURRENT_SYSTEM_SERER, // + GET_SYSTEM_BY_SERVER, // + GET_CURRENT_SYSTEM_SERVER_NAME, // + ; + + static int start = VTableIndex.follow(VTIndices2.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetEventSystem(ULONGByReference Id); + + HRESULT GetCurrentSystemId(ULONGByReference Id); + + HRESULT SetCurrentSystemId(ULONG Id); + + HRESULT GetNumberSystems(ULONGByReference Number); + + HRESULT GetSystemIdsByIndex(ULONG Start, ULONG Count, ULONG[] Ids, ULONG[] SysIds); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects4.java new file mode 100644 index 0000000000..d4d0212259 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/IDebugSystemObjects4.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.sysobj; + +import com.sun.jna.platform.win32.Guid.IID; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; + +public interface IDebugSystemObjects4 extends IDebugSystemObjects3 { + final IID IID_IDEBUG_SYSTEM_OBJECTS4 = new IID("489468e6-7d0f-4af5-87ab-25207454d553"); + + enum VTIndices4 implements VTableIndex { + GET_CURRENT_PROCESS_EXECUTABLE_NAME_WIDE, // + GET_CURRENT_SYSTEM_SERVER_NAME_WIDE, // + ; + + static int start = VTableIndex.follow(VTIndices3.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects.java new file mode 100644 index 0000000000..a550266dac --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects.java @@ -0,0 +1,123 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.sysobj; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgeng.jna.dbgeng.UnknownWithUtils; + +public class WrapIDebugSystemObjects extends UnknownWithUtils implements IDebugSystemObjects { + public static class ByReference extends WrapIDebugSystemObjects + implements Structure.ByReference { + } + + public WrapIDebugSystemObjects() { + } + + public WrapIDebugSystemObjects(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetEventThread(ULONGByReference Id) { + return _invokeHR(VTIndices.GET_EVENT_THREAD, getPointer(), Id); + } + + @Override + public HRESULT GetEventProcess(ULONGByReference Id) { + return _invokeHR(VTIndices.GET_EVENT_PROCESS, getPointer(), Id); + } + + @Override + public HRESULT GetCurrentThreadId(ULONGByReference Id) { + return _invokeHR(VTIndices.GET_CURRENT_THREAD_ID, getPointer(), Id); + } + + @Override + public HRESULT SetCurrentThreadId(ULONG Id) { + return _invokeHR(VTIndices.SET_CURRENT_THREAD_ID, getPointer(), Id); + } + + @Override + public HRESULT GetCurrentProcessId(ULONGByReference Id) { + return _invokeHR(VTIndices.GET_CURRENT_PROCESS_ID, getPointer(), Id); + } + + @Override + public HRESULT SetCurrentProcessId(ULONG Id) { + return _invokeHR(VTIndices.SET_CURRENT_PROCESS_ID, getPointer(), Id); + } + + @Override + public HRESULT GetNumberThreads(ULONGByReference Number) { + return _invokeHR(VTIndices.GET_NUMBER_THREADS, getPointer(), Number); + } + + @Override + public HRESULT GetTotalNumberThreads(ULONGByReference Total, ULONGByReference LargestProcess) { + return _invokeHR(VTIndices.GET_TOTAL_NUMBER_THREADS, getPointer(), Total, LargestProcess); + } + + @Override + public HRESULT GetThreadIdsByIndex(ULONG Start, ULONG Count, ULONG[] Ids, ULONG[] SysIds) { + return _invokeHR(VTIndices.GET_THREAD_IDS_BY_INDEX, getPointer(), Start, Count, Ids, + SysIds); + } + + @Override + public HRESULT GetThreadIdByHandle(ULONGLONG Handle, ULONGByReference Id) { + return _invokeHR(VTIndices.GET_THREAD_ID_BY_HANDLE, getPointer(), Handle, Id); + } + + @Override + public HRESULT GetThreadIdBySystemId(ULONG SystemId, ULONGByReference Id) { + return _invokeHR(VTIndices.GET_THREAD_ID_BY_SYSTEM_ID, getPointer(), SystemId, Id); + } + + @Override + public HRESULT GetProcessIdBySystemId(ULONG SystemId, ULONGByReference Id) { + return _invokeHR(VTIndices.GET_PROCESS_ID_BY_SYSTEM_ID, getPointer(), SystemId, Id); + } + + @Override + public HRESULT GetNumberProcesses(ULONGByReference Number) { + return _invokeHR(VTIndices.GET_NUMBER_PROCESSES, getPointer(), Number); + } + + @Override + public HRESULT GetProcessIdsByIndex(ULONG Start, ULONG Count, ULONG[] Ids, ULONG[] SysIds) { + return _invokeHR(VTIndices.GET_PROCESS_IDS_BY_INDEX, getPointer(), Start, Count, Ids, + SysIds); + } + + @Override + public HRESULT GetProcessIdByHandle(ULONGLONG Handle, ULONGByReference Id) { + return _invokeHR(VTIndices.GET_PROCESS_ID_BY_HANDLE, getPointer(), Handle, Id); + } + + @Override + public HRESULT GetCurrentThreadSystemId(ULONGByReference SysId) { + return _invokeHR(VTIndices.GET_CURRENT_THREAD_SYSTEM_ID, getPointer(), SysId); + } + + @Override + public HRESULT GetCurrentProcessSystemId(ULONGByReference SysId) { + return _invokeHR(VTIndices.GET_CURRENT_PROCESS_SYSTEM_ID, getPointer(), SysId); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects2.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects2.java new file mode 100644 index 0000000000..163bfaeadb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects2.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.sysobj; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public class WrapIDebugSystemObjects2 extends WrapIDebugSystemObjects + implements IDebugSystemObjects2 { + public static class ByReference extends WrapIDebugSystemObjects2 + implements Structure.ByReference { + } + + public WrapIDebugSystemObjects2() { + } + + public WrapIDebugSystemObjects2(Pointer pvInstance) { + super(pvInstance); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects3.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects3.java new file mode 100644 index 0000000000..daea46df9e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects3.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.sysobj; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class WrapIDebugSystemObjects3 extends WrapIDebugSystemObjects2 + implements IDebugSystemObjects3 { + public static class ByReference extends WrapIDebugSystemObjects3 + implements Structure.ByReference { + } + + public WrapIDebugSystemObjects3() { + } + + public WrapIDebugSystemObjects3(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetEventSystem(ULONGByReference Id) { + return _invokeHR(VTIndices3.GET_EVENT_SYSTEM, getPointer(), Id); + } + + @Override + public HRESULT GetCurrentSystemId(ULONGByReference Id) { + return _invokeHR(VTIndices3.GET_CURRENT_SYSTEM_ID, getPointer(), Id); + } + + @Override + public HRESULT SetCurrentSystemId(ULONG Id) { + return _invokeHR(VTIndices3.SET_CURRENT_SYSTEM_ID, getPointer(), Id); + } + + @Override + public HRESULT GetNumberSystems(ULONGByReference Number) { + return _invokeHR(VTIndices3.GET_NUMBER_SYSTEMS, getPointer(), Number); + } + + @Override + public HRESULT GetSystemIdsByIndex(ULONG Start, ULONG Count, ULONG[] Ids, ULONG[] SysIds) { + return _invokeHR(VTIndices3.GET_SYSTEM_IDS_BY_INDEX, getPointer(), Start, Count, Ids, + SysIds); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects4.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects4.java new file mode 100644 index 0000000000..8d760e6fea --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/sysobj/WrapIDebugSystemObjects4.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.dbgeng.sysobj; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public class WrapIDebugSystemObjects4 extends WrapIDebugSystemObjects3 + implements IDebugSystemObjects4 { + public static class ByReference extends WrapIDebugSystemObjects4 + implements Structure.ByReference { + } + + public WrapIDebugSystemObjects4() { + } + + public WrapIDebugSystemObjects4(Pointer pvInstance) { + super(pvInstance); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/javaprovider/JavaProviderNative.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/javaprovider/JavaProviderNative.java new file mode 100644 index 0000000000..024df0908e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/javaprovider/JavaProviderNative.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.jna.javaprovider; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public interface JavaProviderNative extends Library { + JavaProviderNative INSTANCE = Native.loadLibrary("javaprovider", JavaProviderNative.class); + + HRESULT createClient(Pointer client); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgCause.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgCause.java new file mode 100644 index 0000000000..37fbf0954f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgCause.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import agent.dbgeng.manager.cmd.DbgPendingCommand; + +/** + * Identifies the cause of an event emitted by dbgeng + * + * This is not a concept native to dbgeng. Rather, it is a means to distinguish events that result + * from commands issued by the {@link DbgManager} from those issued by the user or some other means. + * For example, a call to {@link DbgManager#addProcess()} will emit a + * {@link DbgEventsListener#processAdded(DbgProcess, DbgCause)} event, identifying the + * {@link DbgPendingCommand} as the cause. + */ +public interface DbgCause { + public enum Causes implements DbgCause { + UNCLAIMED; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgCommand.java new file mode 100644 index 0000000000..7d9421cce8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgCommand.java @@ -0,0 +1,79 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.cmd.AbstractDbgCommand; +import agent.dbgeng.manager.cmd.DbgPendingCommand; + +/** + * The interface for Dbg command implementations + * + * Commands are executed by Dbg in serial. In order to distinguish the likely cause of events, the + * manager will wait to issue each command until it has seen a prompt. Thus, commands are queued up, + * and the manager uses the {@link CompletableFuture} pattern to "return" results after execution + * completes. Once issued, a command is presumed to be executing until another prompt is received. + * This generally immediately follows the command result, i.e., the "^..." result line in Dbg/MI. + * The command implementation is responsible for handling the command result. Implementors ought to + * use {@link AbstractDbgCommand} to ensure consistent processing. + * + * To begin executing the command, the manager invokes the command, using {@link #invoke()}. Any + * event that occurs during command execution is given to + * {@link #handle(DbgEvent, DbgPendingCommand)}. The implementor then has the option to "claim" or + * "steal" the event. When claimed, any subsequent event processor or listener is provided this + * command as the event's cause. When stolen, no subsequent event processors are called. The + * implementation ought to keep a list of claimed and stolen events. Once Dbg has finished executing + * the command, the manager calls {@link #complete(DbgPendingCommand)}, allowing the implementation + * to process its claimed and stolen events and return the result of the command. + * + * @param the type of object "returned" by the command + */ +public interface DbgCommand { + + /** + * Check if this command can be executed given Dbg's current state + * + * @param state Dbg's state + * @return true if it can be executed, false otherwise + */ + public boolean validInState(DbgState state); + + /** + * Invoke the command + */ + public void invoke(); + + /** + * Handle an event that ocurred during the execution of this command + * + * @param evt the event + * @param pending a copy of the executing command instance + * @return true if the command is now ready to be completed + */ + public boolean handle(DbgEvent evt, DbgPendingCommand pending); + + /** + * Called when the manager believes this command is finished executing + * + * This is presumed when the manager receives the prompt after issuing the encoded command + * + * @param pending a copy of the now-finished-executing command instance + * @return the object "returned" by the command + */ + public T complete(DbgPendingCommand pending); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEvent.java new file mode 100644 index 0000000000..f02114b727 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEvent.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import agent.dbgeng.manager.cmd.DbgPendingCommand; + +public interface DbgEvent { + /** + * Get the information detailing the event + * + * @return the information + */ + public T getInfo(); + + /** + * Use {@link DbgPendingCommand#claim(DbgEvent)} instead + * + * @param cause the cause + */ + public void claim(DbgPendingCommand cause); + + /** + * If claimed, get the cause of this event + * + * @return the cause + */ + public DbgCause getCause(); + + /** + * Use {@link DbgPendingCommand#steal(DbgEvent)} instead + */ + public void steal(); + + /** + * Check if this event is stolen + * + * A stolen event should not be processed further, except by the thief + * + * @return true if stolen, false otherwise + */ + public boolean isStolen(); + + /** + * If this event implies a new dbgeng state, get that state + * + * @return the new state, or null for no change + */ + public DbgState newState(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventHandler.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventHandler.java new file mode 100644 index 0000000000..2a75ed4f14 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventHandler.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import agent.dbgeng.manager.cmd.DbgPendingCommand; + +public interface DbgEventHandler { + /** + * Get the information detailing the event + * + * @return the information + */ + public T getInfo(); + + /** + * Use {@link DbgPendingCommand#claim(DbgEventHandler)} instead + * + * @param cause the cause + */ + public void claim(DbgPendingCommand cause); + + /** + * If claimed, get the cause of this event + * + * @return the cause + */ + public DbgCause getCause(); + + /** + * Use {@link DbgPendingCommand#steal(DbgEventHandler)} instead + */ + public void steal(); + + /** + * Check if this event is stolen + * + * A stolen event should not be processed further, except by the thief + * + * @return true if stolen, false otherwise + */ + public boolean isStolen(); + + /** + * If this event implies a new dbgeng state, get that state + * + * @return the new state, or null for no change + */ + public DbgState newState(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java new file mode 100644 index 0000000000..9064c41eca --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java @@ -0,0 +1,247 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; + +public interface DbgEventsListener { + + /** + * A session has been added + * + * @param session a handle to the new session + * @param cause the cause of this event + */ + void sessionAdded(DbgSession session, DbgCause cause); + + /** + * A session has been removed + * + * @param sessionId the ID of the now-defunct session + * @param cause the cause of this event + */ + void sessionRemoved(DebugSessionId sessionId, DbgCause cause); + + /** + * A different session has been selected (gained focus) + * + * @param session a handle to the selected session + * @param cause the cause of this event + */ + void sessionSelected(DbgSession session, DbgCause cause); + + /** + * An Process has been added to the session + * + * @param process a handle to the new process + * @param cause the cause of this event + */ + void processAdded(DbgProcess process, DbgCause cause); + + /** + * An process has been removed from the session + * + * @param processId the ID of the now-defunct process + * @param cause the cause of this event + */ + void processRemoved(DebugProcessId processId, DbgCause cause); + + /** + * A different process has been selected (gained focus) + * + * @param process a handle to the selected process + * @param cause the cause of this event + */ + void processSelected(DbgProcess process, DbgCause cause); + + /** + * Execution has been started in an process + * + * @param process a handle to the now-executing process + * @param cause the cause of this event + */ + void processStarted(DbgProcess process, DbgCause cause); + + /** + * Execution has terminated in an process + * + * @param process a handle to the now-stopped process + * @param cause the cause of this event + */ + void processExited(DbgProcess process, DbgCause cause); + + /** + * A thread has been created + * + * Use {@link DbgThread#getProcess()} to get a handle to the process in which the thread was + * created. + * + * @param thread a handle to the new thread + * @param cause the cause of this event + */ + void threadCreated(DbgThread thread, DbgCause cause); + + /** + * A thread's state has changed, e.g., {@link DbgState#RUNNING} to {@link DbgState#STOPPED} + * + * @param thread a handle to the thread whose state has changed + * @param state the state to which the thread changed + * @param cause the cause of this event + * @param reason the reason for the state change + */ + void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause, DbgReason reason); + + /** + * A thread has exited + * + * @param threadId the ID of the now-defuct thread + * @param process a handle to the process to which the thread belonged + * @param cause the cause of this event + */ + void threadExited(DebugThreadId threadId, DbgProcess process, DbgCause cause); + + /** + * A different thread has been selected (gained focus) + * + * @param thread a handle to the selected thread + * @param frame a handle to the current frame + * @param cause the cause of this event + */ + void threadSelected(DbgThread thread, DbgStackFrame frame, DbgCause cause); + + /** + * A module has been loaded by an process + * + * @param process a handle to the process which loaded the module + * @param name the name of the module on the target + * @param cause the cause of this event + */ + void moduleLoaded(DbgProcess process, String name, DbgCause cause); + + /** + * A module has been unloaded from an process + * + * @param process a handle to the process which unloaded the module + * @param name the name of the module on the target + * @param cause the cause of this event + */ + void moduleUnloaded(DbgProcess process, String name, DbgCause cause); + + /** + * A breakpoint has been created in the session + * + * @param info information about the new breakpoint + * @param cause the cause of this event + */ + void breakpointCreated(DbgBreakpointInfo info, DbgCause cause); + + /** + * A breakpoint in the session has been modified + * + * @param newInfo new information about the modified breakpoint + * @param oldInfo old information about the modified breakpoint + * @param cause the cause of this event + */ + void breakpointModified(DbgBreakpointInfo newInfo, DbgBreakpointInfo oldInfo, DbgCause cause); + + /** + * A breakpoint has been deleted from the session + * + * @param info information about the now-deleted breakpoint + * @param cause the cause of this event + */ + void breakpointDeleted(DbgBreakpointInfo info, DbgCause cause); + + /** + * A breakpoint was hit in the session + * + * @param info information about the breakpoint hit + * @param cause the cause of this event + */ + void breakpointHit(DbgBreakpointInfo info, DbgCause cause); + + /** + * A breakpoint has effectively been applied to an process + * + * dbgeng has a robust (read "complicated") breakpoint model. A breakpoint may apply to multiple + * processes, and even within a single process, it may have multiple locations. Consider, e.g., + * an inlined function or a C++ template function. If the breakpoint is specified as a line + * number, it may correspond to several locations in the compiled binary. Worse yet, a + * breakpoint may be pending, meaning its specification is ambiguous from not having a target + * file. Once a file is loaded, which may be an program, or a shared library loaded during the + * execution of a program, that matches the specification, the breakpoint is resolved to one or + * more locations. Even worse yet, if another library gets loaded that also matches the + * specification, new locations may be appended. + * + * Thus, the dbgeng manager attempts to interpret the information about a breakpoint provided by + * dbgeng and builds a set of "effective breakpoints" each corresponding to a single location in + * a single process image, i.e., process. Consider for example: A new library is loaded and an + * existing dbgeng breakpoint must be applied within. dbgeng will emit a breakpoint modified + * event, which the manager will parse and pass to + * {@link #breakpointModified(DbgBreakpointInfo, DbgBreakpointInfo, DbgCause)}. The manager will + * also interpret that event and, seeing a new location, emit an + * {@link #effectiveBreakpointCreated(DbgProcess, DbgEffectiveBreakpoint, DbgCause)} event. + * + * @param process a handle to the process to which the breakpoint has been applied + * @param newBkpt information about the effective breakpoint + * @param cause the cause of this event + */ + //void effectiveBreakpointCreated(DbgProcess process, DbgEffectiveBreakpoint newBkpt, + // DbgCause cause); + + /** + * An effective breakpoint has been modified within an process + * + * @param process a handle to the process to which the modified effective breakpoint applies + * @param newBkpt information about the new effective breakpoint + * @param oldBkpt information about the old effective breakpoint + * @param cause the cause of this event + * @see #effectiveBreakpointCreated(DbgProcess, DbgEffectiveBreakpoint, DbgCause) + */ + //void effectiveBreakpointModified(DbgProcess process, DbgEffectiveBreakpoint newBkpt, + // DbgEffectiveBreakpoint oldBkpt, DbgCause cause); + + /** + * An effective breakpoint has been deleted from an process + * + * @param process a handle to the process from which the effective breakpoint was deleted + * @param oldBkpt information about the now-deleted effective breakpoint + * @param cause the cause of this event + * @see #effectiveBreakpointCreated(DbgProcess, DbgEffectiveBreakpoint, DbgCause) + */ + //void effectiveBreakpointDeleted(DbgProcess process, DbgEffectiveBreakpoint oldBkpt, + // DbgCause cause); + + /** + * TODO: This is not yet implemented + * + * It is not clear whether dbgeng detects when a target writes into its own memory, or if this + * event is emitted when dbgeng changes the target's memory, or both. + * + * @param process the process whose memory changed + * @param addr the address of the change + * @param len the length, with the address, bounding the region of change + * @param cause the cause of this event + */ + void memoryChanged(DbgProcess process, long addr, int len, DbgCause cause); + + /** + * @param output console output + * @param mask class of output + */ + void consoleOutput(String output, int mask); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java new file mode 100644 index 0000000000..dacfad1b72 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java @@ -0,0 +1,145 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; + +public interface DbgEventsListenerAdapter extends DbgEventsListener { + + @Override + public default void sessionAdded(DbgSession session, DbgCause cause) { + // Extension point + } + + @Override + public default void sessionRemoved(DebugSessionId sessionId, DbgCause cause) { + // Extension point + } + + @Override + public default void sessionSelected(DbgSession session, DbgCause cause) { + // Extension point + } + + @Override + public default void processAdded(DbgProcess process, DbgCause cause) { + // Extension point + } + + @Override + public default void processRemoved(DebugProcessId processId, DbgCause cause) { + // Extension point + } + + @Override + public default void processSelected(DbgProcess process, DbgCause cause) { + // Extension point + } + + @Override + public default void processStarted(DbgProcess process, DbgCause cause) { + // Extension point + } + + @Override + public default void processExited(DbgProcess process, DbgCause cause) { + // Extension point + } + + @Override + public default void threadCreated(DbgThread thread, DbgCause cause) { + // Extension point + } + + @Override + public default void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause, + DbgReason reason) { + // Extension point + } + + @Override + public default void threadExited(DebugThreadId threadId, DbgProcess process, DbgCause cause) { + // Extension point + + } + + @Override + public default void threadSelected(DbgThread thread, DbgStackFrame frame, DbgCause cause) { + // Extension point + } + + @Override + public default void moduleLoaded(DbgProcess process, String name, DbgCause cause) { + // Extension point + } + + @Override + public default void moduleUnloaded(DbgProcess process, String name, DbgCause cause) { + // Extension point + } + + @Override + public default void breakpointCreated(DbgBreakpointInfo info, DbgCause cause) { + // Extension point + } + + @Override + public default void breakpointModified(DbgBreakpointInfo newInfo, DbgBreakpointInfo oldInfo, + DbgCause cause) { + // Extension point + } + + @Override + public default void breakpointDeleted(DbgBreakpointInfo info, DbgCause cause) { + // Extension point + } + + @Override + public default void breakpointHit(DbgBreakpointInfo info, DbgCause cause) { + // Extension point + } + + /* + @Override + public default void effectiveBreakpointCreated(DbgProcess process, + DbgEffectiveBreakpoint newBkpt, DbgCause cause) { + // Extension point + } + + @Override + public default void effectiveBreakpointModified(DbgProcess process, + DbgEffectiveBreakpoint newBkpt, DbgEffectiveBreakpoint oldBkpt, DbgCause cause) { + // Extension point + } + + @Override + public default void effectiveBreakpointDeleted(DbgProcess process, + DbgEffectiveBreakpoint oldBkpt, DbgCause cause) { + // Extension point + } + */ + + @Override + public default void memoryChanged(DbgProcess process, long addr, int len, DbgCause cause) { + // Extension point + } + + @Override + public default void consoleOutput(String output, int mask) { + // Extension point + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgManager.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgManager.java new file mode 100644 index 0000000000..fab30f2456 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgManager.java @@ -0,0 +1,351 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.tuple.Pair; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInsertions; +import agent.dbgeng.manager.impl.*; + +public interface DbgManager extends AutoCloseable, DbgBreakpointInsertions { + + /** + * Possible values for {@link DbgThread#step(ExecSuffix)} + */ + public enum ExecSuffix { + /** Equivalent to {@code finish} in the CLI */ + FINISH("finish"), + /** Equivalent to {@code next} in the CLI */ + NEXT("next"), + /** Equivalent to {@code nexti} in the CLI */ + NEXT_INSTRUCTION("next-instruction"), + /** Equivalent to {@code return} in the CLI */ + RETURN("return"), + /** Equivalent to {@code step} in the CLI */ + STEP("step"), + /** Equivalent to {@code stepi} in the CLI */ + STEP_INSTRUCTION("step-instruction"), + /** Equivalent to {@code until} in the CLI */ + UNTIL("until"),; + + final String str; + + ExecSuffix(String str) { + this.str = str; + } + + @Override + public String toString() { + return str; + } + } + + static DbgManager newInstance() { + //return new DbgManagerModelImpl(); + return new DbgManagerImpl(); + } + + /** + * Launch dbgeng + * + * @param args cmd plus args + * @return a future which completes when dbgeng is ready to accept commands + */ + CompletableFuture start(String[] args); + + /** + * Terminate dbgeng + */ + void terminate(); + + /** + * Check if GDB is alive + * + * Note this is not about the state of inferiors in GDB. If the GDB controlling process is + * alive, GDB is alive. + * + * @return true if GDB is alive, false otherwise + */ + boolean isRunning(); + + /** + * Add a listener for dbgeng's state + * + * @see #getState() + * @param listener the listener to add + */ + void addStateListener(DbgStateListener listener); + + /** + * Remove a listener for dbgeng's state + * + * @see #getState() + * @param listener the listener to remove + */ + void removeStateListener(DbgStateListener listener); + + /** + * Add a listener for events on processes + * + * @param listener the listener to add + */ + void addEventsListener(DbgEventsListener listener); + + /** + * Remove a listener for events on inferiors + * + * @param listener the listener to remove + */ + void removeEventsListener(DbgEventsListener listener); + + /** + * Get a thread by its dbgeng-assigned ID + * + * dbgeng numbers its threads using a global counter. These IDs are unrelated to the OS-assigned + * TID. This method can retrieve a thread by its ID no matter which inferior it belongs to. + * + * @param id the dbgeng-asigned thread ID + * @return a handle to the thread, if it exists + */ + DbgThread getThread(DebugThreadId id); + + /** + * Get an process by its dbgeng-assigned ID + * + * dbgeng numbers processes incrementally. All inferiors and created and destroyed by the user. + * See {@link #addProcess()}. + * + * @param id the process ID + * @return a handle to the process, if it exists + */ + DbgProcess getProcess(DebugProcessId id); + + /** + * Get an session by its dbgeng-assigned ID + * + * dbgeng numbers processes incrementally. All inferiors and created and destroyed by the user. + * See {@link #addSession()}. + * + * @param id the process ID + * @return a handle to the process, if it exists + */ + DbgSession getSession(DebugSessionId id); + + /** + * Get all threads known to the manager + * + * This does not ask dbgeng to lists its known threads. Rather it returns a read-only view of + * the manager's understanding of the current threads based on its tracking of dbgeng events. + * + * @return a map of dbgeng-assigned thread IDs to corresponding thread handles + */ + Map getKnownThreads(); + + /** + * Get all processes known to the manager + * + * This does not ask dbgeng to list its processes. Rather it returns a read-only view of the + * manager's understanding of the current processes based on its tracking of dbgeng events. + * + * @return a map of process IDs to corresponding process handles + */ + Map getKnownProcesses(); + + /** + * Get all sessions known to the manager + * + * This does not ask dbgeng to list its processes. Rather it returns a read-only view of the + * manager's understanding of the current inferiors based on its tracking of dbgeng events. + * + * @return a map of session IDs to corresponding session handles + */ + Map getKnownSessions(); + + /** + * Get all breakpoints known to the manager + * + * This does not ask dbgeng to list its breakpoints. Rather it returns a read-only view of the + * manager's understanding of the current breakpoints based on its tracking of dbgeng events. + * + * @return a map of dbgeng-assigned breakpoint IDs to corresponding breakpoint information + */ + Map getKnownBreakpoints(); + + /** + * Send an interrupt to dbgeng regardless of other queued commands + * + * This may be useful if the manager's command queue is stalled because an inferior is running. + * + */ + void sendInterruptNow(); + + /** + * Get the state of the dbgeng session + * + * In all-stop mode, if any thread is running, dbgeng is said to be in the running state and is + * unable to process commands. Otherwise, if all threads are stopped, then dbgeng is said to be + * in the stopped state and can accept and process commands. This manager has not been tested in + * non-stop mode. + * + * @return the state + */ + DbgState getState(); + + /** + * Add a process + * + * @return a future which completes with the handle to the new process + */ + CompletableFuture addProcess(); + + public void addProcess(DbgProcessImpl process, DbgCause cause); + + /** + * Remove a process + * + * @param process the process to remove + * @return a future which completes then dbgeng has executed the command + */ + CompletableFuture removeProcess(DbgProcess process); + + /** + * Add a session + * + * @return a future which completes with the handle to the new process + */ + CompletableFuture addSession(); + + public void addSession(DbgSessionImpl session, DbgCause cause); + + /** + * Remove a session + * + * @param process the session to remove + * @return a future which completes then dbgeng has executed the command + */ + CompletableFuture removeSession(DbgSession session); + + /** + * Execute an arbitrary CLI command, printing output to the CLI console + * + * Note: to ensure a certain thread or inferior has focus for a console command, see + * {@link DbgThread#console(String)}. + * + * @param command the command to execute + * @return a future that completes when dbgeng has executed the command + */ + CompletableFuture console(String command); + + /** + * Execute an arbitrary CLI command, capturing its console output + * + * The output will not be printed to the CLI console. To ensure a certain thread or inferior has + * focus for a console command, see {@link DbgThread#consoleCapture(String)} and + * {@link DbgProcess#consoleCapture(String)}. + * + * @param command the command to execute + * @return a future that completes with the captured output when dbgeng has executed the command + */ + CompletableFuture consoleCapture(String command); + + /** + * List dbgeng's processes + * + * @return a future that completes with a map of process IDs to process handles + */ + CompletableFuture> listProcesses(); + + /** + * List the available processes on target + * + * @return a future that completes with a list of PIDs + */ + CompletableFuture>> listAvailableProcesses(); + + /** + * List dbgeng's sessions + * + * @return a future that completes with a map of session IDs to session handles + */ + CompletableFuture> listSessions(); + + /** + * List information for all breakpoints + * + * @return a future that completes with a list of information for all breakpoints + */ + CompletableFuture> listBreakpoints(); + + /** + * Disable the given breakpoints + * + * This is equivalent to the CLI command {@code disable breakpoint [NUMBER]}. + * + * @param numbers the dbgeng-assigned breakpoint numbers + * @return a future that completes when dbgeng has executed the command + */ + CompletableFuture disableBreakpoints(long... numbers); + + /** + * Enable the given breakpoints + * + * This is equivalent to the CLI command {@code enable breakpoint [NUMBER]}. + * + * @param numbers the dbgeng-assigned breakpoint numbers + * @return a future that completes when dbgeng has executed the command + */ + CompletableFuture enableBreakpoints(long... numbers); + + /** + * Delete a breakpoint + * + * This is equivalent to the CLI command {@code delete breakpoint [NUMBER]}. + * + * @param numbers the dbgeng-assigned breakpoint numbers + * @return a future that completes when dbgeng has executed the command + */ + CompletableFuture deleteBreakpoints(long... numbers); + + CompletableFuture launch(List args); + + CompletableFuture launch(Map args); + + /********** NEEDED FOR TESTING ************/ + + /** + * Returns the current process + * + * @return the current process + */ + DbgProcess currentProcess(); + + CompletableFuture waitForState(DbgState stopped); + + CompletableFuture waitForPrompt(); + + CompletableFuture waitForEventEx(); + + CompletableFuture execute(DbgCommand cmd); + + DebugEventInformation getLastEventInformation(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgMemoryOperations.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgMemoryOperations.java new file mode 100644 index 0000000000..ec3ea9c143 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgMemoryOperations.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import java.nio.ByteBuffer; +import java.util.concurrent.CompletableFuture; + +import com.google.common.collect.RangeSet; + +public interface DbgMemoryOperations { + + /** + * Read memory + * + * @param addr the address to begin reading at + * @param buf the buffer to read into + * @param len the length of data to read + * @return a future which completes giving the ranges successfully read + */ + CompletableFuture> readMemory(long addr, ByteBuffer buf, int len); + + /** + * Read memory + * + * The length is determined by the available space in the destination buffer + * + * @param addr the address to begin reading at + * @param buf the buffer to read into + * @return a future which completes giving the ranges successfully read + */ + default CompletableFuture> readMemory(long addr, ByteBuffer buf) { + return readMemory(addr, buf, buf.remaining()); + } + + /** + * Write memory + * + * @param addr the address to begin writing at + * @param buf the buffer to copy from + * @param len the length of data to write + * @return a future that completes when the write succeeds in its entirety + */ + CompletableFuture writeMemory(long addr, ByteBuffer buf, int len); + + /** + * Write memory + * + * The length is determined by the available data in the source buffer + * + * @param addr the address to begin writing at + * @param buf the buffer to copy from + * @return a future that completes when the write succeeds in its entirety + */ + default CompletableFuture writeMemory(long addr, ByteBuffer buf) { + return writeMemory(addr, buf, buf.remaining()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModule.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModule.java new file mode 100644 index 0000000000..fce5472a8b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModule.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.impl.DbgMinimalSymbol; + +public interface DbgModule { + + String getName(); + + String getImageName(); + + Long getKnownBase(); + + Integer getSize(); + + Integer getTimeStamp(); + + //CompletableFuture> listSections(); + + //Map getKnownSections(); + + CompletableFuture> listMinimalSymbols(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModuleMemory.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModuleMemory.java new file mode 100644 index 0000000000..67a74ac407 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModuleMemory.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import java.util.List; + +public interface DbgModuleMemory { + + String getName(); + + long getVmaStart(); + + long getVmaEnd(); + + String getType(); + + List getProtect(); + + List getAllocationProtect(); + + long getAllocationBase(); + + String getState(); + + boolean isRead(); + + boolean isWrite(); + + boolean isExec(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModuleSection.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModuleSection.java new file mode 100644 index 0000000000..c2474bec60 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgModuleSection.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +public class DbgModuleSection { + + private DbgModule module; + + public DbgModuleSection(DbgModule module) { + this.module = module; + } + + public Long getStart() { + return module.getKnownBase(); + } + + public Integer getSize() { + return module.getSize(); + } + + public String getName() { + return module.getImageName(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgProcess.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgProcess.java new file mode 100644 index 0000000000..ed71ca9be1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgProcess.java @@ -0,0 +1,214 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.impl.DbgSectionImpl; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.target.TargetAttachable; + +public interface DbgProcess extends DbgMemoryOperations { + + /** + * Get the dbgeng-assigned process number + * + * @return the number + */ + DebugProcessId getId(); + + /** + * If started, get the OS-assigned ID of the process + * + * @return the process ID + */ + Long getPid(); + + /** + * If exited (implying a previous start), get the process exit code + * + * This may be slightly system-dependent, as the exit code may specify either the status of a + * normal exit, or the cause of an abnormal exit. + * + * @return the exit code + */ + Long getExitCode(); + + /** + * Get a thread belonging to this process + * + * dbgeng (at least recent versions) numbers its threads using a global counter. The thread ID + * is this number, not the OS-assigned TID. + * + * @param id the dbgeng-assigned thread ID + * @return a handle to the thread, if it exists + */ + DbgThread getThread(DebugThreadId id); + + /** + * Get a module belonging to this process + * + * dbgeng (at least recent versions) numbers its threads using a global counter. The thread ID + * is this number, not the OS-assigned TID. + * + * @param id the dbgeng-assigned thread ID + * @return a handle to the module, if it exists + */ + DbgModule getModule(String id); + + /** + * Enumerate the threads known to the manager to belong to this process + * + * This does not send any commands to dbgeng. Rather it simply returns a read-only handle to the + * manager's internal map for tracking threads and processes. + * + * @return a map of dbgeng-assigned thread IDs to thread handles + */ + Map getKnownThreads(); + + /** + * List Dbg's threads in this process + * + * This is equivalent to the CLI command: {@code info threads}. + * + * @return a future that completes with a map of global thread IDs to thread handles + */ + CompletableFuture> listThreads(); + + /** + * Enumerate the modules known to the manager to belong to this process + * + * This does not send any commands to dbgeng. Rather it simply returns a read-only handle to the + * manager's internal map for tracking modules. + * + * @return a map of dbgeng-assigned names to module handles + */ + Map getKnownModules(); + + /** + * List dbgeng's modules in this process + * + * This is equivalent to the CLI command: {@code maintenance info sections ALLOBJ}. This command + * is more thorough than {@code info shared} as it contains the executable module, shared + * libraries, system-supplied objects, and enumerates all sections thereof, not just + * {@code .text}. + * + * @return a future that completes with a map of module names to module handles + */ + CompletableFuture> listModules(); + + /** + * Enumerate the memory mappings known to the manager to belong to this process + * + * @return a map of start addresses to mapped memory regions + */ + Map getKnownMappings(); + + /** + * List the memory mappings of this process + * + * @return a future that completes with a map of start addresses to mapped memory regions + */ + CompletableFuture> listMappings(); + + /** + * Change focus to this process + * + * @return a future that completes when dbgeng has executed the command + */ + CompletableFuture select(); + + /** + * Specify a binary image for execution and debug symbols + * + * @param file the path to the binary image + * @return a future that completes when dbgeng has executed the command + */ + CompletableFuture fileExecAndSymbols(String file); + + /** + * Begin execution + * + * @return a future that completes with a handle to the first thread of the running process + */ + CompletableFuture run(); + + /** + * Attach to a running process + * + * @param pid the OS-assigned process ID of the target process + * @return a future that completes with a set of handles to all threads of the attached process + */ + CompletableFuture> attach(long pid); + + /** + * Attach to a running process + * + * @param ref the target process + * @return a future that completes with a set of handles to all threads of the attached process + */ + CompletableFuture> reattach( + TypedTargetObjectRef> ref); + + /** + * Execute an arbitrary kd command, capturing its console output + * + * @param command the command to execute + * @return a future that completes with the captured output when Dbg has executed the command + */ + CompletableFuture consoleCapture(String command); + + /** + * Continue execution + * + * @return a future that completes once the process is running + */ + CompletableFuture cont(); + + /** + * Evaluate an expression + * + * @param expression the expression to evaluate + * @return a future that completes with the string representation of the value + */ + CompletableFuture evaluate(String expression); + + /** + * Detach from the process + * + * @return a future that completes when dbgeng has executed the command + */ + CompletableFuture detach(); + + /** + * Kill the process + * + * @return a future that completes when dbgeng has executed the command + */ + CompletableFuture kill(); + + /** + * Remove this process from the session + * + * @return a future that completes when dbgeng has executed the command + */ + CompletableFuture remove(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgReason.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgReason.java new file mode 100644 index 0000000000..9a8e5bbf27 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgReason.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +public interface DbgReason { + + /** + * Reasons other than those given by GDB + */ + enum Reasons implements DbgReason { + /** + * No reason was given + */ + NONE, + /** + * A reason was given, but the manager does not understand it + */ + UNKNOWN; + + @Override + public String desc() { + // TODO Auto-generated method stub + return null; + } + } + + static DbgReason getReason(String info) { + return Reasons.UNKNOWN; + } + + public String desc(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgSession.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgSession.java new file mode 100644 index 0000000000..00b6d0db89 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgSession.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.dbgeng.DebugSessionId; + +public interface DbgSession { + + /** + * Get the dbgeng-assigned session number + * + * @return the number + */ + DebugSessionId getId(); + + /** + * If exited (implying a previous start), get the session exit code + * + * This may be slightly system-dependent, as the exit code may specify either the status of a + * normal exit, or the cause of an abnormal exit. + * + * @return the exit code + */ + Long getExitCode(); + + /** + * Get a process belonging to this session + * + * dbgeng (at least recent versions) numbers its processes using a global counter. The process + * ID is this number, not the OS-assigned TID. + * + * @param id the dbgeng-assigned process ID + * @return a handle to the process, if it exists + */ + DbgProcess getProcess(DebugProcessId id); + + /** + * Enumerate the processes known to the manager to belong to this session + * + * This does not send any commands to dbgeng. Rather it simply returns a read-only handle to the + * manager's internal map for tracking processes and sessions. + * + * @return a map of dbgeng-assigned process IDs to process handles + */ + Map getKnownProcesses(); + + /** + * List Dbg's processes in this session + * + * This is equivalent to the CLI command: {@code info processes}. + * + * @return a future that completes with a map of global process IDs to process handles + */ + CompletableFuture> listProcesses(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStackFrame.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStackFrame.java new file mode 100644 index 0000000000..d3d0e8d2f6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStackFrame.java @@ -0,0 +1,71 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import java.math.BigInteger; +import java.util.concurrent.CompletableFuture; + +public interface DbgStackFrame extends DbgStackFrameOperations { + + /** + * Get the address of the program counter + * + * @return the program counter + */ + BigInteger getAddress(); + + /** + * Get the name of the function where the program counter is + * + * @return the function name + */ + String getFunction(); + + /** + * Get the level of the frame + * + * Frame 0 is the actual machine state, and each increment unwinds the stack 1 frame. + * + * @return the frame level + */ + int getLevel(); + + /** + * Make this frame the current frame + * + * @return a future that completes when the frame is the current frame + */ + CompletableFuture select(); + + /** + * Get the thread for this frame + * + * @return the thread + */ + DbgThread getThread(); + + long getFuncTableEntry(); + + long getFrameOffset(); + + long getReturnOffset(); + + long getStackOffset(); + + boolean getVirtual(); + + long[] getParams(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStackFrameOperations.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStackFrameOperations.java new file mode 100644 index 0000000000..3a34c54146 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStackFrameOperations.java @@ -0,0 +1,72 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import java.math.BigInteger; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.impl.DbgRegister; + +public interface DbgStackFrameOperations { + + /** + * Evaluate an expression + * + * This evaluates an expression in the same way that the CLI commands {@code print}, + * {@code output}, and {@code call} would. + * + * @param expression the expression to evaluate + * @return a future that completes with the string representation of the value + */ + CompletableFuture evaluate(String expression); + + /** + * Read the values of a given set of registers + * + * @param regs the set of registers + * @return a future that completes with a map of register descriptors to value + */ + CompletableFuture> readRegisters(Set regs); + + /** + * Write the values of a given set of registers + * + * @param regVals a map of register descriptors to value + * @return a future that completes when the registers have been written + */ + CompletableFuture writeRegisters(Map regVals); + + /** + * Execute an arbitrary CLI command, printing output to the CLI console + * + * @param command the command to execute + * @return a future that completes when Dbg has executed the command + */ + CompletableFuture console(String command); + + /** + * Execute an arbitrary CLI command, capturing its console output + * + * The output will not be printed to the CLI console. + * + * @param command the command to execute + * @return a future that completes with the captured output when Dbg has executed the command + */ + CompletableFuture consoleCapture(String command); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgState.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgState.java new file mode 100644 index 0000000000..179fa6dafe --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgState.java @@ -0,0 +1,80 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +/** + * Describes the running state of dbgeng + * + * The manager is initialized in the {@link #NOT_STARTED} state. When + * {@link DbgManager#start(String[] args)} is called, it enters the {@link #STARTING} state + * immediately upon successfully launching the Dbg session. Once dbgeng issues its first prompt, it + * enters the {@link #STOPPED} state. The state then switches between {@link #RUNNING} and + * {@link #STOPPED} according to the execution state of its inferior(s). When the dbgeng session + * exits, it enters the {@link #EXIT} state. + * + * This is also used to describe the state of threads and inferiors. Only {@link #STOPPED}, + * {@link #RUNNING}, and {@link #EXIT} apply to inferiors. Only {@link #STOPPED} and + * {@link #RUNNING} apply to threads. + */ +public enum DbgState { + /** + * Dbg is not alive, because it has not be started + */ + NOT_STARTED { + @Override + public boolean isAlive() { + return false; + } + }, + /** + * Dbg is alive, but has not issued its first prompt, yet + */ + STARTING { + @Override + public boolean isAlive() { + return true; + } + }, + /** + * Dbg, the process, or the thread, is stopped + */ + STOPPED { + @Override + public boolean isAlive() { + return true; + } + }, + /** + * Dbg, the process, or the thread, is running + */ + RUNNING { + @Override + public boolean isAlive() { + return true; + } + }, + /** + * Dbg or the process has exited + */ + EXIT { + @Override + public boolean isAlive() { + return false; + } + }; + + public abstract boolean isAlive(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStateListener.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStateListener.java new file mode 100644 index 0000000000..b7edd7785e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgStateListener.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import ghidra.util.TriConsumer; + +/** + * A listener for changes in Dbg's state + */ +public interface DbgStateListener extends TriConsumer { + /** + * The state has changed because of the given cause + * + * @param state the new state + * @param cause the reason for the change + */ + void stateChanged(DbgState state, DbgCause cause); + + @Override + default void accept(DbgState oldVal, DbgState newVal, DbgCause u) { + stateChanged(newVal, u); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgThread.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgThread.java new file mode 100644 index 0000000000..f6c5bc211d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgThread.java @@ -0,0 +1,136 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.jna.dbgeng.WinNTExtra.Machine; +import agent.dbgeng.manager.DbgManager.ExecSuffix; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInsertions; +import agent.dbgeng.manager.impl.DbgRegisterSet; + +public interface DbgThread + extends DbgBreakpointInsertions, DbgMemoryOperations, DbgStackFrameOperations { + + /** + * Get the dbgeng-assigned thread number + * + * This is not the OS-assigned TID. + * + * @return the number + */ + DebugThreadId getId(); + + Long getTid(); + + /** + * Get the state of the thread, {@link DbgState#RUNNING} or {@link DbgState#STOPPED}. + * + * @return the state + */ + DbgState getState(); + + /** + * Set the state of this thread + * + * @param state the new state + * @param cause the cause for the change + * @param reason the reason (usually a stop reason) for the change + * @return true if the state actually changed + */ + boolean setState(DbgState state, DbgCause cause, DbgReason reason); + + /** + * Make this thread the current thread + * + * @return a future that completes when the thread is the current thread + */ + CompletableFuture select(); + + /** + * Get the process to which this thread belongs + * + * @return the process + */ + DbgProcess getProcess(); + + /** + * List the frames of this thread's stack + * + * @return the list of stack frames + */ + CompletableFuture> listStackFrames(); + + /** + * List the registers available to this thread + * + * @return a future that completes with a map of register names to descriptors + */ + CompletableFuture listRegisters(); + + /** + * Continue execution + * + * This is equivalent to the CLI command: {@code continue}. Depending on GDB's execution mode, + * this may allow other threads to execute, too. + * + * @return a future that completes once the thread is running + */ + CompletableFuture cont(); + + /** + * Step the thread + * + * Note that the command can complete before the thread has finished stepping. The command + * completes as soon as the thread is running. A separate stop event is emitted when the step is + * completed. + * + * @param suffix specifies how far to step, or on what conditions stepping ends. + * + * @return a future that completes once the thread is running + */ + CompletableFuture step(ExecSuffix suffix); + + /** + * Detach from the entire process + * + * This is equivalent to the CLI command {@code detach}. It will detach the entire process, not + * just this thread. + * + * @return a future that completes when GDB has executed the command + */ + CompletableFuture kill(); + + /** + * Kill the entire process + * + * This is equivalent to the CLI command {@code kill}. It will kill the entire process, not just + * this thread. + * + * @return a future that completes when GDB has executed the command + */ + CompletableFuture detach(); + + /** + * Get the effective architecture for the executing thread + * + * @return a future that completes when GDB has executed the command + */ + Machine getExecutingProcessorType(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointDisp.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointDisp.java new file mode 100644 index 0000000000..e03d9bc836 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointDisp.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.breakpoint; + +public enum DbgBreakpointDisp { + /** + * The breakpoint should remain until deleted by the user + */ + KEEP, + /** + * The breakpoint should be deleted once it is hit + */ + DEL, + /** + * Some disposition unknown to the manager + */ + OTHER; + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointInfo.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointInfo.java new file mode 100644 index 0000000000..83bd88f4fc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointInfo.java @@ -0,0 +1,272 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.breakpoint; + +import java.util.*; + +import agent.dbgeng.dbgeng.DebugBreakpoint; +import agent.dbgeng.dbgeng.DebugBreakpoint.*; +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.DbgThread; +import ghidra.comm.util.BitmaskSet; + +public class DbgBreakpointInfo { + + private DebugBreakpoint bpt; + private DbgProcess proc; + private DbgThread eventThread; + private BreakFullType bptType; + private BitmaskSet flags; + private BreakDataParameters parameters = + new BreakDataParameters(1, BitmaskSet.of(BreakAccess.EXECUTE)); + private BitmaskSet access; + private int size = 1; + + private final long number; + private boolean enabled; + + private final String location; + private final List locations; + + /** + * Construct Dbg breakpoint information + * + * @param number the Dbg-assigned breakpoint number + * @param type the type of breakpoint + * @param disp the breakpoint disposition + * @param loc the location of the breakpoint + * @param pending if pending, the location that is not yet resolved + * @param enabled true if the breakpoint is enabled, false otherwise + * @param times the number of times the breakpoint has been hit + * @param locations the resolved address(es) of this breakpoint + */ + public DbgBreakpointInfo(DbgBreakpointInfo oldInfo, boolean enabled) { + this(oldInfo.getDebugBreakpoint(), oldInfo.getProc(), oldInfo.getEventThread()); + this.enabled = enabled; + } + + public DbgBreakpointInfo(DebugBreakpoint bpt, DbgProcess proc) { + this(bpt, proc, null); + } + + public DbgBreakpointInfo(DebugBreakpoint bp, DbgProcess process, DbgThread thread) { + this.bpt = bp; + this.proc = process; + this.eventThread = thread; + this.number = bpt.getId(); + this.bptType = bpt.getType(); + this.flags = bpt.getFlags(); + //this.parameters = bpt.getDataParameters(); + //this.access = parameters.access; + //this.size = parameters.size; + this.location = Long.toHexString(bpt.getOffset()); + List locs = new ArrayList<>(); + List ids = new ArrayList<>(); + ids.add(proc.getId()); + locs.add(new DbgBreakpointLocation(bpt.getId(), 1, true, location, ids)); + this.locations = Collections.unmodifiableList(locs); + } + + @Override + public int hashCode() { + return Objects.hash(number, bptType, getFlags(), location, enabled, access, size); + } + + @Override + public String toString() { + return ""; + } + + @Override + public boolean equals(Object obj) { + if (!((obj instanceof DbgBreakpointInfo))) { + return false; + } + DbgBreakpointInfo that = (DbgBreakpointInfo) obj; + if (this.number != that.number) { + return false; + } + if (this.getFlags() != that.getFlags()) { + return false; + } + if (this.size != that.size) { + return false; + } + if (!Objects.equals(this.location, that.location)) { + return false; + } + if (this.enabled != that.enabled) { + return false; + } + return true; + } + + /** + * Get the Dbg-assigned breakpoint number + * + * This is the key into Dbg's breakpoint table to locate the breakpoint this information + * describes. + * + * @return the number + */ + public long getNumber() { + return number; + } + + /** + * Get the type of breakpoint + * + * @return the type + */ + public DbgBreakpointType getType() { + boolean isCode = bpt.getType().breakType.equals(BreakType.CODE); + if (isCode) { + return DbgBreakpointType.BREAKPOINT; + } + BreakDataParameters params = bpt.getDataParameters(); + if (params == null || params.access.isEmpty()) { + return DbgBreakpointType.OTHER; + } + if (params.access.contains(BreakAccess.READ) && params.access.contains(BreakAccess.WRITE)) { + return DbgBreakpointType.ACCESS_WATCHPOINT; + } + if (params.access.contains(BreakAccess.READ)) { + return DbgBreakpointType.READ_WATCHPOINT; + } + if (params.access.contains(BreakAccess.WRITE)) { + return DbgBreakpointType.HW_WATCHPOINT; + } + if (params.access.contains(BreakAccess.EXECUTE)) { + return DbgBreakpointType.HW_BREAKPOINT; + } + return DbgBreakpointType.OTHER; + } + + /** + * Get the breakpoint disposition, i.e., what happens to the breakpoint once it has been hit + * + * @return the disposition + */ + public DbgBreakpointDisp getDisp() { + return DbgBreakpointDisp.KEEP; + } + + /** + * Get the location of the breakpoint + * + * @return the location + */ + public String getLocation() { + return location; + } + + /** + * Get the size of the breakpoint + * + * @return the size + */ + public int getSize() { + return size; + } + + /** + * Get the access of the breakpoint + * + * @return the size + */ + public BitmaskSet getAccess() { + return access; + } + + /** + * Assuming the location is an address, get it as a long + * + * @return the address + */ + public long addrAsLong() { + return Long.parseUnsignedLong(location, 16); + } + + /** + * If the breakpoint is pending resolution, get the location that is pending + * + * @return the pending location + */ + public String getPending() { + return getFlags().contains(BreakFlags.DEFERRED) ? "pending" : ""; + } + + /** + * Check if the breakpoint is enabled + * + * @return true if enabled, false otherwise + */ + public boolean isEnabled() { + return getFlags().contains(BreakFlags.ENABLED); + } + + /** + * Get the number of times the breakpoint has been hit + * + * @return the hit count + */ + public int getTimes() { + return 0; + } + + /** + * Get a list of resolved addresses + * + * The effective locations may change for a variety of reasons. Most notable, a new module may + * be loaded, having location(s) that match the desired location of this breakpoint. The binary + * addresses within will become new effective locations of this breakpoint. + * + * @return the list of locations at the time the breakpoint information was captured + */ + public List getLocations() { + return locations; + } + + public DbgBreakpointInfo withEnabled(@SuppressWarnings("hiding") boolean enabled) { + if (isEnabled() == enabled) { + return this; + } + return new DbgBreakpointInfo(this, enabled); + } + + public DebugBreakpoint getDebugBreakpoint() { + return bpt; + } + + public BitmaskSet getFlags() { + return flags; + } + + public DbgProcess getProc() { + return proc; + } + + public DbgThread getEventThread() { + return eventThread; + } + + public long getAddressAsLong() { + return locations.get(0).addrAsLong(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointInsertions.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointInsertions.java new file mode 100644 index 0000000000..0c22f1a1e4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointInsertions.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.breakpoint; + +import java.util.concurrent.CompletableFuture; + +import ghidra.util.NumericUtilities; + +public interface DbgBreakpointInsertions { + /** + * Insert a breakpoint + * + * This is equivalent to the CLI command: {@code break [LOC]}, or {@code watch [LOC]}, etc. + * + * Breakpoints in Dbg can get pretty complicated. Depending on the location specification, the + * actual location of the breakpoint may change during the lifetime of an inferior. Take note of + * the breakpoint number to track those changes across breakpoint modification events. + * + * @param loc the location (address, symbol, line number, etc.) to place the breakpoint at + * @param type the type of breakpoint (or watchpoint) to add + * @return a copy of the resulting breakpoint information once Dbg has executed the command + */ + default CompletableFuture insertBreakpoint(String loc, + DbgBreakpointType type) { + return insertBreakpoint(NumericUtilities.parseHexLong(loc), 1, + DbgBreakpointType.BREAKPOINT); + } + + /** + * Insert a (usually software) execution breakpoint + * + * @param loc string version of address + * @return a future that completes when dbgeng has executed the command + * + * @see #insertBreakpoint(String, DbgBreakpointType) + */ + default CompletableFuture insertBreakpoint(String loc) { + return insertBreakpoint(loc, DbgBreakpointType.BREAKPOINT); + } + + /** + * Insert a (usually software) execution breakpoint at the given address offset + * + * Note, this uses the "Address Notation" specified by Dbg. + * + * @param addr breakpoint address + * @return a future that completes when dbgeng has executed the command + * + * @see #insertBreakpoint(String, DbgBreakpointType) + */ + default CompletableFuture insertBreakpoint(long addr) { + return insertBreakpoint(addr, 1, DbgBreakpointType.BREAKPOINT); + } + + /** + * Insert a breakpoint (usually a watchpoint) at the given address range + * + * Note, this implements the length by casting the address pointer to a + * fixed-length-char-array-pointer where the array has the given length. Support for specific + * lengths may vary by platform. + * + * @param addr the starting address + * @param len the length of the range + * @param type the type of breakpoint (usually a watchpoint) + * @return a future that completes when dbgeng has executed the command + * @see #insertBreakpoint(String, DbgBreakpointType) + */ + CompletableFuture insertBreakpoint(long addr, int len, + DbgBreakpointType type); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointLocation.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointLocation.java new file mode 100644 index 0000000000..b5d4176561 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointLocation.java @@ -0,0 +1,138 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.breakpoint; + +import java.util.*; + +import agent.dbgeng.dbgeng.DebugProcessId; + +public class DbgBreakpointLocation { + + private final long number; + private final long sub; + private final boolean enabled; + private final String addr; + private final List processIds; + + /** + * Construct a breakpoint location + * + * @param number the number of breakpoint location, i.e., {@code x} in {@code x.y} + * @param sub the number of the breakpoint location, i.e., {@code y} in {@code x.y} + * @param enabled true if the location is enabled, false otherwise + * @param addr the address of this location, usually resolved, but maybe not + * @param processIds a list of inferior IDs to which this location applies + */ + DbgBreakpointLocation(long number, long sub, boolean enabled, String addr, + List processIds) { + this.number = number; + this.sub = sub; + this.enabled = enabled; + this.addr = addr; + this.processIds = Collections.unmodifiableList(processIds); + } + + @Override + public String toString() { + return ""; + } + + /** + * Get the breakpoint number, i.e., {@code x} in {@code x.y} + * + * @return the breakpoint number + */ + public long getNumber() { + return number; + } + + /** + * If present, get the location number, i.e., {@code y} in {@code x.y} + * + * @return the location number, or 0 + */ + public long getSub() { + return sub; + } + + /** + * Check if the location is enabled + * + * @return true if enabled, false otherwise + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Get the address, usually resolved, but maybe not + * + * see {@link DbgBreakpointInfo#getPending()} + * + * @return the address + */ + public String getAddr() { + return addr; + } + + /** + * If numerical, get the address as a long + * + * @return the address + */ + public long addrAsLong() { + return Long.parseUnsignedLong(addr, 16); + } + + /** + * Get a list of inferior IDs to which this location applies + * + * @return the list of inferiors + */ + public List getProcessIds() { + return processIds; + } + + @Override + public int hashCode() { + return Objects.hash(number, sub, enabled, addr, processIds); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DbgBreakpointLocation)) { + return false; + } + DbgBreakpointLocation that = (DbgBreakpointLocation) obj; + if (this.number != that.number) { + return false; + } + if (this.sub != that.sub) { + return false; + } + if (this.enabled != that.enabled) { + return false; + } + if (!Objects.equals(this.addr, that.addr)) { + return false; + } + if (!Objects.equals(this.processIds, that.processIds)) { + return false; + } + return true; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointType.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointType.java new file mode 100644 index 0000000000..e957b32055 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/breakpoint/DbgBreakpointType.java @@ -0,0 +1,65 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.breakpoint; + +/** + * The type of Dbg breakpoint + */ +public enum DbgBreakpointType { + /** + * A software execution breakpoint, usually set via {@code break} + */ + BREAKPOINT, + /** + * A hardware execution breakpoint, usually set via {@code hbreak} + */ + HW_BREAKPOINT, + /** + * A debug printf point, usually set via {@code dprint} + */ + DPRINTF, + /** + * A hardware (write) watchpoint, usually set via {@code watch} + */ + HW_WATCHPOINT, + /** + * A read watchpoint, usually set via {@code rwatch} + */ + READ_WATCHPOINT, + /** + * An access (r/w) watchpoint, usually set via {@code awatch} + */ + ACCESS_WATCHPOINT, + /** + * Some type not known to the manager + */ + OTHER; + + /** + * Parse a type from a Dbg/MI breakpoint information block + * + * @param string the value of type parsed + * @return the enum constant, or {@link #OTHER} if unrecognized + */ + public static DbgBreakpointType fromStr(String string) { + try { + return valueOf(string); + } + catch (IllegalArgumentException e) { + return OTHER; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/AbstractDbgCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/AbstractDbgCommand.java new file mode 100644 index 0000000000..baa54aa42e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/AbstractDbgCommand.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.evt.AbstractDbgCompletedCommandEvent; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * A base class for interacting with specific Dbg commands + * + * @param the type of object "returned" by the command + */ +public abstract class AbstractDbgCommand implements DbgCommand { + protected final DbgManagerImpl manager; + + /** + * Construct a new command to be executed by the given manager + * + * @param manager the manager to execute the command + */ + protected AbstractDbgCommand(DbgManagerImpl manager) { + this.manager = manager; + } + + @Override + public boolean validInState(DbgState state) { + return true; // With dual interpreters, shouldn't have to worry. + } + + @Override + public boolean handle(DbgEvent evt, DbgPendingCommand pending) { + if (evt instanceof AbstractDbgCompletedCommandEvent) { + if (pending.getCommand().equals(this)) { + return true; + } + } + return false; + } + + @Override + public T complete(DbgPendingCommand pending) { + return null; + } + + @Override + public void invoke() { + // Nothing + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAddProcessCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAddProcessCommand.java new file mode 100644 index 0000000000..91ed6a8d1a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAddProcessCommand.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.manager.DbgManager; +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.manager.impl.DbgProcessImpl; + +/** + * Implementation of {@link DbgManager#addProcess()} + */ +public class DbgAddProcessCommand extends AbstractDbgCommand { + + public DbgAddProcessCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public DbgProcess complete(DbgPendingCommand pending) { + return new DbgProcessImpl(manager); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAddSessionCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAddSessionCommand.java new file mode 100644 index 0000000000..80bc6c1224 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAddSessionCommand.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.manager.DbgManager; +import agent.dbgeng.manager.DbgSession; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.manager.impl.DbgSessionImpl; + +/** + * Implementation of {@link DbgManager#addProcess()} + */ +public class DbgAddSessionCommand extends AbstractDbgCommand { + + public DbgAddSessionCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public DbgSession complete(DbgPendingCommand pending) { + return new DbgSessionImpl(manager); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAttachCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAttachCommand.java new file mode 100644 index 0000000000..943e09ffe3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAttachCommand.java @@ -0,0 +1,82 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.LinkedHashSet; +import java.util.Set; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugClient.DebugAttachFlags; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.evt.*; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.manager.impl.DbgProcessImpl; +import ghidra.comm.util.BitmaskSet; + +/** + * Implementation of {@link DbgProcess#kill()} + */ +public class DbgAttachCommand extends AbstractDbgCommand> { + + private DbgProcessCreatedEvent created = null; + private boolean completed = false; + private DbgProcessImpl proc; + private BitmaskSet flags; + + public DbgAttachCommand(DbgManagerImpl manager, DbgProcessImpl proc, + BitmaskSet flags) { + super(manager); + this.proc = proc; + this.flags = flags; + } + + @Override + public boolean handle(DbgEvent evt, DbgPendingCommand pending) { + if (evt instanceof AbstractDbgCompletedCommandEvent && pending.getCommand().equals(this)) { + completed = true; + } + else if (evt instanceof DbgProcessCreatedEvent) { + created = (DbgProcessCreatedEvent) evt; + } + else if (evt instanceof DbgThreadCreatedEvent) { + pending.claim(evt); + } + else if (evt instanceof DbgStoppedEvent) { + pending.claim(evt); + } + return completed && (created != null); + } + + @Override + public Set complete(DbgPendingCommand pending) { + DebugSystemObjects so = manager.getSystemObjects(); + Set threads = new LinkedHashSet<>(); + for (DbgThreadCreatedEvent adds : pending.findAllOf(DbgThreadCreatedEvent.class)) { + DebugThreadInfo info = adds.getInfo(); + DebugThreadId tid = so.getThreadIdByHandle(info.handle); + threads.add(manager.getThread(tid)); + } + return threads; + } + + @Override + public void invoke() { + DebugClient dbgeng = manager.getClient(); + dbgeng.attachProcess(dbgeng.getLocalServer(), proc.getPid().intValue(), flags); + + manager.waitForEventEx(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAttachKernelCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAttachKernelCommand.java new file mode 100644 index 0000000000..e0a5fabd08 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgAttachKernelCommand.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.Map; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.evt.AbstractDbgCompletedCommandEvent; +import agent.dbgeng.manager.evt.DbgProcessCreatedEvent; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgProcess#fileExecAndSymbols(String)} + */ +public class DbgAttachKernelCommand extends AbstractDbgCommand { + + private DbgProcessCreatedEvent created = null; + private boolean completed = false; + private Map args; + + public DbgAttachKernelCommand(DbgManagerImpl manager, Map args) { + super(manager); + this.args = args; + } + + @Override + public boolean handle(DbgEvent evt, DbgPendingCommand pending) { + if (evt instanceof AbstractDbgCompletedCommandEvent && pending.getCommand().equals(this)) { + completed = true; + } + else if (evt instanceof DbgProcessCreatedEvent) { + created = (DbgProcessCreatedEvent) evt; + } + return completed && (created != null); + } + + @Override + public DbgThread complete(DbgPendingCommand pending) { + DebugProcessInfo info = created.getInfo(); + DebugThreadInfo tinfo = info.initialThreadInfo; + DebugSystemObjects so = manager.getSystemObjects(); + DebugThreadId tid = so.getThreadIdByHandle(tinfo.handle); + return manager.getThread(tid); + } + + @Override + public void invoke() { + DebugClient dbgeng = manager.getClient(); + long flags = (Long) args.get("Flags"); + String options = (String) args.get("Options"); + dbgeng.attachKernel(flags, options); + manager.waitForEventEx(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgCommandError.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgCommandError.java new file mode 100644 index 0000000000..8cb4e32b0c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgCommandError.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.manager.DbgCommand; + +/** + * Exception generated by the default "^error" handler + */ +public class DbgCommandError extends RuntimeException { + private final Object info; + + /** + * Construct an error with the given details + * + * @param info the detail information + * @param cmd source of error + */ + public DbgCommandError(Object info, DbgCommand cmd) { + super(cmd + " caused '" + info + "'"); + this.info = info; + } + + /** + * Construct an error with the given message + * + * @param message the message + */ + public DbgCommandError(String message) { + super(message); + this.info = null; + } + + /** + * Get the details, if present + * + * @return the details, or null + */ + public Object getInfo() { + return info; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgConsoleExecCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgConsoleExecCommand.java new file mode 100644 index 0000000000..a0c1b43962 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgConsoleExecCommand.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.manager.DbgEvent; +import agent.dbgeng.manager.DbgManager; +import agent.dbgeng.manager.evt.AbstractDbgCompletedCommandEvent; +import agent.dbgeng.manager.evt.DbgConsoleOutputEvent; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgManager#console(String)} and similar + */ +public class DbgConsoleExecCommand extends AbstractDbgCommand { + public enum Output { + CONSOLE, CAPTURE; + } + + private String command; + private Output to; + + public DbgConsoleExecCommand(DbgManagerImpl manager, String command, Output to) { + super(manager); + this.command = command; + this.to = to; + } + + @Override + public boolean handle(DbgEvent evt, DbgPendingCommand pending) { + if (evt instanceof AbstractDbgCompletedCommandEvent && pending.getCommand().equals(this)) { + return true; + } + else if (evt instanceof DbgConsoleOutputEvent && to == Output.CAPTURE) { + pending.steal(evt); + } + return false; + } + + @Override + public String complete(DbgPendingCommand pending) { + if (to == Output.CONSOLE) { + return null; + } + StringBuilder builder = new StringBuilder(); + for (DbgConsoleOutputEvent out : pending.findAllOf(DbgConsoleOutputEvent.class)) { + builder.append(out.getOutput()); + } + return builder.toString(); + } + + @Override + public void invoke() { + manager.getControl().execute(command); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgContinueCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgContinueCommand.java new file mode 100644 index 0000000000..1159011c33 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgContinueCommand.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgeng.dbgeng.DebugClient.DebugStatus; +import agent.dbgeng.manager.DbgEvent; +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.evt.*; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgProcess#kill()} + */ +public class DbgContinueCommand extends AbstractDbgCommand { + public DbgContinueCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public boolean handle(DbgEvent evt, DbgPendingCommand pending) { + if (evt instanceof AbstractDbgCompletedCommandEvent && pending.getCommand().equals(this)) { + return evt instanceof DbgCommandErrorEvent || + !pending.findAllOf(DbgRunningEvent.class).isEmpty(); + } + else if (evt instanceof DbgRunningEvent) { + // Event happens no matter which interpreter received the command + pending.claim(evt); + return !pending.findAllOf(AbstractDbgCompletedCommandEvent.class).isEmpty(); + } + return false; + } + + @Override + public void invoke() { + DebugClient dbgeng = manager.getClient(); + dbgeng.getControl().setExecutionStatus(DebugStatus.GO); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDeleteBreakpointsCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDeleteBreakpointsCommand.java new file mode 100644 index 0000000000..59d77aa7d2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDeleteBreakpointsCommand.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugBreakpoint; +import agent.dbgeng.dbgeng.DebugControl; +import agent.dbgeng.manager.DbgCause; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgManagerImpl#deleteBreakpoints(long)} + */ +public class DbgDeleteBreakpointsCommand extends AbstractDbgCommand { + + private final long[] numbers; + + public DbgDeleteBreakpointsCommand(DbgManagerImpl manager, long... numbers) { + super(manager); + this.numbers = numbers; + } + + @Override + public void invoke() { + DebugControl control = manager.getControl(); + for (long id : numbers) { + manager.doBreakpointDeleted(id, DbgCause.Causes.UNCLAIMED); + DebugBreakpoint bp = control.getBreakpointById((int) id); + bp.remove(); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDetachCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDetachCommand.java new file mode 100644 index 0000000000..28f00427b8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDetachCommand.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.ArrayList; +import java.util.Collection; + +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgeng.manager.DbgEvent; +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.evt.AbstractDbgCompletedCommandEvent; +import agent.dbgeng.manager.evt.DbgThreadExitedEvent; +import agent.dbgeng.manager.impl.*; + +/** + * Implementation of {@link DbgProcess#kill()} + */ +public class DbgDetachCommand extends AbstractDbgCommand { + private final DbgProcessImpl process; + + public DbgDetachCommand(DbgManagerImpl manager, DbgProcessImpl process) { + super(manager); + this.process = process; + } + + @Override + public Void complete(DbgPendingCommand pending) { + // TODO: necessary? + Collection threads = new ArrayList<>(process.getKnownThreadsImpl().values()); + for (DbgThreadImpl t : threads) { + manager.fireThreadExited(t.getId(), process, pending); + t.remove(); + } + return null; + } + + @Override + public void invoke() { + DebugClient dbgeng = manager.getClient(); + dbgeng.detachCurrentProcess(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDisableBreakpointsCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDisableBreakpointsCommand.java new file mode 100644 index 0000000000..2a0d02ec2d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgDisableBreakpointsCommand.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugBreakpoint; +import agent.dbgeng.dbgeng.DebugBreakpoint.BreakFlags; +import agent.dbgeng.dbgeng.DebugControl; +import agent.dbgeng.manager.evt.DbgCommandDoneEvent; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgManagerImpl#deleteBreakpoints(long)} + */ +public class DbgDisableBreakpointsCommand extends AbstractDbgCommand { + + private final long[] numbers; + + public DbgDisableBreakpointsCommand(DbgManagerImpl manager, long... numbers) { + super(manager); + this.numbers = numbers; + } + + @Override + public void invoke() { + DebugControl control = manager.getControl(); + for (long id : numbers) { + DebugBreakpoint bp = control.getBreakpointById((int) id); + bp.removeFlags(BreakFlags.ENABLED); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgEnableBreakpointsCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgEnableBreakpointsCommand.java new file mode 100644 index 0000000000..ed6359fa82 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgEnableBreakpointsCommand.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugBreakpoint; +import agent.dbgeng.dbgeng.DebugBreakpoint.BreakFlags; +import agent.dbgeng.dbgeng.DebugControl; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgManagerImpl#enableBreakpoints(long)} + */ +public class DbgEnableBreakpointsCommand extends AbstractDbgCommand { + + private final long[] numbers; + + public DbgEnableBreakpointsCommand(DbgManagerImpl manager, long... numbers) { + super(manager); + this.numbers = numbers; + } + + @Override + public void invoke() { + DebugControl control = manager.getControl(); + for (long id : numbers) { + DebugBreakpoint bp = control.getBreakpointById((int) id); + bp.addFlags(BreakFlags.ENABLED); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgEvaluateCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgEvaluateCommand.java new file mode 100644 index 0000000000..69e3d6cf2d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgEvaluateCommand.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgProcess#evaluate(String)} + */ +public class DbgEvaluateCommand extends AbstractDbgCommand { + private final String expression; + private String result; + + public DbgEvaluateCommand(DbgManagerImpl manager, String expression) { + super(manager); + this.expression = expression; + } + + @Override + public String complete(DbgPendingCommand pending) { + return result; + } + + @Override + public void invoke() { + // TODO: do something with expression to get result + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgFileExecAndSymbolsCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgFileExecAndSymbolsCommand.java new file mode 100644 index 0000000000..df3c4d02a2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgFileExecAndSymbolsCommand.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgProcess#fileExecAndSymbols(String)} + */ +public class DbgFileExecAndSymbolsCommand extends AbstractDbgCommand { + + private final String file; + + public DbgFileExecAndSymbolsCommand(DbgManagerImpl manager, String file) { + super(manager); + this.file = file; + } + + @Override + public void invoke() { + // TODO Auto-generated method stub + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgInsertBreakpointCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgInsertBreakpointCommand.java new file mode 100644 index 0000000000..7019ee6863 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgInsertBreakpointCommand.java @@ -0,0 +1,100 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.ArrayList; +import java.util.List; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugBreakpoint.*; +import agent.dbgeng.manager.breakpoint.*; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import ghidra.comm.util.BitmaskSet; + +/** + * Implementation of {@link DbgBreakpointInsertions#insertBreakpoint(String)} + */ +public class DbgInsertBreakpointCommand extends AbstractDbgCommand { + private List locations; + private final DbgBreakpointType type; + private DbgBreakpointInfo bkpt; + private int len; + + public DbgInsertBreakpointCommand(DbgManagerImpl manager, String expression, + DbgBreakpointType type) { + super(manager); + locations = new ArrayList<>(); + DebugSymbols symbols = manager.getSymbols(); + List ids = symbols.getSymbolIdsByName(expression); + if (ids.isEmpty()) { + locations.add(Long.decode(expression)); + } + else { + for (DebugSymbolId id : ids) { + DebugSymbolEntry entry = symbols.getSymbolEntry(id); + locations.add(entry.offset); + } + } + this.type = type; + } + + public DbgInsertBreakpointCommand(DbgManagerImpl manager, long loc, int len, + DbgBreakpointType type) { + super(manager); + locations = new ArrayList<>(); + locations.add(loc); + this.len = len; + this.type = type; + } + + @Override + public DbgBreakpointInfo complete(DbgPendingCommand pending) { + manager.doBreakpointCreated(bkpt, pending); + return bkpt; + } + + @Override + public void invoke() { + DebugControl control = manager.getControl(); + BreakType bt = BreakType.DATA; + if (type.equals(DbgBreakpointType.BREAKPOINT)) { + bt = BreakType.CODE; + } + DebugBreakpoint bp = control.addBreakpoint(bt); + bp.addFlags(BreakFlags.ENABLED); + if (bt.equals(BreakType.DATA)) { + BitmaskSet access = BitmaskSet.of(BreakAccess.EXECUTE); + if (type.equals(DbgBreakpointType.ACCESS_WATCHPOINT)) { + access = BitmaskSet.of(BreakAccess.READ, BreakAccess.WRITE); + } + if (type.equals(DbgBreakpointType.READ_WATCHPOINT)) { + access = BitmaskSet.of(BreakAccess.READ); + } + if (type.equals(DbgBreakpointType.HW_WATCHPOINT)) { + access = BitmaskSet.of(BreakAccess.WRITE); + } + if (type.equals(DbgBreakpointType.HW_BREAKPOINT)) { + access = BitmaskSet.of(BreakAccess.EXECUTE); + len = 1; + } + bp.setDataParameters(len, access); + } + for (Long loc : locations) { + bp.setOffset(loc); + bkpt = new DbgBreakpointInfo(bp, manager.getCurrentProcess()); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgKillCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgKillCommand.java new file mode 100644 index 0000000000..aabc0d8ec5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgKillCommand.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.evt.DbgProcessExitedEvent; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgProcess#kill()} + */ +public class DbgKillCommand extends AbstractDbgCommand { + public DbgKillCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public void invoke() { + DebugClient dbgeng = manager.getClient(); + // NB: process the event before terminating + manager.processEvent(new DbgProcessExitedEvent(0)); + dbgeng.terminateCurrentProcess(); + dbgeng.detachCurrentProcess(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgLaunchProcessCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgLaunchProcessCommand.java new file mode 100644 index 0000000000..0769d40540 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgLaunchProcessCommand.java @@ -0,0 +1,83 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugClient.DebugCreateFlags; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.evt.AbstractDbgCompletedCommandEvent; +import agent.dbgeng.manager.evt.DbgProcessCreatedEvent; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import ghidra.comm.util.BitmaskSet; + +/** + * Implementation of {@link DbgProcess#fileExecAndSymbols(String)} + */ +public class DbgLaunchProcessCommand extends AbstractDbgCommand { + + private DbgProcessCreatedEvent created = null; + private boolean completed = false; + private List args; + + public DbgLaunchProcessCommand(DbgManagerImpl manager, List args) { + super(manager); + this.args = args; + } + + @Override + public boolean handle(DbgEvent evt, DbgPendingCommand pending) { + if (evt instanceof AbstractDbgCompletedCommandEvent && pending.getCommand().equals(this)) { + completed = true; + } + else if (evt instanceof DbgProcessCreatedEvent) { + created = (DbgProcessCreatedEvent) evt; + } + return completed && (created != null); + } + + @Override + public DbgThread complete(DbgPendingCommand pending) { + DebugProcessInfo info = created.getInfo(); + DebugThreadInfo tinfo = info.initialThreadInfo; + DebugSystemObjects so = manager.getSystemObjects(); + DebugThreadId tid = so.getThreadIdByHandle(tinfo.handle); + return manager.getThread(tid); + } + + @Override + public void invoke() { + DebugClient dbgeng = manager.getClient(); + //DebugControl control = dbgeng.getControl(); + + List newArgs = new ArrayList<>(); + for (String arg : args) { + String na = arg; + if (arg.startsWith("/")) { + na = na.substring(1); + } + na = na.replace("/", "\\"); + newArgs.add(na); + } + dbgeng.createProcess(dbgeng.getLocalServer(), StringUtils.join(newArgs, " "), + BitmaskSet.of(DebugCreateFlags.DEBUG_PROCESS)); + manager.waitForEventEx(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListAvailableProcessesCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListAvailableProcessesCommand.java new file mode 100644 index 0000000000..557d61c2c7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListAvailableProcessesCommand.java @@ -0,0 +1,58 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +import agent.dbgeng.jna.dbgeng.Kernel32Extra.PROCESSENTRY32W; +import agent.dbgeng.jna.dbgeng.ToolhelpUtil; +import agent.dbgeng.jna.dbgeng.ToolhelpUtil.Snapshot; +import agent.dbgeng.jna.dbgeng.ToolhelpUtil.SnapshotFlags; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import ghidra.comm.util.BitmaskSet; + +public class DbgListAvailableProcessesCommand + extends AbstractDbgCommand>> { + + private Snapshot snap; + + public DbgListAvailableProcessesCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public List> complete(DbgPendingCommand pending) { + List> result = new ArrayList<>(); + for (PROCESSENTRY32W proc : snap.getProcesses()) { + int pid = proc.th32ProcessID.intValue(); + char[] name = proc.szExeFile; + String exe = new String(name); + result.add(new ImmutablePair<>(pid, exe)); + } + return result; + } + + @Override + public void invoke() { + snap = ToolhelpUtil + .createSnapshot(BitmaskSet.of(SnapshotFlags.PROCESS, SnapshotFlags.THREAD), 0); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListBreakpointsCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListBreakpointsCommand.java new file mode 100644 index 0000000000..651bb7ad71 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListBreakpointsCommand.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.*; + +import agent.dbgeng.dbgeng.DebugBreakpoint; +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgProcess#listBreakpoints()} + */ +public class DbgListBreakpointsCommand extends AbstractDbgCommand> { + + private List breakpoints; + + public DbgListBreakpointsCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public Map complete(DbgPendingCommand pending) { + Map list = new LinkedHashMap<>(); + for (DebugBreakpoint bpt : breakpoints) { + DbgBreakpointInfo info = new DbgBreakpointInfo(bpt, manager.getCurrentProcess()); + list.put((long) bpt.getId(), info); + } + return list; + } + + @Override + public void invoke() { + breakpoints = manager.getControl().getBreakpoints(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListKernelMemoryRegionsCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListKernelMemoryRegionsCommand.java new file mode 100644 index 0000000000..476397fda7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListKernelMemoryRegionsCommand.java @@ -0,0 +1,93 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.ArrayList; +import java.util.List; + +import com.sun.jna.platform.win32.COM.COMException; + +import agent.dbgeng.dbgeng.DebugDataSpaces; +import agent.dbgeng.dbgeng.DebugDataSpaces.*; +import agent.dbgeng.dbgeng.DebugModule; +import agent.dbgeng.dbgeng.DebugModule.DebugModuleName; +import agent.dbgeng.manager.DbgModuleMemory; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.manager.impl.DbgModuleMemoryImpl; + +public class DbgListKernelMemoryRegionsCommand extends AbstractDbgCommand> { + + private List memoryRegions = new ArrayList<>(); + + public DbgListKernelMemoryRegionsCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public List complete(DbgPendingCommand pending) { + return memoryRegions; + } + + @Override + public void invoke() { + DebugDataSpaces dataSpaces = manager.getDataSpaces(); + for (DebugMemoryBasicInformation info : dataSpaces.iterateVirtual(0)) { + if (info.state == PageState.FREE) { + continue; + } + String type = "[" + info.type + "]"; + if (info.type == PageType.IMAGE) { + try { + DebugModule mod = manager.getSymbols().getModuleByOffset(info.baseAddress, 0); + if (mod != null) { + type = mod.getName(DebugModuleName.IMAGE); + } + } + catch (COMException e) { + type = "[IMAGE UNKNOWN]"; + } + } + else if (info.type == PageType.MAPPED) { + // TODO: Figure out the file name + } + long vmaStart = info.baseAddress; + long vmaEnd = info.baseAddress + info.regionSize; + + boolean isRead = false; + boolean isWrite = false; + boolean isExec = false; + List ap = new ArrayList<>(); + for (PageProtection protect : info.allocationProtect) { + ap.add(protect.toString()); + isRead |= protect.isRead(); + isWrite |= protect.isWrite(); + isExec |= protect.isExecute(); + } + List ip = new ArrayList<>(); + for (PageProtection protect : info.protect) { + ip.add(protect.toString()); + isRead |= protect.isRead(); + isWrite |= protect.isWrite(); + isExec |= protect.isExecute(); + } + DbgModuleMemoryImpl section = + new DbgModuleMemoryImpl(Long.toHexString(vmaStart), vmaStart, vmaEnd, + info.allocationBase, ap, ip, info.state, type, isRead, isWrite, isExec); + memoryRegions.add(section); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListMappingsCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListMappingsCommand.java new file mode 100644 index 0000000000..24ab85ef28 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListMappingsCommand.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.*; + +import agent.dbgeng.dbgeng.DebugSystemObjects; +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.*; +import ghidra.util.Msg; + +public class DbgListMappingsCommand extends AbstractDbgCommand> { + protected final DbgProcessImpl process; + private List updatedThreadIds; + + public DbgListMappingsCommand(DbgManagerImpl manager, DbgProcessImpl process) { + super(manager); + this.process = process; + } + + @Override + public Map complete(DbgPendingCommand pending) { + Map threads = process.getKnownThreads(); + Set cur = threads.keySet(); + DebugSystemObjects so = manager.getSystemObjects(); + DebugThreadId previous = so.getCurrentThreadId(); + for (DebugThreadId id : updatedThreadIds) { + if (cur.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to create the thread as if we receive =thread-created + Msg.warn(this, "Resync: Was missing thread: " + id); + so.setCurrentThreadId(id); + int tid = so.getCurrentThreadSystemId(); + new DbgThreadImpl(manager, process, id, tid).add(); + } + for (DebugThreadId id : new ArrayList<>(cur)) { + if (updatedThreadIds.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to remove the thread as if we received =thread-exited + Msg.warn(this, "Resync: Had extra thread: " + id); + process.removeThread(id); + manager.removeThread(id); + } + so.setCurrentThreadId(previous); + return process.getKnownMappings(); + } + + @Override + public void invoke() { + //TODO + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListMemoryRegionsCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListMemoryRegionsCommand.java new file mode 100644 index 0000000000..a95abf372d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListMemoryRegionsCommand.java @@ -0,0 +1,93 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.ArrayList; +import java.util.List; + +import com.sun.jna.platform.win32.COM.COMException; + +import agent.dbgeng.dbgeng.DebugDataSpaces; +import agent.dbgeng.dbgeng.DebugDataSpaces.*; +import agent.dbgeng.dbgeng.DebugModule; +import agent.dbgeng.dbgeng.DebugModule.DebugModuleName; +import agent.dbgeng.manager.DbgModuleMemory; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.manager.impl.DbgModuleMemoryImpl; + +public class DbgListMemoryRegionsCommand extends AbstractDbgCommand> { + + private List memoryRegions = new ArrayList<>(); + + public DbgListMemoryRegionsCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public List complete(DbgPendingCommand pending) { + return memoryRegions; + } + + @Override + public void invoke() { + DebugDataSpaces dataSpaces = manager.getDataSpaces(); + for (DebugMemoryBasicInformation info : dataSpaces.iterateVirtual(0)) { + if (info.state == PageState.FREE) { + continue; + } + String type = "[" + info.type + "]"; + if (info.type == PageType.IMAGE) { + try { + DebugModule mod = manager.getSymbols().getModuleByOffset(info.baseAddress, 0); + if (mod != null) { + type = mod.getName(DebugModuleName.IMAGE); + } + } + catch (COMException e) { + type = "[IMAGE UNKNOWN]"; + } + } + else if (info.type == PageType.MAPPED) { + // TODO: Figure out the file name + } + long vmaStart = info.baseAddress; + long vmaEnd = info.baseAddress + info.regionSize; + + boolean isRead = false; + boolean isWrite = false; + boolean isExec = false; + List ap = new ArrayList<>(); + for (PageProtection protect : info.allocationProtect) { + ap.add(protect.toString()); + isRead |= protect.isRead(); + isWrite |= protect.isWrite(); + isExec |= protect.isExecute(); + } + List ip = new ArrayList<>(); + for (PageProtection protect : info.protect) { + ip.add(protect.toString()); + isRead |= protect.isRead(); + isWrite |= protect.isWrite(); + isExec |= protect.isExecute(); + } + DbgModuleMemoryImpl section = + new DbgModuleMemoryImpl(Long.toHexString(vmaStart), vmaStart, vmaEnd, + info.allocationBase, ap, ip, info.state, type, isRead, isWrite, isExec); + memoryRegions.add(section); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListModulesCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListModulesCommand.java new file mode 100644 index 0000000000..af0397410a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListModulesCommand.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.*; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugModule.DebugModuleName; +import agent.dbgeng.manager.DbgModule; +import agent.dbgeng.manager.impl.*; +import ghidra.util.Msg; + +public class DbgListModulesCommand extends AbstractDbgCommand> { + protected final DbgProcessImpl process; + private Map updatedModules = new HashMap<>(); + + public DbgListModulesCommand(DbgManagerImpl manager, DbgProcessImpl process) { + super(manager); + this.process = process; + } + + @Override + public Map complete(DbgPendingCommand pending) { + Map modules = process.getKnownModules(); + Set cur = modules.keySet(); + for (String id : updatedModules.keySet()) { + if (cur.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to create the thread as if we receive =thread-created + Msg.warn(this, "Resync: Was missing module: " + id); + new DbgModuleImpl(manager, process, id).add(); + } + for (String id : new ArrayList<>(cur)) { + if (updatedModules.containsKey(id)) { + continue; // Do nothing, we're in sync + } + process.removeModule(id); + } + return process.getKnownModules(); + } + + @Override + public void invoke() { + DebugSystemObjects so = manager.getSystemObjects(); + so.setCurrentProcessId(process.getId()); + DebugSymbols symbols = manager.getSymbols(); + for (DebugModule module : symbols.iterateModules(0)) { + updatedModules.put(module.getName(DebugModuleName.MODULE), module); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListProcessesCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListProcessesCommand.java new file mode 100644 index 0000000000..3e0da3bdff --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListProcessesCommand.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.*; + +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.dbgeng.DebugSystemObjects; +import agent.dbgeng.manager.DbgCause.Causes; +import agent.dbgeng.manager.DbgManager; +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.manager.impl.DbgProcessImpl; +import ghidra.util.Msg; + +/** + * Implementation of {@link DbgManager#listProcesses()} + */ +public class DbgListProcessesCommand extends AbstractDbgCommand> { + private List updatedProcessIds; + + public DbgListProcessesCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public Map complete(DbgPendingCommand pending) { + Map allProcesses = manager.getKnownProcesses(); + Set cur = allProcesses.keySet(); + for (DebugProcessId id : updatedProcessIds) { + if (cur.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to create the inferior as if we received =thread-group-created + Msg.warn(this, "Resync: Was missing group: i" + id); + DebugSystemObjects so = manager.getSystemObjects(); + so.setCurrentProcessId(id); + int pid = so.getCurrentProcessSystemId(); + manager.addProcess(new DbgProcessImpl(manager, id, pid), Causes.UNCLAIMED); + } + for (DebugProcessId id : new ArrayList<>(cur)) { + if (updatedProcessIds.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to remove the inferior as if we received =thread-group-removed + Msg.warn(this, "Resync: Had extra group: i" + id); + manager.removeProcess(id, Causes.UNCLAIMED); + } + return allProcesses; + } + + @Override + public void invoke() { + DebugSystemObjects so = manager.getSystemObjects(); + updatedProcessIds = so.getProcesses(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListRegisterDescriptionsCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListRegisterDescriptionsCommand.java new file mode 100644 index 0000000000..565e138288 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListRegisterDescriptionsCommand.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.*; + +import agent.dbgeng.dbgeng.DebugRegisters; +import agent.dbgeng.dbgeng.DebugRegisters.DebugRegisterDescription; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +public class DbgListRegisterDescriptionsCommand + extends AbstractDbgCommand> { + + private List list; + + public DbgListRegisterDescriptionsCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public List complete(DbgPendingCommand pending) { + return list; + } + + @Override + public void invoke() { + DebugRegisters registers = manager.getRegisters(); + Set descs = registers.getAllDescriptions(); + list = new ArrayList(); + for (DebugRegisterDescription desc : descs) { + list.add(desc); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListSessionsCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListSessionsCommand.java new file mode 100644 index 0000000000..be767249ec --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListSessionsCommand.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.*; + +import agent.dbgeng.dbgeng.DebugSessionId; +import agent.dbgeng.dbgeng.DebugSystemObjects; +import agent.dbgeng.manager.DbgCause.Causes; +import agent.dbgeng.manager.DbgManager; +import agent.dbgeng.manager.DbgSession; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.manager.impl.DbgSessionImpl; +import ghidra.util.Msg; + +/** + * Implementation of {@link DbgManager#listSessions()} + */ +public class DbgListSessionsCommand extends AbstractDbgCommand> { + private List updatedSessionIds = new ArrayList<>(); + + public DbgListSessionsCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public Map complete(DbgPendingCommand pending) { + Map allSessions = manager.getKnownSessions(); + Set cur = allSessions.keySet(); + for (DebugSessionId id : updatedSessionIds) { + if (cur.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to create the inferior as if we received =thread-group-created + Msg.warn(this, "Resync: Was missing group: i" + id); + manager.addSession(new DbgSessionImpl(manager, id), Causes.UNCLAIMED); + } + for (DebugSessionId id : new ArrayList<>(cur)) { + if (updatedSessionIds.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to remove the session as if we received =thread-group-removed + Msg.warn(this, "Resync: Had extra group: i" + id); + manager.removeSession(id, Causes.UNCLAIMED); + } + return allSessions; + } + + @Override + public void invoke() { + DebugSystemObjects so = manager.getSystemObjects(); + updatedSessionIds = so.getSessions(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListThreadsCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListThreadsCommand.java new file mode 100644 index 0000000000..1c406a3ec1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListThreadsCommand.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.*; + +import agent.dbgeng.dbgeng.DebugSystemObjects; +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.*; +import ghidra.util.Msg; + +public class DbgListThreadsCommand extends AbstractDbgCommand> { + protected final DbgProcessImpl process; + private List updatedThreadIds; + + public DbgListThreadsCommand(DbgManagerImpl manager, DbgProcessImpl process) { + super(manager); + this.process = process; + } + + @Override + public Map complete(DbgPendingCommand pending) { + Map threads = process.getKnownThreads(); + Set cur = threads.keySet(); + for (DebugThreadId id : updatedThreadIds) { + if (cur.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to create the thread as if we receive =thread-created + Msg.warn(this, "Resync: Was missing thread: " + id); + DebugSystemObjects so = manager.getSystemObjects(); + so.setCurrentThreadId(id); + int tid = so.getCurrentThreadSystemId(); + new DbgThreadImpl(manager, process, id, tid).add(); + } + for (DebugThreadId id : new ArrayList<>(cur)) { + if (updatedThreadIds.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to remove the thread as if we received =thread-exited + Msg.warn(this, "Resync: Had extra thread: " + id); + process.removeThread(id); + manager.removeThread(id); + } + return process.getKnownThreads(); + } + + @Override + public void invoke() { + DebugSystemObjects so = manager.getSystemObjects(); + so.setCurrentProcessId(process.getId()); + updatedThreadIds = so.getThreads(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgOpenDumpCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgOpenDumpCommand.java new file mode 100644 index 0000000000..ec50688ce3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgOpenDumpCommand.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.Map; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.evt.AbstractDbgCompletedCommandEvent; +import agent.dbgeng.manager.evt.DbgProcessCreatedEvent; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgProcess#fileExecAndSymbols(String)} + */ +public class DbgOpenDumpCommand extends AbstractDbgCommand { + + private DbgProcessCreatedEvent created = null; + private boolean completed = false; + private Map args; + + public DbgOpenDumpCommand(DbgManagerImpl manager, Map args) { + super(manager); + this.args = args; + } + + @Override + public boolean handle(DbgEvent evt, DbgPendingCommand pending) { + if (evt instanceof AbstractDbgCompletedCommandEvent && pending.getCommand().equals(this)) { + completed = true; + } + else if (evt instanceof DbgProcessCreatedEvent) { + created = (DbgProcessCreatedEvent) evt; + } + return completed && (created != null); + } + + @Override + public DbgThread complete(DbgPendingCommand pending) { + DebugProcessInfo info = created.getInfo(); + DebugThreadInfo tinfo = info.initialThreadInfo; + DebugSystemObjects so = manager.getSystemObjects(); + DebugThreadId tid = so.getThreadIdByHandle(tinfo.handle); + return manager.getThread(tid); + } + + @Override + public void invoke() { + DebugClient dbgeng = manager.getClient(); + //DebugControl control = dbgeng.getControl(); + + String f = (String) args.get("TraceOrDump"); + if (f.startsWith("/")) { + f = f.substring(1); + } + f = f.replace("/", "\\"); + dbgeng.openDumpFileWide(f); + manager.waitForEventEx(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgPendingCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgPendingCommand.java new file mode 100644 index 0000000000..6359ce08ff --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgPendingCommand.java @@ -0,0 +1,205 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.evt.AbstractDbgCompletedCommandEvent; +import agent.dbgeng.manager.evt.DbgCommandErrorEvent; + +/** + * A command queued on the dbgeng manager + * + * A {@link DbgCommand} is queued by wrapping it in a {@link DbgPendingCommand} and submitting it to + * the manager implementation's executor. This object also keep track of claimed/stolen events and + * provides convenience methods for sifting through them. + * + * @param the type "returned" by the command + */ +public class DbgPendingCommand extends CompletableFuture implements DbgCause { + private final DbgCommand cmd; + private final Set> evts = new LinkedHashSet<>(); + + /** + * Wrap a command for execution + * + * @param cmd the command + */ + public DbgPendingCommand(DbgCommand cmd) { + this.cmd = cmd; + } + + /** + * Get the command being executed + * + * @return cmd + */ + public DbgCommand getCommand() { + return cmd; + } + + /** + * Finish the execution of this command + */ + public void finish() { + //Msg.trace(this, "Finishing " + cmd); + try { + T result = cmd.complete(this); + complete(result); + } + catch (Throwable e) { + completeExceptionally(e); + } + } + + /** + * Handle an event + * + * This gives the command implementation the first chance to claim or steal an event + * + * @param evt the event + * @return true if the command is ready to be completed + */ + public boolean handle(DbgEvent evt) { + return cmd.handle(evt, this); + } + + /** + * Claim an event + * + * This stores the event for later retrieval and processing. + * + * @param evt the event + */ + public void claim(DbgEvent evt) { + evt.claim(this); + evts.add(evt); + } + + /** + * Steal an event + * + * This stores the event for later retrieval and processing. + * + * @param evt the event + */ + public void steal(DbgEvent evt) { + claim(evt); + evt.steal(); + } + + /** + * Assume a single event of particular type was claimed/stolen, and get that event + * + * @param cls the type of the event + * @return the event cast to the type + * @throws IllegalStateException if more than one event was claimed/stolen + * @throws ClassCastException if the event cannot be cast to the given type + */ + public > E castSingleEvent(Class cls) { + if (evts.size() != 1) { + throw new IllegalStateException("Command did not claim exactly one event"); + } + return cls.cast(evts.iterator().next()); + } + + /** + * Find the first claimed/stolen event of a given type + * + * @param cls the type of the event + * @return the event cast to the type + * @throws IllegalStateException if no event of the given type was claimed/stolen + */ + public > E findFirstOf(Class cls) { + for (DbgEvent evt : evts) { + if (cls.isAssignableFrom(evt.getClass())) { + return cls.cast(evt); + } + } + throw new IllegalStateException("Command did not claim any " + cls); + } + + /** + * Find all events claimed/stolen of a given type + * + * @param cls the type of the events + * @return the list of events cast to the type + */ + public > List findAllOf(Class cls) { + List found = new ArrayList<>(); + for (DbgEvent evt : evts) { + if (cls.isAssignableFrom(evt.getClass())) { + found.add(cls.cast(evt)); + } + } + return found; + } + + /** + * Assume exactly one event of the given type was claimed/stolen, and get that event + * + * @param cls the type of the event + * @return the event cast to the type + * @throws IllegalStateException if more than one event matches + */ + public > E findSingleOf(Class cls) { + List found = findAllOf(cls); + if (found.size() != 1) { + throw new IllegalStateException( + "Command did not claim exactly one " + cls + ". Have " + evts); + } + return found.get(0); + } + + /** + * Check that the command completed with one of the given results + * + * {@link DbgCommandErrorEvent} need not be listed. This method will handle it as a special case + * already. To avoid the special treatment, list it explicitly. + * + * @param classes the completion type to accept + * @return the completion event, cast to the greatest common subclass + */ + @SafeVarargs + public final E checkCompletion( + Class... classes) { + AbstractDbgCompletedCommandEvent completion = + findSingleOf(AbstractDbgCompletedCommandEvent.class); + // Allow query for exact class to override error interpretation + for (Class cls : classes) { + if (cls == completion.getClass()) { + return cls.cast(completion); + } + } + if (completion instanceof DbgCommandErrorEvent) { + throw new DbgCommandError(completion.getInfo(), cmd); + } + for (Class cls : classes) { + if (cls.isAssignableFrom(completion.getClass())) { + return cls.cast(completion); + } + } + throw new IllegalStateException( + "Command completed with " + completion + ", not any of " + Arrays.asList(classes)); + } + + @Override + public String toString() { + return super.toString() + "(" + cmd + ")"; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgProcessSelectCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgProcessSelectCommand.java new file mode 100644 index 0000000000..cd060c0772 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgProcessSelectCommand.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +public class DbgProcessSelectCommand extends AbstractDbgCommand { + private DbgProcess process; + + /** + * Select the given thread and frame level + * + * To simply select a thread, you should use frame 0 as the default. + * + * @param manager the manager to execute the command + * @param process the desired process + */ + public DbgProcessSelectCommand(DbgManagerImpl manager, DbgProcess process) { + super(manager); + this.process = process; + } + + @Override + public void invoke() { + if (process != null) { + DebugProcessId id = process.getId(); + if (id != null) { + manager.getSystemObjects().setCurrentProcessId(id); + } + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadBusDataCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadBusDataCommand.java new file mode 100644 index 0000000000..443da7dd2f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadBusDataCommand.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.nio.ByteBuffer; + +import com.google.common.collect.*; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#readMemory(long, ByteBuffer, int)} + */ +public class DbgReadBusDataCommand extends AbstractDbgCommand> { + + private final long addr; + private final ByteBuffer buf; + private final int len; + private final int busDataType; + private final int busNumber; + private final int slotNumber; + + private int readLen; + + public DbgReadBusDataCommand(DbgManagerImpl manager, long addr, ByteBuffer buf, int len, + int busDataType, int busNumber, int slotNumber) { + super(manager); + this.addr = addr; + this.buf = buf; + this.len = len; + this.busDataType = busDataType; + this.busNumber = busNumber; + this.slotNumber = slotNumber; + } + + @Override + public RangeSet complete(DbgPendingCommand pending) { + RangeSet rangeSet = TreeRangeSet.create(); + rangeSet.add(Range.closedOpen(addr, addr + readLen)); + return rangeSet; + } + + @Override + public void invoke() { + readLen = + manager.getDataSpaces().readBusData(busDataType, busNumber, slotNumber, addr, buf, len); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadControlCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadControlCommand.java new file mode 100644 index 0000000000..d09ae71759 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadControlCommand.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.nio.ByteBuffer; + +import com.google.common.collect.*; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#readMemory(long, ByteBuffer, int)} + */ +public class DbgReadControlCommand extends AbstractDbgCommand> { + + private final long addr; + private final ByteBuffer buf; + private final int len; + private int processor; + + private int readLen; + + public DbgReadControlCommand(DbgManagerImpl manager, long addr, ByteBuffer buf, int len, + int processor) { + super(manager); + this.addr = addr; + this.buf = buf; + this.len = len; + this.processor = processor; + } + + @Override + public RangeSet complete(DbgPendingCommand pending) { + RangeSet rangeSet = TreeRangeSet.create(); + rangeSet.add(Range.closedOpen(addr, addr + readLen)); + return rangeSet; + } + + @Override + public void invoke() { + readLen = manager.getDataSpaces().readControl(processor, addr, buf, len); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadDebuggerDataCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadDebuggerDataCommand.java new file mode 100644 index 0000000000..f39c2ce628 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadDebuggerDataCommand.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.nio.ByteBuffer; + +import com.google.common.collect.*; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#readMemory(long, ByteBuffer, int)} + */ +public class DbgReadDebuggerDataCommand extends AbstractDbgCommand> { + + private final long addr; + private final ByteBuffer buf; + private final int len; + + private int readLen; + + public DbgReadDebuggerDataCommand(DbgManagerImpl manager, long addr, ByteBuffer buf, int len) { + super(manager); + this.addr = addr; + this.buf = buf; + this.len = len; + } + + @Override + public RangeSet complete(DbgPendingCommand pending) { + RangeSet rangeSet = TreeRangeSet.create(); + rangeSet.add(Range.closedOpen(addr, addr + readLen)); + return rangeSet; + } + + @Override + public void invoke() { + readLen = manager.getDataSpaces().readDebuggerData((int) addr, buf, len); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadIoCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadIoCommand.java new file mode 100644 index 0000000000..795e58015f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadIoCommand.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.nio.ByteBuffer; + +import com.google.common.collect.*; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#readMemory(long, ByteBuffer, int)} + */ +public class DbgReadIoCommand extends AbstractDbgCommand> { + + private final long addr; + private final ByteBuffer buf; + private final int len; + private final int interfaceType; + private final int busNumber; + private final int addressSpace; + + private int readLen; + + public DbgReadIoCommand(DbgManagerImpl manager, long addr, ByteBuffer buf, int len, + int interfaceType, int busNumber, int addressSpace) { + super(manager); + this.addr = addr; + this.buf = buf; + this.len = len; + this.interfaceType = interfaceType; + this.busNumber = busNumber; + this.addressSpace = addressSpace; + } + + @Override + public RangeSet complete(DbgPendingCommand pending) { + RangeSet rangeSet = TreeRangeSet.create(); + rangeSet.add(Range.closedOpen(addr, addr + readLen)); + return rangeSet; + } + + @Override + public void invoke() { + readLen = + manager.getDataSpaces().readIo(interfaceType, busNumber, addressSpace, addr, buf, len); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadMemoryCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadMemoryCommand.java new file mode 100644 index 0000000000..1608ed517d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadMemoryCommand.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.nio.ByteBuffer; + +import com.google.common.collect.*; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#readMemory(long, ByteBuffer, int)} + */ +public class DbgReadMemoryCommand extends AbstractDbgCommand> { + + private final long addr; + private final ByteBuffer buf; + private final int len; + + private int readLen; + + public DbgReadMemoryCommand(DbgManagerImpl manager, long addr, ByteBuffer buf, int len) { + super(manager); + this.addr = addr; + this.buf = buf; + this.len = len; + } + + @Override + public RangeSet complete(DbgPendingCommand pending) { + RangeSet rangeSet = TreeRangeSet.create(); + rangeSet.add(Range.closedOpen(addr, addr + readLen)); + return rangeSet; + } + + @Override + public void invoke() { + readLen = manager.getDataSpaces().readVirtual(addr, buf, len); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadPhysicalMemoryCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadPhysicalMemoryCommand.java new file mode 100644 index 0000000000..81d6d992f9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadPhysicalMemoryCommand.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.nio.ByteBuffer; + +import com.google.common.collect.*; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#readMemory(long, ByteBuffer, int)} + */ +public class DbgReadPhysicalMemoryCommand extends AbstractDbgCommand> { + + private final long addr; + private final ByteBuffer buf; + private final int len; + + private int readLen; + + public DbgReadPhysicalMemoryCommand(DbgManagerImpl manager, long addr, ByteBuffer buf, + int len) { + super(manager); + this.addr = addr; + this.buf = buf; + this.len = len; + } + + @Override + public RangeSet complete(DbgPendingCommand pending) { + RangeSet rangeSet = TreeRangeSet.create(); + rangeSet.add(Range.closedOpen(addr, addr + readLen)); + return rangeSet; + } + + @Override + public void invoke() { + readLen = manager.getDataSpaces().readPhysical(addr, buf, len); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadRegistersCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadRegistersCommand.java new file mode 100644 index 0000000000..24d8dd96b2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgReadRegistersCommand.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.math.BigInteger; +import java.util.*; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.manager.DbgStackFrameOperations; +import agent.dbgeng.manager.impl.*; + +/** + * Implementation of {@link DbgStackFrameOperations#readRegisters(Set)} + */ +public class DbgReadRegistersCommand extends AbstractDbgCommand> { + + private final DbgThreadImpl thread; + private final Set regs; + private DebugRegisters registers; + private DebugThreadId previous; + + public DbgReadRegistersCommand(DbgManagerImpl manager, DbgThreadImpl thread, Integer frameId, + Set regs) { + super(manager); + this.thread = thread; + this.regs = regs; + } + + @Override + public Map complete(DbgPendingCommand pending) { + DebugSystemObjects so = manager.getSystemObjects(); + if (regs.isEmpty()) { + return Collections.emptyMap(); + } + Map result = new LinkedHashMap<>(); + for (DbgRegister r : regs) { + if (registers != null) { + DebugValue value = registers.getValueByName(r.getName()); + if (value != null) { + BigInteger bval = new BigInteger(value.encodeAsBytes()); + result.put(r, bval); + } + } + } + so.setCurrentThreadId(previous); + return result; + } + + @Override + public void invoke() { + DebugSystemObjects so = manager.getSystemObjects(); + previous = so.getCurrentThreadId(); + so.setCurrentThreadId(thread.getId()); + registers = manager.getClient().getRegisters(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRemoveProcessCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRemoveProcessCommand.java new file mode 100644 index 0000000000..e29743362d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRemoveProcessCommand.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.manager.DbgManager; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgManager#addProcess()} + */ +public class DbgRemoveProcessCommand extends AbstractDbgCommand { + private DebugProcessId id; + + public DbgRemoveProcessCommand(DbgManagerImpl manager, DebugProcessId id) { + super(manager); + this.id = id; + } + + @Override + public void invoke() { + manager.removeProcess(manager.getProcess(id)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRemoveSessionCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRemoveSessionCommand.java new file mode 100644 index 0000000000..5f9a941731 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRemoveSessionCommand.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugSessionId; +import agent.dbgeng.manager.DbgManager; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgManager#addProcess()} + */ +public class DbgRemoveSessionCommand extends AbstractDbgCommand { + private DebugSessionId id; + + public DbgRemoveSessionCommand(DbgManagerImpl manager, DebugSessionId id) { + super(manager); + this.id = id; + } + + @Override + public void invoke() { + manager.removeSession(manager.getSession(id)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRunCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRunCommand.java new file mode 100644 index 0000000000..3c36a5de74 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgRunCommand.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.evt.*; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgProcess#fileExecAndSymbols(String)} + */ +public class DbgRunCommand extends AbstractDbgCommand { + + public DbgRunCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public boolean handle(DbgEvent evt, DbgPendingCommand pending) { + if (evt instanceof AbstractDbgCompletedCommandEvent && pending.getCommand().equals(this)) { + pending.claim(evt); + return true; + } + else if (evt instanceof DbgRunningEvent) { + pending.claim(evt); + } + else if (evt instanceof DbgThreadCreatedEvent) { + pending.claim(evt); + } + return false; + } + + @Override + public DbgThread complete(DbgPendingCommand pending) { + // Just take the first thread. Others are considered clones. + DbgThreadCreatedEvent created = pending.findFirstOf(DbgThreadCreatedEvent.class); + DebugThreadInfo info = created.getInfo(); + DebugSystemObjects so = manager.getSystemObjects(); + DebugThreadId tid = so.getThreadIdByHandle(info.handle); + return manager.getThread(tid); + } + + @Override + public void invoke() { + // TODO Auto-generated method stub + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSessionSelectCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSessionSelectCommand.java new file mode 100644 index 0000000000..ef5908bb4c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSessionSelectCommand.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugSessionId; +import agent.dbgeng.manager.DbgSession; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +public class DbgSessionSelectCommand extends AbstractDbgCommand { + private DbgSession session; + + /** + * Select the given thread and frame level + * + * To simply select a thread, you should use frame 0 as the default. + * + * @param manager the manager to execute the command + * @param process the desired process + */ + public DbgSessionSelectCommand(DbgManagerImpl manager, DbgSession session) { + super(manager); + this.session = session; + } + + @Override + public void invoke() { + if (session != null) { + DebugSessionId id = session.getId(); + if (id != null) { + manager.getSystemObjects().setCurrentSystemId(id); + } + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgStackListFramesCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgStackListFramesCommand.java new file mode 100644 index 0000000000..f5e799150a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgStackListFramesCommand.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_STACK_FRAME; +import agent.dbgeng.manager.DbgStackFrame; +import agent.dbgeng.manager.impl.*; + +public class DbgStackListFramesCommand extends AbstractDbgCommand> { + protected final DbgThreadImpl thread; + private List result; + + public DbgStackListFramesCommand(DbgManagerImpl manager, DbgThreadImpl thread) { + super(manager); + this.thread = thread; + } + + @Override + public List complete(DbgPendingCommand pending) { + return result; + } + + @Override + public void invoke() { + result = new ArrayList<>(); + DebugSystemObjects so = manager.getSystemObjects(); + DebugThreadId previous = so.getCurrentThreadId(); + so.setCurrentThreadId(thread.getId()); + DebugStackInformation stackTrace = manager.getControl().getStackTrace(0L, 0L, 0L); + for (int i = 0; i < stackTrace.getNumberOfFrames(); i++) { + DEBUG_STACK_FRAME tf = stackTrace.getFrame(i); + //DbgStackFrame frame = new DbgStackFrameImpl(thread, tf.FrameNumber.intValue(), + // new BigInteger(Long.toHexString(tf.InstructionOffset.longValue()), 16), null); + DbgStackFrame frame = new DbgStackFrameImpl(thread, // + tf.FrameNumber.intValue(), // + new BigInteger(Long.toHexString(tf.InstructionOffset.longValue()), 16), // + tf.FuncTableEntry.longValue(), // + tf.FrameOffset.longValue(), // + tf.ReturnOffset.longValue(), // + tf.StackOffset.longValue(), // + tf.Virtual.booleanValue(), // + tf.Params[0].longValue(), // + tf.Params[1].longValue(), // + tf.Params[2].longValue(), // + tf.Params[3].longValue()); + result.add(frame); + } + so.setCurrentThreadId(previous); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgStepCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgStepCommand.java new file mode 100644 index 0000000000..7f719b68d7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgStepCommand.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugClient.DebugStatus; +import agent.dbgeng.dbgeng.DebugControl; +import agent.dbgeng.manager.DbgEvent; +import agent.dbgeng.manager.DbgManager.ExecSuffix; +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.evt.*; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#stepInstruction()} + */ +public class DbgStepCommand extends AbstractDbgCommand { + protected final ExecSuffix suffix; + + public DbgStepCommand(DbgManagerImpl manager, ExecSuffix suffix) { + super(manager); + this.suffix = suffix; + } + + @Override + public boolean handle(DbgEvent evt, DbgPendingCommand pending) { + if (evt instanceof AbstractDbgCompletedCommandEvent && pending.getCommand().equals(this)) { + return evt instanceof DbgCommandErrorEvent || + !pending.findAllOf(DbgRunningEvent.class).isEmpty(); + } + else if (evt instanceof DbgRunningEvent) { + // Event happens no matter which interpreter received the command + pending.claim(evt); + return !pending.findAllOf(AbstractDbgCompletedCommandEvent.class).isEmpty(); + } + return false; + } + + @Override + public void invoke() { + DebugControl control = manager.getControl(); + if (suffix.equals(ExecSuffix.STEP_INSTRUCTION)) { + control.setExecutionStatus(DebugStatus.STEP_INTO); + } + else if (suffix.equals(ExecSuffix.NEXT_INSTRUCTION)) { + control.setExecutionStatus(DebugStatus.STEP_OVER); + } + else if (suffix.equals(ExecSuffix.FINISH)) { + control.setExecutionStatus(DebugStatus.STEP_BRANCH); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgThreadSelectCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgThreadSelectCommand.java new file mode 100644 index 0000000000..97bb53bf95 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgThreadSelectCommand.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +public class DbgThreadSelectCommand extends AbstractDbgCommand { + private DbgThread thread; + + /** + * Select the given thread and frame level + * + * To simply select a thread, you should use frame 0 as the default. + * + * @param manager the manager to execute the command + * @param thread the desired thread + * @param frameId the desired frame level + */ + public DbgThreadSelectCommand(DbgManagerImpl manager, DbgThread thread, Integer frameId) { + super(manager); + this.thread = thread; + } + + @Override + public void invoke() { + DebugThreadId id = thread.getId(); + if (id != null) { + manager.getSystemObjects().setCurrentThreadId(id); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteBusDataCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteBusDataCommand.java new file mode 100644 index 0000000000..76769516d3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteBusDataCommand.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.nio.ByteBuffer; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#writeMemory(long, ByteBuffer, int)} + */ +public class DbgWriteBusDataCommand extends AbstractDbgCommand { + + private final long addr; + private final ByteBuffer buf; + private final int busDataType; + private final int busNumber; + private final int slotNumber; + private final int len; + + public DbgWriteBusDataCommand(DbgManagerImpl manager, long addr, ByteBuffer buf, int len, + int busDataType, int busNumber, int slotNumber) { + super(manager); + this.addr = addr; + this.busDataType = busDataType; + this.busNumber = busNumber; + this.slotNumber = slotNumber; + this.buf = buf.duplicate(); + this.len = len; + } + + @Override + public void invoke() { + manager.getDataSpaces() + .writeBusData(busDataType, busNumber, slotNumber, addr, buf, buf.remaining()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteControlCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteControlCommand.java new file mode 100644 index 0000000000..97a1a4863e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteControlCommand.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.nio.ByteBuffer; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#writeMemory(long, ByteBuffer, int)} + */ +public class DbgWriteControlCommand extends AbstractDbgCommand { + + private final long addr; + private final ByteBuffer buf; + private final int processor; + private final int len; + + public DbgWriteControlCommand(DbgManagerImpl manager, long addr, ByteBuffer buf, int len, + int processor) { + super(manager); + this.addr = addr; + this.processor = processor; + this.buf = buf.duplicate(); + this.len = len; + } + + @Override + public void invoke() { + manager.getDataSpaces().writeControl(processor, addr, buf, buf.remaining()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteIoCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteIoCommand.java new file mode 100644 index 0000000000..b833ef23e6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteIoCommand.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.nio.ByteBuffer; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#writeMemory(long, ByteBuffer, int)} + */ +public class DbgWriteIoCommand extends AbstractDbgCommand { + + private final long addr; + private final ByteBuffer buf; + private final int interfaceType; + private final int busNumber; + private final int addressSpace; + private final int len; + + public DbgWriteIoCommand(DbgManagerImpl manager, long addr, ByteBuffer buf, int len, + int interfaceType, int busNumber, int addressSpace) { + super(manager); + this.addr = addr; + this.interfaceType = interfaceType; + this.busNumber = busNumber; + this.addressSpace = addressSpace; + this.buf = buf.duplicate(); + this.len = len; + } + + @Override + public void invoke() { + manager.getDataSpaces() + .writeIo(interfaceType, busNumber, addressSpace, addr, buf, buf.remaining()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteMemoryCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteMemoryCommand.java new file mode 100644 index 0000000000..87c4b8c8fd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteMemoryCommand.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.nio.ByteBuffer; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#writeMemory(long, ByteBuffer, int)} + */ +public class DbgWriteMemoryCommand extends AbstractDbgCommand { + + private final long addr; + private final ByteBuffer buf; + private final int len; + + public DbgWriteMemoryCommand(DbgManagerImpl manager, long addr, ByteBuffer buf, int len) { + super(manager); + this.addr = addr; + this.buf = buf.duplicate(); + this.len = len; + } + + @Override + public void invoke() { + manager.getDataSpaces().writeVirtual(addr, buf, buf.remaining()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWritePhysicalMemoryCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWritePhysicalMemoryCommand.java new file mode 100644 index 0000000000..bf0e30474b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWritePhysicalMemoryCommand.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.nio.ByteBuffer; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +/** + * Implementation of {@link DbgThread#writeMemory(long, ByteBuffer, int)} + */ +public class DbgWritePhysicalMemoryCommand extends AbstractDbgCommand { + + private final long addr; + private final ByteBuffer buf; + private final int len; + + public DbgWritePhysicalMemoryCommand(DbgManagerImpl manager, long addr, ByteBuffer buf, + int len) { + super(manager); + this.addr = addr; + this.buf = buf.duplicate(); + this.len = len; + } + + @Override + public void invoke() { + manager.getDataSpaces().writePhysical(addr, buf, buf.remaining()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteRegistersCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteRegistersCommand.java new file mode 100644 index 0000000000..efcfa0ec5d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgWriteRegistersCommand.java @@ -0,0 +1,71 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.math.BigInteger; +import java.util.*; + +import com.sun.jna.platform.win32.COM.COMException; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugRegisters.DebugRegisterDescription; +import agent.dbgeng.dbgeng.DebugRegisters.DebugRegisterSource; +import agent.dbgeng.manager.DbgStackFrameOperations; +import agent.dbgeng.manager.impl.*; + +/** + * Implementation of {@link DbgStackFrameOperations#readRegisters(Set)} + */ +public class DbgWriteRegistersCommand extends AbstractDbgCommand { + + private final DbgThreadImpl thread; + private final Map regVals; + + public DbgWriteRegistersCommand(DbgManagerImpl manager, DbgThreadImpl thread, Integer frameId, + Map regVals) { + super(manager); + this.thread = thread; + this.regVals = regVals; + } + + @Override + public void invoke() { + DebugSystemObjects so = manager.getSystemObjects(); + DebugThreadId previous = so.getCurrentThreadId(); + so.setCurrentThreadId(thread.getId()); + DebugRegisters registers = manager.getRegisters(); + Map values = new LinkedHashMap<>(); + for (DbgRegister r : regVals.keySet()) { + try { + BigInteger val = regVals.get(r); + DebugRegisterDescription desc = registers.getDescription(r.getNumber()); + byte[] bytes = new byte[desc.type.byteLength]; + byte[] newBytes = val.toByteArray(); + for (int i = newBytes.length - 1, j = bytes.length - 1; i >= 0 && + j >= 0; i--, j--) { + bytes[j] = newBytes[i]; + } + DebugValue dv = desc.type.decodeBytes(bytes); + values.put(r.getNumber(), dv); + } + catch (COMException e) { + manager.getControl().errln("No register: " + r.getName()); + } + } + registers.setValues(DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE, values); + so.setCurrentThreadId(previous); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/AbstractDbgCompletedCommandEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/AbstractDbgCompletedCommandEvent.java new file mode 100644 index 0000000000..74e53b5041 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/AbstractDbgCompletedCommandEvent.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.manager.DbgState; + +public class AbstractDbgCompletedCommandEvent extends AbstractDbgEvent { + + public AbstractDbgCompletedCommandEvent(String message) { + super(message); + } + + public AbstractDbgCompletedCommandEvent() { + super(null); + } + + public static void main(String[] args) { + // TODO Auto-generated method stub + + } + + public DbgState newState() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/AbstractDbgEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/AbstractDbgEvent.java new file mode 100644 index 0000000000..42ecd00d3f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/AbstractDbgEvent.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugClient.DebugStatus; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.DbgCause.Causes; +import agent.dbgeng.manager.cmd.DbgPendingCommand; + +/** + * A base class for Dbg events + * + * @param the type of information detailing the event + */ +public abstract class AbstractDbgEvent implements DbgEvent { + private final T info; + protected DbgCause cause = Causes.UNCLAIMED; + protected boolean stolen = false; + protected DebugStatus status = DebugStatus.NO_CHANGE; + + /** + * Construct a new event with the given information + * + * @param info the information + */ + protected AbstractDbgEvent(T info) { + this.info = info; + } + + @Override + public T getInfo() { + return info; + } + + @Override + public void claim(DbgPendingCommand cmd) { + if (cause != Causes.UNCLAIMED) { + throw new IllegalStateException("Event is already claimed by " + cause); + } + cause = cmd; + } + + @Override + public DbgCause getCause() { + return cause; + } + + public DbgReason getReason() { + return DbgReason.getReason(null); + } + + @Override + public void steal() { + stolen = true; + } + + @Override + public boolean isStolen() { + return stolen; + } + + @Override + public String toString() { + return "<" + getClass().getSimpleName() + " " + info + " >"; + } + + @Override + public DbgState newState() { + return null; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointCreatedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointCreatedEvent.java new file mode 100644 index 0000000000..973b38a1fe --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointCreatedEvent.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; + +/** + * The event corresponding with "{@code =breakpoint-created}" + */ +public class DbgBreakpointCreatedEvent extends AbstractDbgEvent { + private final DbgBreakpointInfo bkptInfo; + + /** + * Construct a new event by parsing the tail for information + * + * The breakpoint information must be specified by GDB. + * + * @param info breakpoint info + * + */ + public DbgBreakpointCreatedEvent(DbgBreakpointInfo info) { + super(info); + this.bkptInfo = info; + } + + /** + * Get the breakpoint information + * + * @return the parsed, but not processed, breakpoint information + */ + public DbgBreakpointInfo getBreakpointInfo() { + return bkptInfo; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointDeletedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointDeletedEvent.java new file mode 100644 index 0000000000..1409dc905f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointDeletedEvent.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; + +/** + * The event corresponding with "{@code =breakpoint-deleted}" + */ +public class DbgBreakpointDeletedEvent extends AbstractDbgEvent { + private final long number; + + /** + * Construct a new event by parsing the tail for information + * + * The breakpoint number must be specified by GDB. + * + * @param info breakpoint info + */ + public DbgBreakpointDeletedEvent(DbgBreakpointInfo info) { + super(info); + this.number = info.getNumber(); + } + + /** + * Get the breakpoint number + * + * @return the breakpoint number + */ + public long getNumber() { + return number; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointEvent.java new file mode 100644 index 0000000000..51fc54dbff --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointEvent.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugBreakpoint; + +public class DbgBreakpointEvent extends AbstractDbgEvent { + + public DbgBreakpointEvent(DebugBreakpoint info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointModifiedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointModifiedEvent.java new file mode 100644 index 0000000000..02c3e75fa5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointModifiedEvent.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; + +/** + * The event corresponding with "{@code =breakpoint-modified}" + */ +public class DbgBreakpointModifiedEvent extends AbstractDbgEvent { + + private long bptId; + + /** + * Construct a new event by parsing the tail for information + * + * The breakpoint information must be specified by GDB. + * + * @param info breakpoint info + */ + public DbgBreakpointModifiedEvent(DbgBreakpointInfo info) { + super(info); + this.bptId = info.getNumber(); + } + + public DbgBreakpointModifiedEvent(long bptId) { + super(null); + this.bptId = bptId; + } + + /** + * Get the breakpoint information + * + * @return the parsed, but not processed, breakpoint information + */ + public DbgBreakpointInfo getBreakpointInfo() { + return getInfo(); + } + + public long getId() { + return bptId; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandDoneEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandDoneEvent.java new file mode 100644 index 0000000000..04d703ad8a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandDoneEvent.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.manager.DbgState; + +/** + * The event corresponding with "{@code ^done}" + */ +public class DbgCommandDoneEvent extends AbstractDbgCompletedCommandEvent { + + /** + * Construct a new event, parsing the tail for information + */ + public DbgCommandDoneEvent() { + super(); + } + + public DbgCommandDoneEvent(String info) { + super(info); + } + + @Override + public DbgState newState() { + return DbgState.STOPPED; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandErrorEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandErrorEvent.java new file mode 100644 index 0000000000..7f2549b0cd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandErrorEvent.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.manager.DbgEvent; +import agent.dbgeng.manager.DbgState; + +/** + * The event corresponding with "{@code ^error}" + */ +public class DbgCommandErrorEvent extends AbstractDbgCompletedCommandEvent { + + /** + * Construct a new event using the given error message + * + * @param message the message + * @return the new event + */ + public static DbgEvent fromMessage(String message) { + return new DbgCommandErrorEvent(message); + } + + protected DbgCommandErrorEvent() { + super(); + } + + protected DbgCommandErrorEvent(String message) { + super(message); + } + + @Override + public DbgState newState() { + return DbgState.STOPPED; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandRunningEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandRunningEvent.java new file mode 100644 index 0000000000..f33c06f079 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandRunningEvent.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.manager.DbgState; + +/** + * The event corresponding with "{@code ^running}" + */ +public class DbgCommandRunningEvent extends AbstractDbgCompletedCommandEvent { + + /** + * Construct a new event, parsing the tail for information + */ + public DbgCommandRunningEvent() { + super(); + } + + @Override + public DbgState newState() { + return DbgState.RUNNING; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgConsoleOutputEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgConsoleOutputEvent.java new file mode 100644 index 0000000000..2a751813e9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgConsoleOutputEvent.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +/** + * The event corresponding with "{@code ~""}" output records + */ +public class DbgConsoleOutputEvent extends AbstractDbgEvent { + + private int mask; + + public DbgConsoleOutputEvent(int mask, String info) { + super(info); + this.mask = mask; + } + + public String getOutput() { + return getInfo(); + } + + public int getMask() { + return mask; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgExceptionEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgExceptionEvent.java new file mode 100644 index 0000000000..fcb6a48ce9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgExceptionEvent.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugExceptionRecord64; + +public class DbgExceptionEvent extends AbstractDbgEvent { + + public DbgExceptionEvent(DebugExceptionRecord64 info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgModuleLoadedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgModuleLoadedEvent.java new file mode 100644 index 0000000000..a1fcb8797d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgModuleLoadedEvent.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugModuleInfo; + +public class DbgModuleLoadedEvent extends AbstractDbgEvent { + + public DbgModuleLoadedEvent(DebugModuleInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgModuleUnloadedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgModuleUnloadedEvent.java new file mode 100644 index 0000000000..fd2a07d421 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgModuleUnloadedEvent.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugModuleInfo; + +public class DbgModuleUnloadedEvent extends AbstractDbgEvent { + + public DbgModuleUnloadedEvent(DebugModuleInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessCreatedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessCreatedEvent.java new file mode 100644 index 0000000000..38b9bd14e9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessCreatedEvent.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugProcessInfo; + +public class DbgProcessCreatedEvent extends AbstractDbgEvent { + + public DbgProcessCreatedEvent(DebugProcessInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessExitedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessExitedEvent.java new file mode 100644 index 0000000000..f111737487 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessExitedEvent.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.manager.DbgState; + +public class DbgProcessExitedEvent extends AbstractDbgEvent { + + public DbgProcessExitedEvent(Integer exitCode) { + super(exitCode); + } + + @Override + public DbgState newState() { + return DbgState.EXIT; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessSelectedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessSelectedEvent.java new file mode 100644 index 0000000000..12326e064d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessSelectedEvent.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.manager.impl.DbgProcessImpl; + +/** + * The event corresponding with "{@code =thread-selected}" + */ +public class DbgProcessSelectedEvent extends AbstractDbgEvent { + private final DebugProcessId id; + private DbgProcessImpl process; + + /** + * The selected process ID must be specified by dbgeng. + * + * @param id dbgeng-defined id + */ + public DbgProcessSelectedEvent(DbgProcessImpl process) { + super(process.getId()); + this.process = process; + this.id = process.getId(); + } + + /** + * Get the selected process ID + * + * @return the process ID + */ + public DebugProcessId getProcessId() { + return id; + } + + public DbgProcessImpl getProcess() { + return process; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgRunningEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgRunningEvent.java new file mode 100644 index 0000000000..78c1c528dd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgRunningEvent.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.DbgState; + +/** + * The event corresponding with "{@code *running}" + */ +public class DbgRunningEvent extends AbstractDbgEvent { + private final DebugThreadId id; + + /** + * Construct a new event, parsing the tail for information + * + * A thread ID must be specified by dbgeng. + * + * @param id the event info + */ + public DbgRunningEvent(DebugThreadId id) { + super(id); + this.id = id; + } + + /** + * Get the ID of the thread causing the event + * + * @return the thread ID + */ + public DebugThreadId getThreadId() { + return id; + } + + @Override + public DbgState newState() { + return DbgState.RUNNING; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSessionSelectedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSessionSelectedEvent.java new file mode 100644 index 0000000000..350714e86e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSessionSelectedEvent.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugSessionId; +import agent.dbgeng.manager.impl.DbgSessionImpl; + +/** + * The event corresponding with "{@code =thread-selected}" + */ +public class DbgSessionSelectedEvent extends AbstractDbgEvent { + private final DebugSessionId id; + private DbgSessionImpl session; + + /** + * The selected session ID must be specified by dbgeng. + * + * @param session dbgeng-defined session + */ + public DbgSessionSelectedEvent(DbgSessionImpl session) { + super(session.getId()); + this.session = session; + this.id = session.getId(); + } + + /** + * Get the selected session ID + * + * @return the session ID + */ + public DebugSessionId getSessionId() { + return id; + } + + public DbgSessionImpl getSession() { + return session; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStateChangedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStateChangedEvent.java new file mode 100644 index 0000000000..a642200b63 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStateChangedEvent.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugClient.ChangeEngineState; +import agent.dbgeng.manager.DbgState; +import agent.dbgeng.manager.impl.DbgStackFrameImpl; +import agent.dbgeng.manager.impl.DbgThreadImpl; +import ghidra.comm.util.BitmaskSet; + +public class DbgStateChangedEvent extends AbstractDbgEvent> { + + private long argument; + private DbgState state = null; + + public DbgStateChangedEvent(BitmaskSet flags) { + super(flags); + } + + public long getArgument() { + return argument; + } + + public void setArgument(long argument) { + this.argument = argument; + } + + public DbgStackFrameImpl getFrame(DbgThreadImpl thread) { + return null; + } + + @Override + public DbgState newState() { + return state; + } + + public void setState(DbgState state) { + this.state = state; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStoppedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStoppedEvent.java new file mode 100644 index 0000000000..3df476fc79 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStoppedEvent.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.DbgState; +import agent.dbgeng.manager.impl.DbgStackFrameImpl; +import agent.dbgeng.manager.impl.DbgThreadImpl; + +/** + * The event corresponding with "{@code *stopped}" + */ +public class DbgStoppedEvent extends AbstractDbgEvent { + private final DebugThreadId id; + + /** + * Construct a new event, parsing the tail for information + * + * A thread ID must be specified by dbgeng. + * + * @param id the event info + */ + public DbgStoppedEvent(DebugThreadId id) { + super(id); + this.id = id; + } + + /** + * Get the ID of the thread causing the event + * + * @return the thread ID + */ + public DebugThreadId getThreadId() { + return id; + } + + /** + * Get the current frame, if applicable + * + * @param thread the current thread + * @return the frame + */ + public DbgStackFrameImpl getFrame(DbgThreadImpl thread) { + return null; + } + + @Override + public DbgState newState() { + return DbgState.STOPPED; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSystemsEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSystemsEvent.java new file mode 100644 index 0000000000..788851d79f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSystemsEvent.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +/** + * The event corresponding with "{@code =thread-selected}" + */ +public class DbgSystemsEvent extends AbstractDbgEvent { + private final long id; + + /** + * The selected target ID must be specified by dbgeng. + * + * @param targetID dbgeng-provided id + */ + public DbgSystemsEvent(Long targetID) { + super(targetID); + this.id = targetID; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadCreatedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadCreatedEvent.java new file mode 100644 index 0000000000..453361429a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadCreatedEvent.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugThreadInfo; + +public class DbgThreadCreatedEvent extends AbstractDbgEvent { + + public DbgThreadCreatedEvent(DebugThreadInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadExitedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadExitedEvent.java new file mode 100644 index 0000000000..71773502dd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadExitedEvent.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +public class DbgThreadExitedEvent extends AbstractDbgEvent { + + public DbgThreadExitedEvent(Integer exitCode) { + super(exitCode); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadSelectedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadSelectedEvent.java new file mode 100644 index 0000000000..dae89bf58b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadSelectedEvent.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.DbgState; +import agent.dbgeng.manager.impl.DbgStackFrameImpl; +import agent.dbgeng.manager.impl.DbgThreadImpl; + +/** + * The event corresponding with "{@code =thread-selected}" + */ +public class DbgThreadSelectedEvent extends AbstractDbgEvent { + private final DebugThreadId id; + private DbgState state; + private DbgThreadImpl thread; + private DbgStackFrameImpl frame; + + /** + * The selected thread ID must be specified by dbgeng. + * + * @param frame + * @param id dbgeng-provided id + */ + public DbgThreadSelectedEvent(DbgState state, DbgThreadImpl thread, DbgStackFrameImpl frame) { + super(thread.getId()); + this.id = thread.getId(); + this.state = state; + this.thread = thread; + this.frame = frame; + } + + /** + * Get the selected thread ID + * + * @return the thread ID + */ + public DebugThreadId getThreadId() { + return id; + } + + public DbgState getState() { + return state; + } + + public DbgThreadImpl getThread() { + return thread; + } + + public DbgStackFrameImpl getFrame() { + return frame; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugEventCallbacksAdapter.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugEventCallbacksAdapter.java new file mode 100644 index 0000000000..e26199ee9b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugEventCallbacksAdapter.java @@ -0,0 +1,131 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import java.nio.file.Paths; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugClient.ChangeEngineState; +import agent.dbgeng.dbgeng.DebugClient.DebugStatus; +import agent.dbgeng.dbgeng.util.DebugEventCallbacksAdapter; +import agent.dbgeng.manager.DbgState; +import agent.dbgeng.manager.evt.*; +import ghidra.comm.util.BitmaskSet; +import ghidra.util.Msg; + +public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter { + private DbgManagerImpl manager; + + public DbgDebugEventCallbacksAdapter(DbgManagerImpl manager) { + super(); + this.manager = manager; + } + + @Override + public DebugStatus breakpoint(DebugBreakpoint bp) { + Msg.info(this, "***Breakpoint: " + bp.getId()); + return manager.processEvent(new DbgBreakpointEvent(bp)); + } + + @Override + public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { + Msg.info(this, "***Exception: " + exception + ", first=" + firstChance); + return manager.processEvent(new DbgExceptionEvent(exception)); + } + + @Override + public DebugStatus createThread(DebugThreadInfo threadInfo) { + Msg.info(this, "***Thread created: " + Long.toHexString(threadInfo.handle)); + return manager.processEvent(new DbgThreadCreatedEvent(threadInfo)); + } + + @Override + public DebugStatus exitThread(int exitCode) { + Msg.info(this, "***Thread exited: " + exitCode); + return manager.processEvent(new DbgThreadExitedEvent(exitCode)); + } + + @Override + public DebugStatus createProcess(DebugProcessInfo processInfo) { + Msg.info(this, "***Process created: " + Long.toHexString(processInfo.handle)); + Msg.info(this, + " **Thread created: " + Long.toHexString(processInfo.initialThreadInfo.handle)); + return manager.processEvent(new DbgProcessCreatedEvent(processInfo)); + } + + @Override + public DebugStatus exitProcess(int exitCode) { + Msg.info(this, "***Process exited: " + exitCode); + Msg.info(this, " **Thread exited"); + return manager.processEvent(new DbgProcessExitedEvent(exitCode)); + } + + @Override + public DebugStatus loadModule(DebugModuleInfo moduleInfo) { + Msg.info(this, "***Module Loaded: " + moduleInfo); + return manager.processEvent(new DbgModuleLoadedEvent(moduleInfo)); + } + + @Override + public DebugStatus unloadModule(String imageBaseName, long baseOffset) { + Msg.info(this, + "***Module Unloaded: " + imageBaseName + ", " + Long.toHexString(baseOffset)); + DebugModuleInfo info = + new DebugModuleInfo(0L, baseOffset, 0, basename(imageBaseName), imageBaseName, 0, 0); + return manager.processEvent(new DbgModuleUnloadedEvent(info)); + } + + private String basename(String path) { + return Paths.get(path).getFileName().toString(); + } + + @Override + public DebugStatus changeEngineState(BitmaskSet flags, long argument) { + DbgStateChangedEvent event = new DbgStateChangedEvent(flags); + event.setArgument(argument); + if (flags.contains(ChangeEngineState.EXECUTION_STATUS)) { + if (DebugStatus.isInsideWait(argument)) { + return DebugStatus.NO_CHANGE; + } + Msg.info(this, "***ExecutionStatus: " + DebugStatus.fromArgument(argument)); + return manager.processEvent(event); + } + if (flags.contains(ChangeEngineState.BREAKPOINTS)) { + Msg.info(this, "***BreakpointChanged: " + flags + ", " + argument + " on " + + Thread.currentThread()); + return manager.processEvent(event); + } + if (flags.contains(ChangeEngineState.CURRENT_THREAD)) { + Msg.info(this, "***CurrentThread: " + argument); + if (argument < 0) { + return manager.processEvent(event); + } + } + if (flags.contains(ChangeEngineState.SYSTEMS)) { + Msg.info(this, "***Systems: " + argument); + event.setState(DbgState.RUNNING); + return manager.processEvent(event); + } + return DebugStatus.NO_CHANGE; + } + + //@Override + //public DebugStatus changeDebuggeeState(BitmaskSet flags, long argument) { + // System.err.println("CHANGE_DEBUGGEE_STATE: " + flags + ":" + argument); + // return DebugStatus.NO_CHANGE; + //} + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugOutputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugOutputCallbacks.java new file mode 100644 index 0000000000..4482d2fce8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugOutputCallbacks.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import agent.dbgeng.dbgeng.DebugOutputCallbacks; +import agent.dbgeng.manager.evt.DbgConsoleOutputEvent; + +public class DbgDebugOutputCallbacks implements DebugOutputCallbacks { + private DbgManagerImpl manager; + + public DbgDebugOutputCallbacks(DbgManagerImpl manager) { + this.manager = manager; + } + + @Override + public void output(int mask, String text) { + //System.out.print(text); + manager.processEvent(new DbgConsoleOutputEvent(mask, text)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java new file mode 100644 index 0000000000..e77bf5e55c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java @@ -0,0 +1,1508 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import static ghidra.async.AsyncUtils.*; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.commons.lang3.tuple.Pair; + +import com.sun.jna.platform.win32.COM.COMException; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugBreakpoint.BreakFlags; +import agent.dbgeng.dbgeng.DebugBreakpoint.BreakType; +import agent.dbgeng.dbgeng.DebugClient.*; +import agent.dbgeng.dbgeng.DebugControl.DebugInterrupt; +import agent.dbgeng.gadp.impl.AbstractClientThreadExecutor; +import agent.dbgeng.gadp.impl.DbgEngClientThreadExecutor; +import agent.dbgeng.impl.dbgeng.DbgEngUtil; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.DbgCause.Causes; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.manager.breakpoint.DbgBreakpointType; +import agent.dbgeng.manager.cmd.*; +import agent.dbgeng.manager.evt.*; +import ghidra.async.*; +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.error.DebuggerModelAccessException; +import ghidra.dbg.util.HandlerMap; +import ghidra.lifecycle.Internal; +import ghidra.util.Msg; +import ghidra.util.datastruct.ListenerSet; + +public class DbgManagerImpl implements DbgManager { + + private String dbgSrvTransport; + + //private final AsyncClaimQueue claimsCreateThread = new AsyncClaimQueue<>(); + //private final AsyncClaimQueue claimsContinueThread = new AsyncClaimQueue<>(); + //private final AsyncClaimQueue claimsStopThread = new AsyncClaimQueue<>(); + //private final AsyncClaimQueue claimsExitThread = new AsyncClaimQueue<>(); + //private final AsyncClaimQueue claimsLoadModule = new AsyncClaimQueue<>(); + private final AsyncClaimQueue claimsBreakpointAdded = new AsyncClaimQueue<>(); + private final AsyncClaimQueue claimsBreakpointRemoved = new AsyncClaimQueue<>(); + //private final AsyncClaimQueue claimsFocusThread = new AsyncClaimQueue<>(); + + public DebugStatus status; + + public final Set statiAccessible = + Collections.unmodifiableSet(EnumSet.of(DebugStatus.NO_DEBUGGEE, DebugStatus.BREAK)); + + private final Map breaksById = new LinkedHashMap<>(); + + protected AbstractClientThreadExecutor engThread; + protected DebugClientReentrant reentrantClient; + + private List> activeCmds = new ArrayList<>(); + + protected final Map sessions = new LinkedHashMap<>(); + protected DbgSessionImpl curSession = null; + private final Map unmodifiableSessions = + Collections.unmodifiableMap(sessions); + + protected final Map processes = new LinkedHashMap<>(); + protected DbgProcessImpl curProcess = null; + private final Map unmodifiableProcesses = + Collections.unmodifiableMap(processes); + + protected final Map threads = new LinkedHashMap<>(); + private final Map unmodifiableThreads = + Collections.unmodifiableMap(threads); + + private final Map breakpoints = new LinkedHashMap<>(); + private final Map unmodifiableBreakpoints = + Collections.unmodifiableMap(breakpoints); + + protected final AsyncReference state = + new AsyncReference<>(DbgState.NOT_STARTED); + private final HandlerMap, Void, DebugStatus> handlerMap = new HandlerMap<>(); + private final Map, DebugStatus> statusMap = new LinkedHashMap<>(); + private final Map statusByNameMap = new LinkedHashMap<>(); + protected final ListenerSet listenersEvent = + new ListenerSet<>(DbgEventsListener.class); + + private DebugEventInformation lastEventInformation; + private volatile boolean waiting = false; + private boolean kernelMode = false; + + /** + * Instantiate a new manager + */ + public DbgManagerImpl() { + state.filter(this::stateFilter); + state.addChangeListener(this::trackRunningInterpreter); + defaultHandlers(); + //TODO: this.server = createSctlSide(addr); + //TODO: this.dbgSrvTransport = dbgSrvTransport; + } + + /** + * Use {@link DbgThreadImpl#add()} instead + * + * @param thread the thread to add + */ + public void addThread(DbgThreadImpl thread) { + DbgThreadImpl exists = threads.get(thread.getId()); + if (exists != null) { + threads.remove(thread.getId()); + Msg.debug(this, "Replacing process " + thread.getId() + " (" + thread.getTid() + ")"); + //throw new IllegalArgumentException("There is already thread " + exists); + } + threads.put(thread.getId(), thread); + } + + @Override + public DbgThreadImpl getThread(DebugThreadId tid) { + return threads.get(tid); + } + + /** + * Use {@link DbgThreadImpl#remove()} instead + * + * @param id the thread ID to remove + */ + public void removeThread(DebugThreadId id) { + if (threads.remove(id) == null) { + throw new IllegalArgumentException("There is no thread with id " + id); + } + } + + /** + * Use {@link DbgProcessImpl#add(DbgCause)} instead + * + * @param process the process to add + * @param cause the cause of the new process + */ + @Override + @Internal + public void addProcess(DbgProcessImpl process, DbgCause cause) { + DbgProcessImpl exists = processes.get(process.getId()); + if (exists != null) { + processes.remove(process.getId()); + Msg.debug(this, "Replacing process " + process.getId() + " (" + process.getPid() + ")"); + //throw new IllegalArgumentException("There is already process " + exists); + } + processes.put(process.getId(), process); + listenersEvent.fire.processAdded(process, cause); + } + + /** + * Use {@link DbgSessionImpl#add(DbgCause)} instead + * + * @param session the session to add + * @param cause the cause of the new session + */ + @Override + @Internal + public void addSession(DbgSessionImpl session, DbgCause cause) { + DbgSession exists = sessions.get(session.getId()); + if (exists != null) { + sessions.remove(session.getId()); + Msg.debug(this, "Replacing session " + session.getId()); + //throw new IllegalArgumentException("There is already process " + exists); + } + sessions.put(session.getId(), session); + listenersEvent.fire.sessionAdded(session, cause); + } + + /** + * Use {@link DbgProcessImpl#remove(DbgCause)} instead + * + * @param id the process ID to remove + * @param cause the cause of removal + */ + @Internal + public void removeProcess(DebugProcessId id, DbgCause cause) { + if (processes.remove(id) == null) { + throw new IllegalArgumentException("There is no process with id " + id); + } + listenersEvent.fire.processRemoved(id, cause); + } + + /** + * Update the selected process + * + * @param process the process that now has focus + * @param cause the cause of the focus change + * @param fire signal listeners + * @return success status + */ + protected boolean updateCurrentProcess(DbgProcessImpl process, DbgCause cause, boolean fire) { + // dbgeng will not permit removing all processes, so one is guaranteed to exist + // dbgeng may actually have already selected it, but without generating events + if (process == null) { + process = processes.values().iterator().next(); + } + if (curProcess != process) { + curProcess = process; + if (fire) { + listenersEvent.fire.processSelected(process, cause); + } + return true; + } + return false; + } + + @Override + public DbgProcessImpl getProcess(DebugProcessId id) { + DbgProcessImpl result = processes.get(id); + if (result == null) { + throw new IllegalArgumentException("There is no process with id " + id); + } + return result; + } + + /** + * Use {@link DbgSessionImpl#remove(DbgCause)} instead + * + * @param id the session ID to remove + * @param cause the cause of removal + */ + @Internal + public void removeSession(DebugSessionId id, DbgCause cause) { + if (sessions.remove(id) == null) { + throw new IllegalArgumentException("There is no session with id " + id); + } + listenersEvent.fire.sessionRemoved(id, cause); + } + + @Override + public DbgSession getSession(DebugSessionId id) { + DbgSession result = sessions.get(id); + if (result == null) { + throw new IllegalArgumentException("There is no session with id " + id); + } + return result; + } + + @Override + public Map getKnownThreads() { + return unmodifiableThreads; + } + + @Override + public Map getKnownProcesses() { + return unmodifiableProcesses; + } + + @Override + public Map getKnownSessions() { + return unmodifiableSessions; + } + + @Override + public Map getKnownBreakpoints() { + return unmodifiableBreakpoints; + } + + private DbgBreakpointInfo addKnownBreakpoint(DbgBreakpointInfo bkpt, boolean expectExisting) { + DbgBreakpointInfo old = breakpoints.put(bkpt.getNumber(), bkpt); + if (expectExisting && old == null) { + Msg.warn(this, "Breakpoint " + bkpt.getNumber() + " is not known"); + } + else if (!expectExisting && old != null) { + Msg.warn(this, "Breakpoint " + bkpt.getNumber() + " is already known"); + } + return old; + } + + private DbgBreakpointInfo getKnownBreakpoint(long number) { + DbgBreakpointInfo info = breakpoints.get(number); + if (info == null) { + Msg.warn(this, "Breakpoint " + number + " is not known"); + } + return info; + } + + private DbgBreakpointInfo removeKnownBreakpoint(long number) { + DbgBreakpointInfo del = breakpoints.remove(number); + if (del == null) { + Msg.warn(this, "Breakpoint " + number + " is not known"); + } + return del; + } + + @Override + public CompletableFuture insertBreakpoint(String loc, + DbgBreakpointType type) { + return execute(new DbgInsertBreakpointCommand(this, loc, type)); + } + + @Override + public CompletableFuture insertBreakpoint(long loc, int len, + DbgBreakpointType type) { + return execute(new DbgInsertBreakpointCommand(this, loc, len, type)); + } + + @Override + public CompletableFuture disableBreakpoints(long... numbers) { + return execute(new DbgDisableBreakpointsCommand(this, numbers)); + } + + @Override + public CompletableFuture enableBreakpoints(long... numbers) { + return execute(new DbgEnableBreakpointsCommand(this, numbers)); + } + + @Override + public CompletableFuture deleteBreakpoints(long... numbers) { + return execute(new DbgDeleteBreakpointsCommand(this, numbers)); + } + + @Override + public CompletableFuture> listBreakpoints() { + return execute(new DbgListBreakpointsCommand(this)); + } + + private void checkStarted() { + if (state.get() == DbgState.NOT_STARTED) { + throw new IllegalStateException( + "dbgeng has not been started or has not finished starting"); + } + } + + @Override + public CompletableFuture start(String[] args) { + state.set(DbgState.STARTING, Causes.UNCLAIMED); + boolean create = true; + if (args.length == 0) { + engThread = new DbgEngClientThreadExecutor(() -> DbgEng.debugCreate().createClient()); + } + else { + String remoteOptions = String.join(" ", args); + engThread = new DbgEngClientThreadExecutor( + () -> DbgEng.debugConnect(remoteOptions).createClient()); + create = false; + } + engThread.setManager(this); + AtomicReference creat = new AtomicReference<>(create); + return sequence(TypeSpec.VOID).then(engThread, (seq) -> { + doExecute(creat.get()); + seq.exit(); + }).finish().exceptionally((exc) -> { + Msg.error(this, "start failed"); + return null; + }); + } + + protected void doExecute(Boolean create) { + DebugClient dbgeng = engThread.getClient(); + reentrantClient = dbgeng; + + status = dbgeng.getControl().getExecutionStatus(); + // Take control of the session. + // Helps if the JVM is using it for SA, or when starting a new server during testing. + if (create) { + dbgeng.endSession(DebugEndSessionFlags.DEBUG_END_ACTIVE_TERMINATE); + } + + status = dbgeng.getControl().getExecutionStatus(); + dbgeng.setOutputCallbacks(new DbgDebugOutputCallbacks(this)); + dbgeng.setEventCallbacks(new DbgDebugEventCallbacksAdapter(this)); + dbgeng.flushCallbacks(); + + if (!create) { + dbgeng.connectSession(0); + } + + if (dbgSrvTransport != null && !"none".equalsIgnoreCase(dbgSrvTransport)) { + dbgeng.startServer(dbgSrvTransport); + } + } + + @Override + public boolean isRunning() { + return !engThread.isShutdown() && !engThread.isTerminated(); + } + + @Override + public void terminate() { + //TODO: server.terminate(); + engThread.execute(100, dbgeng -> { + Msg.debug(this, "Disconnecting DebugClient from session"); + dbgeng.endSession(DebugEndSessionFlags.DEBUG_END_DISCONNECT); + dbgeng.setOutputCallbacks(null); + }); + engThread.shutdown(); + try { + engThread.awaitTermination(5000, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) { + // Eh, just go on + } + } + + @Override + public void close() throws Exception { + terminate(); + } + + /** + * Schedule a command for execution + * + * @param cmd the command to execute + * @return the pending command, which acts as a future for later completion + */ + //@Override + @Override + public CompletableFuture execute(DbgCommand cmd) { + assert cmd != null; + checkStarted(); + DbgPendingCommand pcmd = new DbgPendingCommand<>(cmd); + if (isWaiting()) { + throw new DebuggerModelAccessException( + "Cannot process command " + cmd.toString() + " while engine is waiting for events"); + } + sequence(TypeSpec.VOID).then(engThread, (seq) -> { + synchronized (this) { + if (!cmd.validInState(state.get())) { + throw new DbgCommandError( + "Command " + cmd + " is not valid while " + state.get()); + } + activeCmds.add(pcmd); + } + cmd.invoke(); + processEvent(new DbgCommandDoneEvent(cmd.toString())); + seq.exit(); + }).finish().exceptionally((exc) -> { + pcmd.completeExceptionally(exc); + return null; + }); + return pcmd; + } + + /*@Override + public DbgPendingCommand execute1(DbgCommand cmd) { + assert cmd != null; + checkStarted(); + DbgPendingCommand pcmd = new DbgPendingCommand<>(cmd); + sequence(TypeSpec.VOID).then((seq) -> { + Msg.debug(this, "WAITING cmdLock: " + pcmd); + cmdLock.acquire(null).handle(seq::next); + }, cmdLockHold).then((seq) -> { + Msg.debug(this, "ACQUIRED cmdLock: " + pcmd); + synchronized (this) { + if (curCmd != null) { + throw new AssertionError("Cannot execute more than one command at a time"); + } + if (!cmd.validInState(state.get())) { + throw new DbgCommandError( + "Command " + cmd + " is not valid while " + state.get()); + } + curCmd = pcmd; + } + cmd.invoke(); + processEvent(new DbgCommandDoneEvent(cmd.toString())); + seq.exit(); + }).finish().exceptionally((exc) -> { + pcmd.completeExceptionally(exc); + Msg.debug(this, "ON_EXCEPTION: CURCMD = " + curCmd); + curCmd = null; + Msg.debug(this, "SET CURCMD = null"); + Msg.debug(this, "RELEASING cmdLock"); + cmdLockHold.getAndSet(null).release(); + return null; + }); + return pcmd; + } + */ + + public DebugStatus processEvent(DbgEvent evt) { + if (state.get() == DbgState.STARTING) { + state.set(DbgState.STOPPED, Causes.UNCLAIMED); + } + DbgState newState = evt.newState(); + if (newState != null && !(evt instanceof DbgCommandDoneEvent)) { + Msg.debug(this, evt + " transitions state to " + newState); + state.set(newState, evt.getCause()); + } + + boolean cmdFinished = false; + List> toRemove = new ArrayList>(); + for (DbgPendingCommand pcmd : activeCmds) { + cmdFinished = pcmd.handle(evt); + if (cmdFinished) { + pcmd.finish(); + toRemove.add(pcmd); + } + } + for (DbgPendingCommand pcmd : toRemove) { + activeCmds.remove(pcmd); + } + + synchronized (this) { + boolean waitState = isWaiting(); + waiting = false; + DebugStatus ret = handlerMap.handle(evt, null); + if (ret == null) { + ret = DebugStatus.NO_CHANGE; + } + waiting = ret.equals(DebugStatus.NO_DEBUGGEE) ? false : waitState; + return ret; + } + } + + @Override + public void addStateListener(DbgStateListener listener) { + state.addChangeListener(listener); + } + + @Override + public void removeStateListener(DbgStateListener listener) { + state.removeChangeListener(listener); + } + + @Override + public void addEventsListener(DbgEventsListener listener) { + listenersEvent.add(listener); + } + + @Override + public void removeEventsListener(DbgEventsListener listener) { + listenersEvent.remove(listener); + } + + private DbgState stateFilter(DbgState cur, DbgState set, DbgCause cause) { + if (set == null) { + return cur; + } + return set; + } + + private void trackRunningInterpreter(DbgState oldSt, DbgState st, DbgCause cause) { + if (st == DbgState.RUNNING && cause instanceof DbgPendingCommand) { + DbgPendingCommand pcmd = (DbgPendingCommand) cause; + DbgCommand command = pcmd.getCommand(); + Msg.debug(this, "Entered " + st + " from " + command); + } + } + + private void defaultHandlers() { + handlerMap.put(DbgBreakpointEvent.class, this::processBreakpoint); + handlerMap.put(DbgExceptionEvent.class, this::processException); + handlerMap.put(DbgThreadCreatedEvent.class, this::processThreadCreated); + handlerMap.put(DbgThreadExitedEvent.class, this::processThreadExited); + handlerMap.put(DbgThreadSelectedEvent.class, this::processThreadSelected); + handlerMap.put(DbgProcessCreatedEvent.class, this::processProcessCreated); + handlerMap.put(DbgProcessExitedEvent.class, this::processProcessExited); + handlerMap.put(DbgProcessSelectedEvent.class, this::processProcessSelected); + handlerMap.put(DbgModuleLoadedEvent.class, this::processModuleLoaded); + handlerMap.put(DbgModuleUnloadedEvent.class, this::processModuleUnloaded); + handlerMap.put(DbgStateChangedEvent.class, this::processStateChanged); + handlerMap.put(DbgSessionSelectedEvent.class, this::processSessionSelected); + handlerMap.put(DbgSystemsEvent.class, this::processSystemsEvent); + handlerMap.putVoid(DbgCommandDoneEvent.class, this::processDefault); + handlerMap.putVoid(DbgStoppedEvent.class, this::processDefault); + handlerMap.putVoid(DbgRunningEvent.class, this::processDefault); + handlerMap.putVoid(DbgConsoleOutputEvent.class, this::processConsoleOutput); + handlerMap.putVoid(DbgBreakpointCreatedEvent.class, this::processBreakpointCreated); + handlerMap.putVoid(DbgBreakpointModifiedEvent.class, this::processBreakpointModified); + handlerMap.putVoid(DbgBreakpointDeletedEvent.class, this::processBreakpointDeleted); + + statusMap.put(DbgBreakpointEvent.class, DebugStatus.BREAK); + statusMap.put(DbgExceptionEvent.class, DebugStatus.BREAK); + statusMap.put(DbgProcessCreatedEvent.class, DebugStatus.BREAK); + statusMap.put(DbgStateChangedEvent.class, DebugStatus.NO_CHANGE); + statusMap.put(DbgStoppedEvent.class, DebugStatus.BREAK); + } + + private DebugThreadId updateState() { + DebugClient dbgeng = engThread.getClient(); + DebugSystemObjects so = dbgeng.getSystemObjects(); + DebugThreadId etid = so.getEventThread(); + DebugProcessId epid = so.getEventProcess(); + DebugSessionId esid = so.getCurrentSystemId(); + so.setCurrentProcessId(epid); + so.setCurrentThreadId(etid); + + DebugControl control = dbgeng.getControl(); + int execType = control.getExecutingProcessorType(); + lastEventInformation = control.getLastEventInformation(); + lastEventInformation.setSession(esid); + lastEventInformation.setExecutingProcessorType(execType); + DbgThreadImpl eventThread = threads.get(etid); + if (eventThread != null) { + eventThread.setInfo(lastEventInformation); + } + return etid; + } + + /** + * Default handler for events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processDefault(AbstractDbgEvent evt, Void v) { + //updateState(); + return statusMap.get(evt.getClass()); + } + + /** + * Handler for breakpoint events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processBreakpoint(DbgBreakpointEvent evt, Void v) { + updateState(); + + DebugBreakpoint bp = evt.getInfo(); + DbgBreakpointInfo info = new DbgBreakpointInfo(bp, getEventProcess(), getEventThread()); + listenersEvent.fire.breakpointHit(info, evt.getCause()); + + String key = Integer.toHexString(bp.getId()); + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for breakpoint events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processException(DbgExceptionEvent evt, Void v) { + DebugThreadId eventId = updateState(); + + DebugExceptionRecord64 info = evt.getInfo(); + String key = Integer.toHexString(info.code); + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for thread created events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processThreadCreated(DbgThreadCreatedEvent evt, Void v) { + DebugClient dbgeng = engThread.getClient(); + DebugSystemObjects so = dbgeng.getSystemObjects(); + + DebugThreadId eventId = updateState(); + DbgProcessImpl process = getCurrentProcess(); + int tid = so.getCurrentThreadSystemId(); + DbgThreadImpl thread = new DbgThreadImpl(this, process, eventId, tid); + thread.add(); + listenersEvent.fire.threadCreated(thread, evt.getCause()); + listenersEvent.fire.threadSelected(thread, null, evt.getCause()); + + String key = Integer.toHexString(eventId.id); + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for thread exited events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processThreadExited(DbgThreadExitedEvent evt, Void v) { + DebugThreadId eventId = updateState(); + DbgProcessImpl process = getCurrentProcess(); + DbgThreadImpl thread = getCurrentThread(); + if (thread != null) { + thread.remove(); + } + process.threadExited(eventId); + listenersEvent.fire.threadExited(eventId, process, evt.getCause()); + + String key = Integer.toHexString(eventId.id); + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for thread selected events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processThreadSelected(DbgThreadSelectedEvent evt, Void v) { + DebugThreadId eventId = updateState(); + + DbgThreadImpl thread = evt.getThread(); + thread.setState(evt.getState(), evt.getCause(), evt.getReason()); + listenersEvent.fire.threadSelected(thread, evt.getFrame(), evt.getCause()); + + String key = Integer.toHexString(eventId.id); + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for process created events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processProcessCreated(DbgProcessCreatedEvent evt, Void v) { + DebugClient dbgeng = engThread.getClient(); + DebugSystemObjects so = dbgeng.getSystemObjects(); + + DebugProcessInfo info = evt.getInfo(); + long handle = info.handle; + DebugProcessId id = so.getProcessIdByHandle(handle); + so.setCurrentProcessId(id); + int pid = so.getCurrentProcessSystemId(); + DbgProcessImpl proc = new DbgProcessImpl(this, id, pid); + proc.add(evt.getCause()); + listenersEvent.fire.processAdded(proc, evt.getCause()); + listenersEvent.fire.processSelected(proc, evt.getCause()); + + handle = info.initialThreadInfo.handle; + DebugThreadId idt = so.getThreadIdByHandle(handle); + int tid = so.getCurrentThreadSystemId(); + DbgThreadImpl thread = new DbgThreadImpl(this, proc, idt, tid); + thread.add(); + listenersEvent.fire.threadCreated(thread, evt.getCause()); + listenersEvent.fire.threadSelected(thread, null, evt.getCause()); + + DebugModuleInfo moduleInfo = info.moduleInfo; + String moduleName = moduleInfo.moduleName; + proc.moduleLoaded(moduleName, moduleInfo); + listenersEvent.fire.moduleLoaded(proc, moduleName, evt.getCause()); + + String key = Integer.toHexString(id.id); + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for process exited events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processProcessExited(DbgProcessExitedEvent evt, Void v) { + DebugThreadId eventId = updateState(); + + DbgProcessImpl process = getCurrentProcess(); + process.remove(evt.getCause()); + listenersEvent.fire.processRemoved(process.getId(), evt.getCause()); + + DbgThreadImpl thread = getCurrentThread(); + if (thread != null) { + thread.remove(); + } + listenersEvent.fire.threadExited(eventId, process, evt.getCause()); + + for (DebugBreakpoint bpt : getBreakpoints()) { + breaksById.remove(bpt.getId()); + } + + String key = Integer.toHexString(process.getId().id); + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for process selected events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processProcessSelected(DbgProcessSelectedEvent evt, Void v) { + DebugThreadId eventId = updateState(); + + DbgProcessImpl process = evt.getProcess(); + listenersEvent.fire.processSelected(process, evt.getCause()); + + String key = Integer.toHexString(eventId.id); + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for module loaded events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processModuleLoaded(DbgModuleLoadedEvent evt, Void v) { + updateState(); + DbgProcessImpl process = getCurrentProcess(); + DebugModuleInfo info = evt.getInfo(); + String moduleName = info.moduleName; + process.moduleLoaded(moduleName, info); + listenersEvent.fire.moduleLoaded(process, moduleName, evt.getCause()); + + String key = info.moduleName; + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for module unloaded events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processModuleUnloaded(DbgModuleUnloadedEvent evt, Void v) { + updateState(); + DbgProcessImpl process = getCurrentProcess(); + DebugModuleInfo info = evt.getInfo(); + String moduleName = info.moduleName; + process.moduleUnloaded(moduleName); + listenersEvent.fire.moduleUnloaded(process, moduleName, evt.getCause()); + + String key = info.moduleName; + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for state changed events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processStateChanged(DbgStateChangedEvent evt, Void v) { + BitmaskSet flags = evt.getInfo(); + long argument = evt.getArgument(); + if (flags.contains(ChangeEngineState.EXECUTION_STATUS)) { + if (DebugStatus.isInsideWait(argument)) { + return DebugStatus.NO_CHANGE; + } + status = DebugStatus.fromArgument(argument); + + if (status.equals(DebugStatus.NO_DEBUGGEE)) { + waiting = false; + return DebugStatus.NO_DEBUGGEE; + } + if (!threads.isEmpty()) { + //DbgSessionImpl session = getCurrentSession(); + //DbgProcessImpl process = getCurrentProcess(); + DbgThreadImpl thread = getCurrentThread(); + DbgState dbgState = null; + if (thread != null) { + if (status.threadState.equals(ExecutionState.STOPPED)) { + dbgState = DbgState.STOPPED; + //System.err.println("STOPPED " + id); + processEvent(new DbgStoppedEvent(thread.getId())); + } + if (status.threadState.equals(ExecutionState.RUNNING)) { + //System.err.println("RUNNING " + id); + dbgState = DbgState.RUNNING; + processEvent(new DbgRunningEvent(thread.getId())); + } + // Don't fire + if (dbgState != null) { + processEvent( + new DbgThreadSelectedEvent(dbgState, thread, evt.getFrame(thread))); + } + return DebugStatus.NO_CHANGE; + } + } + if (status.equals(DebugStatus.BREAK)) { + waiting = false; + processEvent(new DbgStoppedEvent(getSystemObjects().getCurrentThreadId())); + DbgProcessImpl process = getCurrentProcess(); + if (process != null) { + processEvent(new DbgProcessSelectedEvent(process)); + } + return DebugStatus.BREAK; + } + if (status.equals(DebugStatus.GO)) { + waiting = true; + processEvent(new DbgRunningEvent(getSystemObjects().getCurrentThreadId())); + return DebugStatus.GO; + } + waiting = false; + return DebugStatus.NO_CHANGE; + } + if (flags.contains(ChangeEngineState.BREAKPOINTS)) { + long bptId = evt.getArgument(); + //System.err.println("BPT: " + bptId + ":" + flags + ":" + argument); + processEvent(new DbgBreakpointModifiedEvent(bptId)); + } + if (flags.contains(ChangeEngineState.CURRENT_THREAD)) { + // handled above + } + if (flags.contains(ChangeEngineState.SYSTEMS)) { + processEvent(new DbgSystemsEvent(argument)); + } + return DebugStatus.NO_CHANGE; + } + + /** + * Handler for session selected events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processSessionSelected(DbgSessionSelectedEvent evt, Void v) { + DebugThreadId eventId = updateState(); + + DbgSessionImpl session = evt.getSession(); + listenersEvent.fire.sessionSelected(session, evt.getCause()); + + String key = Integer.toHexString(eventId.id); + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for systems events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processSystemsEvent(DbgSystemsEvent evt, Void v) { + + waiting = true; + + Long info = evt.getInfo(); + DebugProcessId id = new DebugProcessId(info.intValue()); + + String key = Integer.toHexString(id.id); + if (statusByNameMap.containsKey(key)) { + return statusByNameMap.get(key); + } + return statusMap.get(evt.getClass()); + } + + protected void processConsoleOutput(DbgConsoleOutputEvent evt, Void v) { + listenersEvent.fire.consoleOutput(evt.getInfo(), evt.getMask()); + } + + /** + * Handler for breakpoint-created event + * + * @param evt the event + * @param v nothing + */ + protected void processBreakpointCreated(DbgBreakpointCreatedEvent evt, Void v) { + doBreakpointCreated(evt.getBreakpointInfo(), evt.getCause()); + } + + /** + * Handler for breakpoint-modified event + * + * @param evt the event + * @param v nothing + */ + protected void processBreakpointModified(DbgBreakpointModifiedEvent evt, Void v) { + DbgBreakpointInfo breakpointInfo = evt.getBreakpointInfo(); + if (breakpointInfo == null) { + long bptId = evt.getId(); + if (bptId == DbgEngUtil.DEBUG_ANY_ID.longValue()) { + changeBreakpoints(); + } + DebugBreakpoint bpt = getControl().getBreakpointById((int) bptId); + if (bpt == null) { + doBreakpointDeleted(bptId, evt.getCause()); + return; + } + DbgBreakpointInfo knownBreakpoint = getKnownBreakpoint(bptId); + if (knownBreakpoint == null) { + breakpointInfo = new DbgBreakpointInfo(bpt, getCurrentProcess()); + if (!breakpointInfo.getLocation().equals("0")) { + doBreakpointCreated(breakpointInfo, evt.getCause()); + } + return; + } + breakpointInfo = knownBreakpoint; + } + doBreakpointModified(breakpointInfo, evt.getCause()); + } + + /** + * Handler for breakpoint-deleted event + * + * @param evt the event + * @param v nothing + */ + protected void processBreakpointDeleted(DbgBreakpointDeletedEvent evt, Void v) { + doBreakpointDeleted(evt.getNumber(), evt.getCause()); + } + + /** + * Fire breakpoint created event + * + * @param newInfo the new information + * @param cause the cause of the creation + */ + @Internal + public void doBreakpointCreated(DbgBreakpointInfo newInfo, DbgCause cause) { + addKnownBreakpoint(newInfo, false); + listenersEvent.fire.breakpointCreated(newInfo, cause); + } + + /** + * Fire breakpoint modified event + * + * @param newInfo the new information + * @param cause the cause of the modification + */ + @Internal + public void doBreakpointModified(DbgBreakpointInfo newInfo, DbgCause cause) { + DbgBreakpointInfo oldInfo = addKnownBreakpoint(newInfo, true); + listenersEvent.fire.breakpointModified(newInfo, oldInfo, cause); + } + + /** + * Fire breakpoint deleted event + * + * @param number the deleted breakpoint number + * @param cause the cause of the deletion + */ + @Internal + public void doBreakpointDeleted(long number, DbgCause cause) { + DbgBreakpointInfo oldInfo = removeKnownBreakpoint(number); + if (oldInfo == null) { + return; + } + listenersEvent.fire.breakpointDeleted(oldInfo, cause); + } + + protected void doBreakpointModifiedSameLocations(DbgBreakpointInfo newInfo, + DbgBreakpointInfo oldInfo, DbgCause cause) { + if (Objects.equals(newInfo, oldInfo)) { + return; + } + listenersEvent.fire.breakpointModified(newInfo, oldInfo, cause); + } + + @Internal + public void doBreakpointDisabled(long number, DbgCause cause) { + DbgBreakpointInfo oldInfo = getKnownBreakpoint(number); + if (oldInfo == null) { + return; + } + DbgBreakpointInfo newInfo = oldInfo.withEnabled(false); + doBreakpointModifiedSameLocations(newInfo, oldInfo, cause); + } + + @Internal + public void doBreakpointEnabled(long number, DbgCause cause) { + DbgBreakpointInfo oldInfo = getKnownBreakpoint(number); + if (oldInfo == null) { + return; + } + DbgBreakpointInfo newInfo = oldInfo.withEnabled(true); + doBreakpointModifiedSameLocations(newInfo, oldInfo, cause); + } + + private void changeBreakpoints() { + Set retained = new HashSet<>(); + DebugSystemObjects so = getSystemObjects(); + try (SavedFocus focus = new SavedFocus(so)) { + for (DebugProcessId pid : so.getProcesses()) { + try { + Msg.debug(this, "BREAKPOINTS: Changing current process to " + pid); + so.setCurrentProcessId(pid); + } + catch (COMException e) { + Msg.debug(this, e.getMessage()); + continue; + } + List tids = so.getThreads(); + for (DebugBreakpoint bpt : getControl().getBreakpoints()) { + BitmaskSet f = bpt.getFlags(); + if (!f.contains((BreakFlags.ENABLED)) || f.contains(BreakFlags.DEFERRED)) { + continue; + } + if (bpt.getType().breakType != BreakType.CODE) { + continue; // TODO: Extend SCTL to handle R/W breakpoints + } + int id = bpt.getId(); + retained.add(id); + long newOffset = bpt.getOffset(); + BreakpointTag tag = breaksById.get(id); + if (tag == null) { + for (DebugThreadId tid : tids) { + Msg.debug(this, "TRAP Added: " + id + " on " + tid); + if (!claimsBreakpointAdded.satisfy(tid)) { + /* + AbstractSctlTrapSpec spec = + server.getDialect().create(AbstractSctlTrapSpec.class); + spec.setActionStop(); + spec.setAddress(newOffset); + synth.synthSetTrap(null, tid.id, spec, id); + */ + } + else { + Msg.debug(this, " claimed"); + } + breaksById.put(id, new BreakpointTag(newOffset)); + } + } + else if (tag.offset != newOffset) { + /* + for (DebugThreadId tid : tids) { + synth.synthClearTrap(null, tid.id, id); + AbstractSctlTrapSpec spec = + server.getDialect().create(AbstractSctlTrapSpec.class); + spec.setActionStop(); + spec.setAddress(newOffset); + synth.synthSetTrap(null, tid.id, spec, id); + } + */ + tag.offset = newOffset; + } // else the breakpoint is unchanged + } + Iterator it = breaksById.keySet().iterator(); + while (it.hasNext()) { + int id = it.next(); + if (retained.contains(id)) { + continue; + } + for (DebugThreadId tid : tids) { + Msg.debug(this, "TRAP Removed: " + id + " on " + tid); + if (!claimsBreakpointRemoved.satisfy(new BreakId(tid, id))) { + /* + synth.synthClearTrap(null, tid.id, id); + */ + } + else { + Msg.debug(this, " claimed"); + } + } + it.remove(); + } + } + } + catch (COMException e) { + Msg.error(this, "Error retrieving processes: " + e); + } + } + + @Override + public CompletableFuture> listProcesses() { + return execute(new DbgListProcessesCommand(this)); + } + + @Override + public CompletableFuture>> listAvailableProcesses() { + return execute(new DbgListAvailableProcessesCommand(this)); + } + + @Override + public CompletableFuture> listSessions() { + return CompletableFuture.completedFuture(null); + ///return execute(new DbgListSessionsCommand(this)); + } + + @Override + public void sendInterruptNow() { + checkStarted(); + Msg.info(this, "Interrupting"); + // NB: don't use "execute" here - engThread is paused on waitForEvents + // and execute::sequence blocks on engThread + reentrantClient.getControl().setInterrupt(DebugInterrupt.ACTIVE); + } + + @Override + public CompletableFuture addProcess() { + return execute(new DbgAddProcessCommand(this)); + } + + @Override + public CompletableFuture removeProcess(DbgProcess process) { + return execute(new DbgRemoveProcessCommand(this, process.getId())); + } + + @Override + public CompletableFuture addSession() { + return execute(new DbgAddSessionCommand(this)); + } + + @Override + public CompletableFuture removeSession(DbgSession session) { + return execute(new DbgRemoveSessionCommand(this, session.getId())); + } + + @Override + public CompletableFuture launch(List args) { + return execute(new DbgLaunchProcessCommand(this, args)); + } + + @Override + public CompletableFuture launch(Map args) { + return CompletableFuture.completedFuture(null); + } + + public CompletableFuture openFile(Map args) { + return execute(new DbgOpenDumpCommand(this, args)); + } + + public CompletableFuture attachKernel(Map args) { + setKernelMode(true); + return execute(new DbgAttachKernelCommand(this, args)); + } + + static class ExitEvent { + final DebugThreadId tid; + final long exitCode; + + public ExitEvent(DebugThreadId tid, long exitCode) { + this.tid = tid; + this.exitCode = exitCode; + } + } + + static class BreakId { + final DebugThreadId tid; + final int bpid; + + public BreakId(DebugThreadId tid, int bpid) { + this.tid = tid; + this.bpid = bpid; + } + } + + static class BreakpointTag { + long offset; + + public BreakpointTag(long offset) { + this.offset = offset; + } + } + + class SavedFocus implements AutoCloseable { + final DebugSystemObjects so; + DebugThreadId tid = null; + + public SavedFocus(DebugSystemObjects so) { + this.so = so; + try { + tid = so.getCurrentThreadId(); + } + catch (COMException e) { + Msg.debug(this, "Cannot save current thread id: " + e); + } + } + + @Override + public void close() { + if (tid != null) { + try { + so.setCurrentThreadId(tid); + } + catch (COMException e) { + Msg.debug(this, "Could not restore current thread id: " + e); + } + } + } + } + + public DebugClient getClient() { + return engThread.getClient(); + } + + public DebugAdvanced getAdvanced() { + DebugClient dbgeng = getClient(); + return dbgeng.getAdvanced(); + } + + public DebugControl getControl() { + DebugClient dbgeng = getClient(); + return dbgeng.getControl(); + } + + public DebugDataSpaces getDataSpaces() { + DebugClient dbgeng = getClient(); + return dbgeng.getDataSpaces(); + } + + public DebugRegisters getRegisters() { + DebugClient dbgeng = getClient(); + return dbgeng.getRegisters(); + } + + public DebugSymbols getSymbols() { + DebugClient dbgeng = getClient(); + return dbgeng.getSymbols(); + } + + public DebugSystemObjects getSystemObjects() { + DebugClient dbgeng = getClient(); + return dbgeng.getSystemObjects(); + } + + public List getThreads() { + DebugSystemObjects so = getSystemObjects(); + return so.getThreads(); + } + + private List getBreakpoints() { + DebugControl control = getControl(); + return control.getBreakpoints(); + } + + public DbgThreadImpl getCurrentThread() { + DebugEventInformation info = getLastEventInformation(); + if (info == null) { + return null; + } + DbgThreadImpl thread = threads.get(info.getThreadId()); + if (thread != null) { + return thread; + } + DebugSystemObjects so = getSystemObjects(); + DbgProcessImpl process = getCurrentProcess(); + int tid = so.getCurrentThreadSystemId(); + if (tid < 0) { + return null; + } + thread = new DbgThreadImpl(this, process, info.getThreadId(), tid, info); + addThread(thread); + return thread; + } + + public DbgProcessImpl getCurrentProcess() { + DebugEventInformation info = getLastEventInformation(); + if (info == null) { + return null; + } + DbgProcessImpl process = processes.get(info.getProcessId()); + if (process != null) { + return process; + } + DebugSystemObjects so = getSystemObjects(); + int pid = so.getCurrentProcessSystemId(); + process = new DbgProcessImpl(this, info.getProcessId(), pid); + addProcess(process, DbgCause.Causes.UNCLAIMED); + return process; + } + + public DbgSessionImpl getCurrentSession() { + DebugEventInformation info = getLastEventInformation(); + if (info == null) { + return null; + } + DbgSessionImpl session = sessions.get(info.getSessionId()); + if (session != null) { + return session; + } + session = new DbgSessionImpl(this, info.getSessionId()); + addSession(session, DbgCause.Causes.UNCLAIMED); + return session; + } + + public DbgThreadImpl getEventThread() { + DebugSystemObjects so = getSystemObjects(); + DebugThreadId id = so.getEventThread(); + return threads.get(id); + } + + public DbgProcessImpl getEventProcess() { + DebugSystemObjects so = getSystemObjects(); + DebugProcessId id = so.getEventProcess(); + return processes.get(id); + } + + public DbgSessionImpl getEventSession() { + DebugSystemObjects so = getSystemObjects(); + DebugSessionId id = so.getEventSystem(); + return sessions.get(id); + } + + public CompletableFuture selectThread(DbgThreadImpl thread) { + DebugEventInformation info = getLastEventInformation(); + info.setThread(thread.getId()); + return execute(new DbgThreadSelectCommand(this, thread, null)); + } + + public CompletableFuture selectProcess(DbgProcessImpl process) { + DebugEventInformation info = getLastEventInformation(); + info.setProcess(process.getId()); + return execute(new DbgProcessSelectCommand(this, process)); + } + + public CompletableFuture selectSession(DbgSessionImpl session) { + DebugEventInformation info = getLastEventInformation(); + info.setSession(session.getId()); + return execute(new DbgSessionSelectCommand(this, session)); + } + + @Override + public CompletableFuture console(String command) { + return execute( + new DbgConsoleExecCommand(this, command, DbgConsoleExecCommand.Output.CONSOLE)) + .thenApply(e -> null); + } + + @Override + public CompletableFuture consoleCapture(String command) { + return execute( + new DbgConsoleExecCommand(this, command, DbgConsoleExecCommand.Output.CAPTURE)); + } + + public void fireThreadExited(DebugThreadId id, DbgProcessImpl process, DbgCause cause) { + listenersEvent.fire.threadExited(id, process, cause); + } + + @Override + public DbgState getState() { + return state.get(); + } + + @Override + public DbgProcess currentProcess() { + return getCurrentProcess(); + } + + @Override + public CompletableFuture waitForEventEx() { + //System.err.println("ENTER"); + DebugControl control = getControl(); + waiting = true; + control.waitForEvent(); + //System.err.println("EXIT"); + waiting = false; + updateState(); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture waitForState(DbgState forState) { + checkStarted(); + return state.waitValue(forState); + } + + @Override + public CompletableFuture waitForPrompt() { + return CompletableFuture.completedFuture(null); + } + + @Override + public DebugEventInformation getLastEventInformation() { + return lastEventInformation; + } + + public CompletableFuture> getRegisterMap(List path) { + return null; + } + + public boolean isWaiting() { + return waiting; + } + + public boolean isKernelMode() { + return kernelMode; + } + + public void setKernelMode(boolean kernelMode) { + this.kernelMode = kernelMode; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgMinimalSymbol.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgMinimalSymbol.java new file mode 100644 index 0000000000..c53d407d3f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgMinimalSymbol.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +public class DbgMinimalSymbol { + protected final long index; + protected final String type; + protected final String name; + protected final long address; + + public DbgMinimalSymbol(long index, String type, String name, long address) { + this.index = index; + this.type = type; + this.name = name; + this.address = address; + } + + public long getIndex() { + return index; + } + + public String getType() { + // TODO: Interpret these types + // Observed: t, T, D, S + return type; + } + + public String getName() { + return name; + } + + public long getAddress() { + return address; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgModuleImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgModuleImpl.java new file mode 100644 index 0000000000..ddaa0cd3e3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgModuleImpl.java @@ -0,0 +1,105 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugModuleInfo; +import agent.dbgeng.manager.DbgCause.Causes; +import agent.dbgeng.manager.DbgModule; +import ghidra.async.AsyncLazyValue; + +public class DbgModuleImpl implements DbgModule { + + DbgManagerImpl manager; + protected final DbgProcessImpl process; + protected final String name; + private DebugModuleInfo info; + + protected final AsyncLazyValue> minimalSymbols = + new AsyncLazyValue<>(this::doGetMinimalSymbols); + + /** + * Construct a new module + * + * @param manager the manager creating the module + * @param process the process to which the module belongs + * @param id the dbgeng-assigned module ID + */ + public DbgModuleImpl(DbgManagerImpl manager, DbgProcessImpl process, String name) { + this.manager = manager; + this.process = process; + this.name = name; + } + + @Override + public String getName() { + return name; + } + + /** + * Add this thread to the inferior and manager + */ + public void add() { + process.addModule(this); + manager.listenersEvent.fire.moduleLoaded(process, name, Causes.UNCLAIMED); + } + + /** + * Remove this thread from the inferior and manager + */ + public void remove() { + process.removeModule(name); + manager.listenersEvent.fire.moduleUnloaded(process, name, Causes.UNCLAIMED); + } + + @Override + public String getImageName() { + return info == null ? getName() : info.imageName; + } + + @Override + public Long getKnownBase() { + return info == null ? 0L : info.baseOffset; + } + + @Override + public Integer getSize() { + return info == null ? 0 : info.moduleSize; + } + + @Override + public Integer getTimeStamp() { + return info == null ? 0 : info.timeDateStamp; + } + + protected CompletableFuture> doGetMinimalSymbols() { + // TODO: Apparently, this is using internal GDB-debugging commands.... + // TODO: Also make methods for "full" symbols (DWARF?) + return null; + } + + @Override + public CompletableFuture> listMinimalSymbols() { + return minimalSymbols.request(); + } + + public void setInfo(DebugModuleInfo info) { + this.info = info; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgModuleMemoryImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgModuleMemoryImpl.java new file mode 100644 index 0000000000..a8934f1854 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgModuleMemoryImpl.java @@ -0,0 +1,104 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import java.util.List; + +import agent.dbgeng.dbgeng.DebugDataSpaces.PageState; +import agent.dbgeng.manager.DbgModuleMemory; + +public class DbgModuleMemoryImpl implements DbgModuleMemory { + + protected final String index; + protected final long vmaStart; + protected final long vmaEnd; + protected final long allocationBase; + protected final List allocationProtect; + protected final List protect; + protected final PageState state; + protected final String type; + private boolean isRead; + private boolean isWrite; + private boolean isExec; + + public DbgModuleMemoryImpl(String index, long vmaStart, long vmaEnd, long allocationBase, + List allocationProtect, List protect, PageState state, String type, + boolean isRead, boolean isWrite, boolean isExec) { + this.index = index; + this.vmaStart = vmaStart; + this.vmaEnd = vmaEnd; + this.allocationBase = allocationBase; + this.state = state; + this.type = type; + this.allocationProtect = List.copyOf(allocationProtect); + this.protect = List.copyOf(protect); + this.isRead = isRead; + this.isWrite = isWrite; + this.isExec = isExec; + } + + @Override + public String getName() { + return index; + } + + @Override + public long getVmaStart() { + return vmaStart; + } + + @Override + public long getVmaEnd() { + return vmaEnd; + } + + @Override + public long getAllocationBase() { + return allocationBase; + } + + @Override + public List getAllocationProtect() { + return allocationProtect; + } + + @Override + public List getProtect() { + return protect; + } + + @Override + public String getState() { + return state.toString(); + } + + @Override + public String getType() { + return type; + } + + public boolean isRead() { + return isRead; + } + + public boolean isWrite() { + return isWrite; + } + + public boolean isExec() { + return isExec; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgProcessImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgProcessImpl.java new file mode 100644 index 0000000000..f30be4a8d2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgProcessImpl.java @@ -0,0 +1,360 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import static ghidra.async.AsyncUtils.*; + +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.Supplier; + +import com.google.common.collect.RangeSet; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugClient.DebugAttachFlags; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.cmd.*; +import ghidra.async.TypeSpec; +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.target.TargetAttachable; +import ghidra.util.Msg; + +public class DbgProcessImpl implements DbgProcess { + + private final Map threads = new LinkedHashMap<>(); + private final Map unmodifiableThreads = + Collections.unmodifiableMap(threads); + + private final Map modules = new LinkedHashMap<>(); + private final Map unmodifiableModules = Collections.unmodifiableMap(modules); + + private final NavigableMap mappings = new TreeMap<>(); + private final NavigableMap unmodifiableMappings = + Collections.unmodifiableNavigableMap(mappings); + + private DbgManagerImpl manager; + private DebugProcessId id; + private Long pid; + private Long exitCode; + + /** + * Construct a new inferior + * + * @param manager the manager creating the process + * @param id the dbgeng-assigned process ID + */ + public DbgProcessImpl(DbgManagerImpl manager, DebugProcessId id, long pid) { + this.manager = manager; + this.id = id; + this.pid = pid; + } + + public DbgProcessImpl(DbgManagerImpl manager) { + this.manager = manager; + } + + @Override + public String toString() { + return ""; + } + + @Override + public DebugProcessId getId() { + return id; + } + + public void setId(DebugProcessId id) { + this.id = id; + } + + @Override + public Long getPid() { + return pid; + } + + /** + * Set the exit code + * + * @param exitCode the exit code (status or signal) + */ + public void setExitCode(Long exitCode) { + this.exitCode = exitCode; + } + + @Override + public Long getExitCode() { + return exitCode; + } + + /** + * Add this process to the manager's list of processes, because of a given cause + * + * @param cause the cause of the new inferior + */ + public void add(DbgCause cause) { + manager.addProcess(this, cause); + } + + /** + * Remove this process from the manager's list of processes, because of a given cause + * + * @param cause the cause of removal + */ + public void remove(DbgCause cause) { + manager.removeProcess(id, cause); + } + + @Override + public CompletableFuture remove() { + return manager.removeProcess(this); + } + + /** + * Use {@link DbgThreadImpl#add()} instead + * + * @param thread the thread to add + */ + public void addThread(DbgThreadImpl thread) { + DbgThreadImpl exists = threads.get(thread.getId()); + if (exists != null) { + Msg.warn(this, "Adding pre-existing thread " + exists); + //throw new IllegalArgumentException("There is already thread " + exists); + } + threads.put(thread.getId(), thread); + + } + + @Override + public DbgThreadImpl getThread(DebugThreadId tid) { + DbgThreadImpl result = threads.get(tid); + if (result == null) { + throw new IllegalArgumentException("There is no thread with id " + tid); + } + return result; + } + + /** + * Use {@link DbgThreadImpl#remove()} instead + * + * @param tid the ID of the thread to remove + */ + public void removeThread(DebugThreadId tid) { + if (threads.remove(tid) == null) { + throw new IllegalArgumentException("There is no thread with id " + tid); + } + } + + /** + * Use {@link DbgModuleImpl#add()} instead + * + * @param module the thread to add + */ + public void addModule(DbgModuleImpl module) { + DbgModuleImpl exists = modules.get(module.getName()); + if (exists != null) { + throw new IllegalArgumentException("There is already module " + exists); + } + modules.put(module.getName(), module); + + } + + @Override + public DbgModuleImpl getModule(String id) { + DbgModuleImpl result = modules.get(id); + if (result == null) { + throw new IllegalArgumentException("There is no module with id " + id); + } + return result; + } + + /** + * Use {@link DbgModulesImpl#remove()} instead + * + * @param id the ID of the thread to remove + */ + public void removeModule(String id) { + if (modules.remove(id) == null) { + throw new IllegalArgumentException("There is no module with id " + id); + } + } + + @Override + public Map getKnownThreads() { + return unmodifiableThreads; + } + + public Map getKnownThreadsImpl() { + return threads; + } + + @Override + public CompletableFuture> listThreads() { + return manager.execute(new DbgListThreadsCommand(manager, this)); + } + + @Override + public Map getKnownModules() { + return unmodifiableModules; + } + + @Override + public CompletableFuture> listModules() { + return manager.execute(new DbgListModulesCommand(manager, this)); + } + + @Override + public Map getKnownMappings() { + return unmodifiableMappings; + } + + @Override + public CompletableFuture> listMappings() { + return manager.execute(new DbgListMappingsCommand(manager, this)); + } + + @Override + public CompletableFuture select() { + return manager.execute(new DbgProcessSelectCommand(manager, this)); + } + + @Override + public CompletableFuture fileExecAndSymbols(String file) { + return sequence(TypeSpec.VOID).then((seq) -> { + select().handle(seq::next); + }).then((seq) -> { + manager.execute(new DbgFileExecAndSymbolsCommand(manager, file)).handle(seq::exit); + }).finish(); + } + + @Override + public CompletableFuture run() { + return sequence(TypeSpec.cls(DbgThread.class)).then((seq) -> { + select().handle(seq::next); + }).then((seq) -> { + manager.execute(new DbgRunCommand(manager)).handle(seq::exit); + }).finish(); + } + + @Override + public CompletableFuture> attach(long toPid) { + return sequence(TypeSpec.cls(DbgThread.class).set()).then((seq) -> { + select().handle(seq::next); + }).then((seq) -> { + pid = toPid; // TODO: Wait for successful completion? + manager.execute( + new DbgAttachCommand(manager, this, BitmaskSet.of(DebugAttachFlags.DEFAULT))) + .handle(seq::exit); + }).finish(); + } + + @Override + public CompletableFuture> reattach( + TypedTargetObjectRef> ref) { + return sequence(TypeSpec.cls(DbgThread.class).set()).then((seq) -> { + select().handle(seq::next); + }).then((seq) -> { + manager.execute( + new DbgAttachCommand(manager, this, BitmaskSet.of(DebugAttachFlags.EXISTING))) + .handle(seq::exit); + }).finish(); + } + + @Override + public CompletableFuture detach() { + return sequence(TypeSpec.VOID).then((seq) -> { + select().handle(seq::next); + }).then((seq) -> { + manager.execute(new DbgDetachCommand(manager, this)).handle(seq::exit); + }).finish(); + } + + @Override + public CompletableFuture kill() { + return sequence(TypeSpec.VOID).then((seq) -> { + select().handle(seq::next); + }).then((seq) -> { + manager.execute(new DbgKillCommand(manager)).handle(seq::exit); + }).finish(); + } + + @Override + public CompletableFuture cont() { + return sequence(TypeSpec.VOID).then((seq) -> { + select().handle(seq::next); + }).then((seq) -> { + manager.execute(new DbgContinueCommand(manager)).handle(seq::exit); + }).finish(); + } + + protected CompletableFuture preferThread( + Function> viaThread, + Supplier> viaThis) { + Optional first = threads.values().stream().findFirst(); + if (first.isPresent()) { + return viaThread.apply(first.get()); + } + return select().thenCompose(__ -> viaThis.get()); + } + + @Override + public CompletableFuture> readMemory(long addr, ByteBuffer buf, int len) { + // I can't imagine this working without a thread.... + return preferThread(t -> t.readMemory(addr, buf, len), + () -> manager.execute(new DbgReadMemoryCommand(manager, addr, buf, len))); + } + + @Override + public CompletableFuture writeMemory(long addr, ByteBuffer buf, int len) { + // I can't imagine this working without a thread.... + return preferThread(t -> t.writeMemory(addr, buf, len), + () -> manager.execute(new DbgWriteMemoryCommand(manager, addr, buf, len))); + } + + @Override + public CompletableFuture consoleCapture(String command) { + // TODO Auto-generated method stub + return null; + } + + protected DbgModuleImpl createModule(String name) { + return new DbgModuleImpl(manager, this, name); + } + + protected void moduleLoaded(String name, DebugModuleInfo info) { + DbgModuleImpl module = modules.computeIfAbsent(name, this::createModule); + module.setInfo(info); + } + + protected void moduleUnloaded(String name) { + modules.remove(name); + } + + protected void threadCreated(DbgThreadImpl thread) { + threads.put(thread.getId(), thread); + } + + public void threadExited(DebugThreadId id) { + threads.remove(id); + } + + @Override + public CompletableFuture evaluate(String expression) { + return manager.execute(new DbgEvaluateCommand(manager, expression)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgRegister.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgRegister.java new file mode 100644 index 0000000000..775e5873ec --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgRegister.java @@ -0,0 +1,89 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import agent.dbgeng.dbgeng.DebugRegisters.DebugRegisterDescription; + +public class DbgRegister implements Comparable { + private final String name; + private final int number; + private final int size; + private DebugRegisterDescription desc; + + /** + * Construct a new register descriptor + * + * @param name the register's name + * @param number the dbgeng-assigned register number + * @param size the size in bytes + */ + public DbgRegister(String name, int number, int size) { + this.name = name; + this.number = number; + this.size = size; + } + + public DbgRegister(DebugRegisterDescription desc) { + this.name = desc.name; + this.number = desc.index; + this.size = desc.type.byteLength; + this.desc = desc; + } + + /** + * Get the register's name + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Get the register's Dbg-assigned number + * + * @return the number + */ + public int getNumber() { + return number; + } + + /** + * Get the register's size in bytes + * + * @return the size + */ + public int getSize() { + return size; + } + + @Override + public int compareTo(DbgRegister that) { + return this.number - that.number; + } + + @Override + public String toString() { + return "<" + getClass().getSimpleName() + " " + name + " (" + number + ")>"; + } + + public boolean isBaseRegister() { + if (desc == null) { + return true; + } + return desc.subregMaster == 0; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgRegisterSet.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgRegisterSet.java new file mode 100644 index 0000000000..a66afa11b9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgRegisterSet.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import java.util.*; + +public class DbgRegisterSet extends AbstractSet { + private final Map byName = new HashMap<>(); + private final Map byNumber = new TreeMap<>(); + + /** + * Construct a set from the given collection + * + * Note that regs need not be presented in any particular order; however, there must be at most + * one register per number. Otherwise, there may be undefined behavior. + * + * @param regs the registers to index + */ + public DbgRegisterSet(Collection regs) { + for (DbgRegister r : regs) { + byName.put(r.getName(), r); + byNumber.put(r.getNumber(), r); + } + } + + /** + * Get a register by name + * + * @param name the name + * @return the register + */ + public DbgRegister get(String name) { + return byName.get(name); + } + + /** + * Get a register by number + * + * @param number the number + * @return the register + */ + public DbgRegister get(int number) { + return byNumber.get(number); + } + + @Override + public Iterator iterator() { + return byNumber.values().iterator(); + } + + @Override + public int size() { + return byNumber.size(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgSectionImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgSectionImpl.java new file mode 100644 index 0000000000..b0e6fdbfe8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgSectionImpl.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +public class DbgSectionImpl { + private final long start; + private final long end; + private final long size; + private final long offset; + private final String objfile; + + public DbgSectionImpl(long start, long end, long size, long offset, String objfile) { + this.start = start; + this.end = end; + this.size = size; + this.offset = offset; + this.objfile = objfile; + + assert start + size == end; + } + + public long getStart() { + return start; + } + + public long getEnd() { + return end; + } + + public long getSize() { + return size; + } + + public long getOffset() { + return offset; + } + + public String getObjfile() { + return objfile; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgSessionImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgSessionImpl.java new file mode 100644 index 0000000000..9c887ca693 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgSessionImpl.java @@ -0,0 +1,152 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.dbgeng.DebugSessionId; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.cmd.DbgListProcessesCommand; + +public class DbgSessionImpl implements DbgSession { + + private final Map processes = new LinkedHashMap<>(); + private final Map unmodifiableProcesses = + Collections.unmodifiableMap(processes); + + private DbgManagerImpl manager; + private DebugSessionId id; + private Long exitCode; + + /** + * Construct a new session + * + * @param manager the manager creating the session + * @param id the dbgeng-assigned session ID + */ + public DbgSessionImpl(DbgManagerImpl manager, DebugSessionId id) { + this.manager = manager; + this.id = id; + } + + public DbgSessionImpl(DbgManagerImpl manager) { + this.manager = manager; + } + + @Override + public String toString() { + return ""; + } + + @Override + public DebugSessionId getId() { + return id; + } + + public void setId(DebugSessionId id) { + this.id = id; + } + + /** + * Set the exit code + * + * @param exitCode the exit code (status or signal) + */ + public void setExitCode(Long exitCode) { + this.exitCode = exitCode; + } + + @Override + public Long getExitCode() { + return exitCode; + } + + /** + * Add this process to the manager's list of processes, because of a given cause + * + * @param cause the cause of the new inferior + */ + public void add(DbgCause cause) { + manager.addSession(this, cause); + } + + /** + * Remove this process from the manager's list of processes, because of a given cause + * + * @param cause the cause of removal + */ + public void remove(DbgCause cause) { + manager.removeSession(id, cause); + } + + /** + * Use {@link DbgSessionImpl#add()} instead + * + * @param process the process to add + */ + public void addProcess(DbgProcessImpl process) { + DbgProcessImpl exists = processes.get(process.getId()); + if (exists != null) { + throw new IllegalArgumentException("There is already process " + exists); + } + processes.put(process.getId(), process); + + } + + @Override + public DbgProcessImpl getProcess(DebugProcessId tid) { + DbgProcessImpl result = processes.get(tid); + if (result == null) { + throw new IllegalArgumentException("There is no thread with id " + tid); + } + return result; + } + + /** + * Use {@link DbgProcessImpl#remove()} instead + * + * @param pid the ID of the thread to remove + */ + public void removeProcess(DebugProcessId pid) { + if (processes.remove(pid) == null) { + throw new IllegalArgumentException("There is no process with id " + pid); + } + } + + @Override + public Map getKnownProcesses() { + return unmodifiableProcesses; + } + + public Map getKnownProcessImpl() { + return processes; + } + + @Override + public CompletableFuture> listProcesses() { + return manager.execute(new DbgListProcessesCommand(manager)); + } + + protected void processCreated(DbgProcessImpl process) { + processes.put(process.getId(), process); + } + + public void processExited(DebugProcessId id) { + processes.remove(id); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgStackFrameImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgStackFrameImpl.java new file mode 100644 index 0000000000..a68e307339 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgStackFrameImpl.java @@ -0,0 +1,162 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import java.math.BigInteger; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgStackFrame; +import agent.dbgeng.manager.cmd.*; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; + +public class DbgStackFrameImpl implements DbgStackFrame { + + protected final DbgManagerImpl manager; + protected final DbgThreadImpl thread; + protected final int level; + protected final BigInteger addr; + protected final String func; + private long funcTableEntry; + private long frameOffset; + private long returnOffset; + private long stackOffset; + private boolean virtual; + + private long params[] = new long[4]; + + public DbgStackFrameImpl(DbgThreadImpl thread, int level, BigInteger addr, String func) { + this.manager = thread.manager; + this.thread = thread; + this.level = level; + this.addr = addr; + this.func = func; + } + + public DbgStackFrameImpl(DbgThreadImpl thread, int level, BigInteger addr, long funcTableEntry, + long frameOffset, long returnOffset, long stackOffset, boolean virtual, long param0, + long param1, long param2, long param3) { + this.manager = thread.manager; + this.thread = thread; + this.level = level; + this.addr = addr; + this.func = null; + this.funcTableEntry = funcTableEntry; + this.frameOffset = frameOffset; + this.returnOffset = returnOffset; + this.stackOffset = stackOffset; + this.virtual = virtual; + this.params[0] = param0; + this.params[1] = param1; + this.params[2] = param2; + this.params[3] = param3; + } + + @Override + public String toString() { + return ""; + } + + @Override + public int getLevel() { + return level; + } + + @Override + public BigInteger getAddress() { + return addr; + } + + @Override + public String getFunction() { + return func; + } + + @Override + public DbgThreadImpl getThread() { + return thread; + } + + @Override + public long getFuncTableEntry() { + return funcTableEntry; + } + + @Override + public long getFrameOffset() { + return frameOffset; + } + + @Override + public long getReturnOffset() { + return returnOffset; + } + + @Override + public long getStackOffset() { + return stackOffset; + } + + @Override + public boolean getVirtual() { + return virtual; + } + + @Override + public long[] getParams() { + return params; + } + + @Override + public CompletableFuture select() { + return manager.execute(new DbgThreadSelectCommand(manager, thread, level)); + } + + @Override + public CompletableFuture evaluate(String expression) { + return manager.execute(new DbgEvaluateCommand(manager, expression)); + } + + @Override + public CompletableFuture> readRegisters(Set regs) { + return AsyncUtils.sequence(TypeSpec.map(DbgRegister.class, BigInteger.class)).then(seq -> { + manager.execute(new DbgReadRegistersCommand(manager, thread, level, regs)) + .handle(seq::exit); + }).finish(); + } + + @Override + public CompletableFuture writeRegisters(Map regVals) { + return manager.execute(new DbgWriteRegistersCommand(manager, thread, level, regVals)); + } + + @Override + public CompletableFuture console(String command) { + return manager + .execute(new DbgConsoleExecCommand(manager, command, + DbgConsoleExecCommand.Output.CONSOLE)) + .thenApply(e -> null); + } + + @Override + public CompletableFuture consoleCapture(String command) { + return manager.execute( + new DbgConsoleExecCommand(manager, command, DbgConsoleExecCommand.Output.CAPTURE)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgThreadImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgThreadImpl.java new file mode 100644 index 0000000000..07d1778467 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgThreadImpl.java @@ -0,0 +1,281 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import static ghidra.async.AsyncUtils.*; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.google.common.collect.RangeSet; + +import agent.dbgeng.dbgeng.DebugEventInformation; +import agent.dbgeng.dbgeng.DebugRegisters.DebugRegisterDescription; +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.jna.dbgeng.WinNTExtra; +import agent.dbgeng.jna.dbgeng.WinNTExtra.Machine; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.DbgManager.ExecSuffix; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.manager.breakpoint.DbgBreakpointType; +import agent.dbgeng.manager.cmd.*; +import ghidra.async.*; +import ghidra.util.Msg; + +public class DbgThreadImpl implements DbgThread { + + DbgManagerImpl manager; + private DbgProcessImpl process; + private DebugThreadId id; + private long tid; + private DebugEventInformation info; + + private final AsyncReference state = + new AsyncReference<>(DbgState.STOPPED); + + private final AsyncLazyValue registers = + new AsyncLazyValue<>(this::doListRegisters); + + /** + * Construct a new thread + * + * @param manager the manager creating the thread + * @param process the process to which the thread belongs + * @param id the dbgeng-assigned thread ID + */ + public DbgThreadImpl(DbgManagerImpl manager, DbgProcessImpl process, DebugThreadId id, + long tid) { + this.manager = manager; + this.process = process; + this.id = id; + this.tid = tid; + } + + public DbgThreadImpl(DbgManagerImpl manager, DbgProcessImpl process, DebugThreadId id, long tid, + DebugEventInformation info) { + this(manager, process, id, tid); + this.setInfo(info); + } + + @Override + public String toString() { + return ""; + } + + @Override + public DebugThreadId getId() { + return id; + } + + @Override + public DbgProcess getProcess() { + return process; + } + + @Override + public Long getTid() { + return tid; + } + + /** + * Add this thread to the inferior and manager + */ + public void add() { + process.addThread(this); + manager.addThread(this); + state.addChangeListener((oldState, newState, pair) -> { + this.manager.listenersEvent.fire.threadStateChanged(this, newState, pair.cause, + pair.reason); + }); + } + + /** + * Remove this thread from the inferior and manager + */ + public void remove() { + try { + process.removeThread(id); + manager.removeThread(id); + } + catch (IllegalArgumentException e) { + Msg.warn(this, "Thread " + id + " already removed"); + } + } + + @Override + public DbgState getState() { + return state.get(); + } + + /** + * Set the state of this thread + * + * @param state the new state + * @param cause the cause for the change + * @param reason the reason (usually a stop reason) for the change + * @return true if the state actually changed + */ + @Override + public boolean setState(DbgState state, DbgCause cause, DbgReason reason) { + return this.state.set(state, new CauseReasonPair(cause, reason)); + } + + @Override + public CompletableFuture select() { + return manager.execute(new DbgThreadSelectCommand(manager, this, null)); + } + + @Override + public CompletableFuture evaluate(String expression) { + return manager.execute(new DbgEvaluateCommand(manager, expression)); + } + + @Override + // TODO: Is this per thread or per inferior? + public CompletableFuture listRegisters() { + return registers.request(); + } + + private CompletableFuture doListRegisters() { + return sequence(TypeSpec.cls(DbgRegisterSet.class)).then((seq) -> { + manager.execute(new DbgListRegisterDescriptionsCommand(manager)).handle(seq::next); + }, TypeSpec.cls(DebugRegisterDescription.class).list()).then((descs, seq) -> { + if (descs == null) { + return; + } + List regs = new ArrayList<>(); + for (DebugRegisterDescription desc : descs) { + regs.add(new DbgRegister(desc)); + } + seq.exit(new DbgRegisterSet(regs)); + }).finish(); + } + + @Override + public CompletableFuture> listStackFrames() { + return manager.execute(new DbgStackListFramesCommand(manager, this)); + } + + @Override + public CompletableFuture> readRegisters(Set regs) { + return manager.execute(new DbgReadRegistersCommand(manager, this, null, regs)); + } + + @Override + public CompletableFuture writeRegisters(Map regVals) { + return manager.execute(new DbgWriteRegistersCommand(manager, this, null, regVals)); + } + + @Override + public CompletableFuture> readMemory(long addr, ByteBuffer buf, int len) { + return manager.execute(new DbgReadMemoryCommand(manager, addr, buf, len)); + } + + @Override + public CompletableFuture writeMemory(long addr, ByteBuffer buf, int len) { + return manager.execute(new DbgWriteMemoryCommand(manager, addr, buf, len)); + } + + @Override + public CompletableFuture insertBreakpoint(long loc, int len, + DbgBreakpointType type) { + return manager.execute(new DbgInsertBreakpointCommand(manager, loc, len, type)); + } + + @Override + public CompletableFuture console(String command) { + return manager + .execute(new DbgConsoleExecCommand(manager, command, + DbgConsoleExecCommand.Output.CONSOLE)) + .thenApply(e -> null); + } + + @Override + public CompletableFuture consoleCapture(String command) { + return manager.execute( + new DbgConsoleExecCommand(manager, command, DbgConsoleExecCommand.Output.CAPTURE)); + } + + @Override + public CompletableFuture cont() { + return sequence(TypeSpec.VOID).then((seq) -> { + select().handle(seq::next); + }).then((seq) -> { + manager.execute(new DbgContinueCommand(manager)).handle(seq::exit); + }).finish(); + } + + @Override + public CompletableFuture step(ExecSuffix suffix) { + return sequence(TypeSpec.VOID).then((seq) -> { + select().handle(seq::next); + }).then((seq) -> { + manager.execute(new DbgStepCommand(manager, suffix)).handle(seq::exit); + }).finish(); + } + + @Override + public CompletableFuture kill() { + return sequence(TypeSpec.VOID).then((seq) -> { + select().handle(seq::next); + }).then((seq) -> { + manager.execute(new DbgKillCommand(manager)).handle(seq::exit); + }).finish(); + } + + @Override + public CompletableFuture detach() { + return sequence(TypeSpec.VOID).then((seq) -> { + select().handle(seq::next); + }).then((seq) -> { + manager.execute(new DbgDetachCommand(manager, process)).handle(seq::exit); + }).finish(); + } + + public DebugEventInformation getInfo() { + return info; + } + + public void setInfo(DebugEventInformation info) { + Machine newType = WinNTExtra.Machine.getByNumber(info.getExecutingProcessorType()); + Machine oldType = getExecutingProcessorType(); + if (!newType.equals(oldType)) { + registers.forget(); + } + this.info = info; + } + + private static class CauseReasonPair { + private final DbgCause cause; + private final DbgReason reason; + + CauseReasonPair(DbgCause cause, DbgReason reason) { + this.cause = cause; + this.reason = reason; + } + } + + @Override + public Machine getExecutingProcessorType() { + if (info == null) { + return WinNTExtra.Machine.IMAGE_FILE_MACHINE_AMD64; + } + int executingProcessorType = info.getExecutingProcessorType(); + return WinNTExtra.Machine.getByNumber(executingProcessorType); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgEndSteppingRangeReason.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgEndSteppingRangeReason.java new file mode 100644 index 0000000000..0cf0c5cac0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgEndSteppingRangeReason.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.reason; + +import java.util.Map; + +import agent.dbgeng.manager.DbgReason; + +/** + * The inferior stopped because it finished stepping + */ +public class DbgEndSteppingRangeReason implements DbgReason { + public DbgEndSteppingRangeReason(Map info) { + // Nothing additional to parse + } + + @Override + public String desc() { + return "Stepping ended"; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgExitNormallyReason.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgExitNormallyReason.java new file mode 100644 index 0000000000..0d05f231b4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgExitNormallyReason.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.reason; + +import java.util.Map; + +import agent.dbgeng.manager.DbgReason; + +/** + * The inferior stopped because it has exited (with status 0) + */ +public class DbgExitNormallyReason implements DbgReason { + public DbgExitNormallyReason(Map info) { + // Nothing additional to parse + } + + @Override + public String desc() { + return "Exited normally"; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgExitedReason.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgExitedReason.java new file mode 100644 index 0000000000..a25a13110f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgExitedReason.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.reason; + +import java.util.Map; + +import agent.dbgeng.manager.DbgReason; + +/** + * The inferior stopped because it has exited + */ +public class DbgExitedReason implements DbgReason { + private final int exitCode; + + public DbgExitedReason(Map info) { + this.exitCode = Integer.parseInt((String) info.get("exit-code")); + } + + /** + * Get the exit code of the inferior + * + * @return the exit code + */ + public int getExitCode() { + return exitCode; + } + + @Override + public String desc() { + return "Exited with code " + exitCode; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgSignalReceivedReason.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgSignalReceivedReason.java new file mode 100644 index 0000000000..ace93471b3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/reason/DbgSignalReceivedReason.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.reason; + +import java.util.Map; + +import agent.dbgeng.manager.DbgReason; + +/** + * The inferior stopped because it has received a signal + */ +public class DbgSignalReceivedReason implements DbgReason { + private final String signalName; + + public DbgSignalReceivedReason(Map info) { + this.signalName = (String) info.get("signal-name"); + } + + /** + * Get the (POSIX) name of the signal received + * + * @return the signal name + */ + public String getSignalName() { + return signalName; + } + + @Override + public String desc() { + return "Signalled with " + getSignalName(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/AbstractDbgModel.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/AbstractDbgModel.java new file mode 100644 index 0000000000..f8c26fe87a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/AbstractDbgModel.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgManager; +import agent.dbgeng.model.iface2.DbgModelTargetSession; +import ghidra.dbg.agent.AbstractDebuggerObjectModel; +import ghidra.program.model.address.AddressFactory; + +public abstract class AbstractDbgModel extends AbstractDebuggerObjectModel { + + public abstract DbgManager getManager(); + + public abstract CompletableFuture startDbgEng(String[] args); + + public abstract boolean isRunning(); + + public abstract void terminate() throws IOException; + + public abstract AddressFactory getAddressFactory(); + + public abstract DbgModelTargetSession getSession(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelSelectableObject.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelSelectableObject.java new file mode 100644 index 0000000000..d8b0a5c5ea --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelSelectableObject.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface2.*; +import ghidra.dbg.attributes.TargetObjectRef; + +public interface DbgModelSelectableObject extends DbgModelTargetObject { + + public default CompletableFuture select() { + if (this instanceof DbgModelTargetSession) { + DbgManagerImpl manager = getManager(); + DbgProcess process = manager.getCurrentProcess(); + return process.select(); + } + if (this instanceof DbgModelTargetProcess) { + DbgModelTargetProcess tp = (DbgModelTargetProcess) this; + DbgProcess process = tp.getProcess(); + return process.select(); + } + if (this instanceof DbgModelTargetThread) { + DbgModelTargetThread tt = (DbgModelTargetThread) this; + DbgThread thread = tt.getThread(); + return thread.select(); + } + if (this instanceof DbgModelTargetStackFrame) { + DbgModelTargetStackFrame tf = (DbgModelTargetStackFrame) this; + TargetObjectRef ref = tf.getThread(); + if (ref instanceof DbgModelTargetThread) { + DbgModelTargetThread tt = (DbgModelTargetThread) ref; + DbgThread thread = tt.getThread(); + return thread.select(); + } + } + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAccessConditioned.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAccessConditioned.java new file mode 100644 index 0000000000..785aebacd3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAccessConditioned.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetAccessConditioned; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetAccessConditioned> + extends DbgModelTargetObject, TargetAccessConditioned { + + @Override + public TargetAccessibility getAccessibility(); + + public void setAccessibility(TargetAccessibility accessibility); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAttachable.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAttachable.java new file mode 100644 index 0000000000..f0afd3dea6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAttachable.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetAttachable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetAttachable> + extends DbgModelTargetObject, TargetAttachable { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAttacher.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAttacher.java new file mode 100644 index 0000000000..55b7e999aa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetAttacher.java @@ -0,0 +1,76 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.impl.DbgProcessImpl; +import agent.dbgeng.model.iface2.DbgModelTargetAvailable; +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.target.*; +import ghidra.util.Msg; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetAttacher> + extends DbgModelTargetObject, TargetAttacher { + + @Override + public default CompletableFuture attach( + TypedTargetObjectRef> ref) { + getModel().assertMine(TargetObjectRef.class, ref); + List tPath = ref.getPath(); + AtomicReference process = new AtomicReference<>(); + AtomicReference attachable = new AtomicReference<>(); + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + getModel().fetchModelObject(tPath).handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + attachable.set((DbgModelTargetAvailable) DebuggerObjectModel.requireIface( + TargetAttachable.class, obj, tPath)); + getManager().addProcess().handle(seq::next); + }, process).then(seq -> { + process.get().attach((int) attachable.get().getPid()).handle(seq::nextIgnore); + }).finish().exceptionally((exc) -> { + Msg.error(this, "attach failed"); + return null; + }); + } + + @Override + public default CompletableFuture attach(long pid) { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + DbgProcess process = new DbgProcessImpl(getManager()); + process.attach(pid).handle(seq::nextIgnore); + }).finish().exceptionally((exc) -> { + Msg.error(this, "attach failed"); + return null; + }); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetBptHelper.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetBptHelper.java new file mode 100644 index 0000000000..227b69c61e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetBptHelper.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointAction; +import ghidra.util.datastruct.ListenerSet; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetBptHelper extends DbgModelTargetObject { + + public DbgBreakpointInfo getBreakpointInfo(); + + public void setBreakpointId(String id); + + public void setBreakpointInfo(DbgBreakpointInfo info); + + public boolean isBreakpointEnabled(); + + public void setBreakpointEnabled(boolean enabled); + + public ListenerSet getActions(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetDeletable.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetDeletable.java new file mode 100644 index 0000000000..9f32897639 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetDeletable.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetDeletable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetDeletable> + extends DbgModelTargetObject, TargetDeletable { + + @Override + public CompletableFuture delete(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetDetachable.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetDetachable.java new file mode 100644 index 0000000000..c2775869ae --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetDetachable.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetDetachable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetDetachable> + extends DbgModelTargetObject, TargetDetachable { + + @Override + public default CompletableFuture detach() { + DbgProcess process = getManager().getCurrentProcess(); + return process.detach(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetEnvironment.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetEnvironment.java new file mode 100644 index 0000000000..9f2dc718c6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetEnvironment.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetEnvironment; + +public interface DbgModelTargetEnvironment> + extends DbgModelTargetObject, TargetEnvironment { + + public void refresh(); + + @Override + public default String getArchitecture() { + return getTypedAttributeNowByName(ARCH_ATTRIBUTE_NAME, String.class, ""); + } + + @Override + public default String getDebugger() { + return getTypedAttributeNowByName(DEBUGGER_ATTRIBUTE_NAME, String.class, ""); + } + + @Override + public default String getOperatingSystem() { + return getTypedAttributeNowByName(OS_ATTRIBUTE_NAME, String.class, ""); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetEventScope.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetEventScope.java new file mode 100644 index 0000000000..aeb013d4d9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetEventScope.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetEventScope; + +/** + * The object can emit events affecting itself and its successors + * + * @param type for this + */ +public interface DbgModelTargetEventScope> + extends DbgModelTargetObject, TargetEventScope { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetExecutionStateful.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetExecutionStateful.java new file mode 100644 index 0000000000..c8b6e600ee --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetExecutionStateful.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.List; +import java.util.Map; + +import agent.dbgeng.manager.DbgState; +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetExecutionStateful; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetExecutionStateful> + extends DbgModelTargetObject, TargetExecutionStateful { + + public default TargetExecutionState convertState(DbgState state) { + switch (state) { + case RUNNING: + return TargetExecutionState.RUNNING; + case STOPPED: + default: + return TargetExecutionState.STOPPED; + } + } + + public default void setExecutionState(TargetExecutionState state, String reason) { + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, state // + ), reason); + getListeners().fire(TargetExecutionStateListener.class).executionStateChanged(this, state); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetFocusScope.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetFocusScope.java new file mode 100644 index 0000000000..83500336a9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetFocusScope.java @@ -0,0 +1,89 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.target.TargetFocusScope; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetFocusScope> + extends DbgModelTargetObject, TargetFocusScope { + + @Override + public DbgModelSelectableObject getFocus(); + + // NB: setFocus changes attributes - propagates up to client + public boolean setFocus(DbgModelSelectableObject sel); + + // NB: requestFocus request change in active object - propagates down to manager + // (but, of course, may then cause change in state) + @Override + public default CompletableFuture requestFocus(TargetObjectRef ref) { + if (getManager().isWaiting()) { + return CompletableFuture.completedFuture(null); + } + getModel().assertMine(TargetObjectRef.class, ref); + if (ref.equals(getFocus())) { + return CompletableFuture.completedFuture(null); + } + if (!PathUtils.isAncestor(this.getPath(), ref.getPath())) { + throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope"); + } + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + ref.fetch().handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + TargetObject cur = obj; + while (cur != null) { + if (cur instanceof DbgModelSelectableObject) { + DbgModelSelectableObject sel = (DbgModelSelectableObject) cur; + sel.select().handle(seq::exit); + AtomicReference> scope = new AtomicReference<>(); + AsyncUtils.sequence(TypeSpec.VOID).then(seqx -> { + DebugModelConventions.findSuitable(DbgModelTargetFocusScope.class, sel) + .handle(seqx::next); + }, scope).then(seqx -> { + scope.get().setFocus(sel); + }).finish(); + break; + } + if (cur instanceof DbgModelTargetObject) { + DbgModelTargetObject def = (DbgModelTargetObject) cur; + cur = def.getImplParent(); + continue; + } + throw new AssertionError(); + } + seq.exit(); + }).finish(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterpreter.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterpreter.java new file mode 100644 index 0000000000..980005854d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterpreter.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetInterpreter; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetInterpreter> + extends DbgModelTargetObject, TargetInterpreter { + + @Override + public default CompletableFuture execute(String cmd) { + return getManager().console(cmd); + } + + @Override + public default CompletableFuture executeCapture(String cmd) { + return getManager().consoleCapture(cmd); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterruptible.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterruptible.java new file mode 100644 index 0000000000..1e4f376454 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterruptible.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetInterruptible; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetInterruptible> + extends DbgModelTargetObject, TargetInterruptible { + + @Override + public default CompletableFuture interrupt() { + getManager().sendInterruptNow(); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetKillable.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetKillable.java new file mode 100644 index 0000000000..9000f2716c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetKillable.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetKillable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetKillable> + extends DbgModelTargetObject, TargetKillable { + + @Override + public default CompletableFuture kill() { + DbgProcess process = getManager().getCurrentProcess(); + return process.kill(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetLauncher.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetLauncher.java new file mode 100644 index 0000000000..d0e518b9aa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetLauncher.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; + +/** + * An interface which indicates this object is capable of launching targets. + * + *

+ * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetLauncher> + extends DbgModelTargetObject, TargetCmdLineLauncher { + + @Override + public default CompletableFuture launch(List args) { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + getManager().launch(args).handle(seq::nextIgnore); + }).finish().exceptionally((exc) -> { + throw new DebuggerUserException("Launch failed for " + args); + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetMethod.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetMethod.java new file mode 100644 index 0000000000..aa8760c4ca --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetMethod.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetMethod; + +/** + * An interface which indicates this object is a method in its parent. + * + * @param type for this + */ +public interface DbgModelTargetMethod> + extends DbgModelTargetObject, TargetMethod { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetResumable.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetResumable.java new file mode 100644 index 0000000000..7dd3097bff --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetResumable.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetResumable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetResumable> + extends DbgModelTargetObject, TargetResumable { + + @Override + public default CompletableFuture resume() { + DbgProcess process = getManager().getCurrentProcess(); + return process.cont(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetSteppable.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetSteppable.java new file mode 100644 index 0000000000..d4cf40d290 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetSteppable.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgManager.ExecSuffix; +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import ghidra.dbg.target.TargetSteppable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface DbgModelTargetSteppable> + extends DbgModelTargetObject, TargetSteppable { + + default ExecSuffix convertToDbg(TargetStepKind kind) { + switch (kind) { + case FINISH: + return ExecSuffix.FINISH; + case INTO: + return ExecSuffix.STEP_INSTRUCTION; + case LINE: + return ExecSuffix.STEP; + case OVER: + return ExecSuffix.NEXT_INSTRUCTION; + case OVER_LINE: + return ExecSuffix.NEXT; + case RETURN: + return ExecSuffix.RETURN; + case UNTIL: + return ExecSuffix.UNTIL; + default: + throw new AssertionError(); + } + } + + @Override + default CompletableFuture step(TargetStepKind kind) { + DbgThread thread = getManager().getCurrentThread(); + switch (kind) { + case SKIP: + throw new UnsupportedOperationException(kind.name()); + case ADVANCE: // Why no exec-advance in dbgeng? + return thread.console("advance"); + default: + return thread.step(convertToDbg(kind)); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetAvailable.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetAvailable.java new file mode 100644 index 0000000000..f6f9d63099 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetAvailable.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import ghidra.dbg.target.TargetAttachable; + +public interface DbgModelTargetAvailable + extends DbgModelTargetObject, TargetAttachable { + + public long getPid(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetAvailableContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetAvailableContainer.java new file mode 100644 index 0000000000..453d6373c4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetAvailableContainer.java @@ -0,0 +1,22 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +public interface DbgModelTargetAvailableContainer extends DbgModelTargetObject { + + public DbgModelTargetAvailable getTargetAttachable(int pid); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointContainer.java new file mode 100644 index 0000000000..9a66adfde1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointContainer.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; + +import agent.dbgeng.manager.DbgCause; +import agent.dbgeng.manager.DbgEventsListenerAdapter; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.manager.breakpoint.DbgBreakpointType; +import ghidra.async.AsyncFence; +import ghidra.dbg.target.TargetBreakpointContainer; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.program.model.address.AddressRange; + +public interface DbgModelTargetBreakpointContainer extends DbgModelTargetObject, + TargetBreakpointContainer, DbgEventsListenerAdapter { + + @Override + public void breakpointCreated(DbgBreakpointInfo info, DbgCause cause); + + @Override + public void breakpointModified(DbgBreakpointInfo newInfo, DbgBreakpointInfo oldInfo, + DbgCause cause); + + @Override + public void breakpointDeleted(DbgBreakpointInfo info, DbgCause cause); + + @Override + public void breakpointHit(DbgBreakpointInfo info, DbgCause cause); + + public default CompletableFuture doPlaceBreakpoint(Set kinds, + Function> placer) { + AsyncFence fence = new AsyncFence(); + if (kinds.contains(TargetBreakpointKind.READ) && + kinds.contains(TargetBreakpointKind.WRITE)) { + fence.include(placer.apply(DbgBreakpointType.ACCESS_WATCHPOINT)); + } + else if (kinds.contains(TargetBreakpointKind.READ)) { + fence.include(placer.apply(DbgBreakpointType.READ_WATCHPOINT)); + } + else if (kinds.contains(TargetBreakpointKind.WRITE)) { + fence.include(placer.apply(DbgBreakpointType.HW_WATCHPOINT)); + } + if (kinds.contains(TargetBreakpointKind.EXECUTE)) { + fence.include(placer.apply(DbgBreakpointType.HW_BREAKPOINT)); + } + if (kinds.contains(TargetBreakpointKind.SOFTWARE)) { + fence.include(placer.apply(DbgBreakpointType.BREAKPOINT)); + } + return fence.ready(); + } + + @Override + public default CompletableFuture placeBreakpoint(String expression, + Set kinds) { + return doPlaceBreakpoint(kinds, t -> getManager().insertBreakpoint(expression, t)); + } + + @Override + public default CompletableFuture placeBreakpoint(AddressRange range, + Set kinds) { + // TODO: Consider how to translate address spaces + long offset = range.getMinAddress().getOffset(); + int len = (int) range.getLength(); + return doPlaceBreakpoint(kinds, t -> getManager().insertBreakpoint(offset, len, t)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointLocation.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointLocation.java new file mode 100644 index 0000000000..b2934001a5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointLocation.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import ghidra.dbg.attributes.TargetObjectRefList; +import ghidra.dbg.target.TargetBreakpointLocation; +import ghidra.program.model.address.Address; + +public interface DbgModelTargetBreakpointLocation + extends DbgModelTargetObject, TargetBreakpointLocation { + + @Override + public Address getAddress(); + + @Override + public TargetObjectRefList getAffects(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointSpec.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointSpec.java new file mode 100644 index 0000000000..f137de9785 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetBreakpointSpec.java @@ -0,0 +1,181 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.model.iface1.DbgModelTargetBptHelper; +import ghidra.dbg.attributes.TargetObjectRefList; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet; +import ghidra.program.model.address.*; + +public interface DbgModelTargetBreakpointSpec extends // + DbgModelTargetObject, // + TargetBreakpointSpec, // + TargetBreakpointLocation, // + TargetDeletable, // + DbgModelTargetBptHelper { + + String BPT_ACCESS_ATTRIBUTE_NAME = "Access"; + String BPT_DISP_ATTRIBUTE_NAME = "Disposition"; + String BPT_PENDING_ATTRIBUTE_NAME = "Pending"; + String BPT_TIMES_ATTRIBUTE_NAME = "Times"; + String BPT_TYPE_ATTRIBUTE_NAME = "Type"; + String BPT_INDEX_ATTRIBUTE_NAME = "Id"; + + @Override + public default CompletableFuture delete() { + return getManager().deleteBreakpoints(getNumber()); + } + + @Override + public default CompletableFuture disable() { + setEnabled(false, "Disabled"); + return getManager().disableBreakpoints(getNumber()); + } + + @Override + public default CompletableFuture enable() { + setEnabled(true, "Enabled"); + return getManager().enableBreakpoints(getNumber()); + } + + @Override + public default String getExpression() { + return getBreakpointInfo().getLocation(); + } + + public default long getNumber() { + return getBreakpointInfo().getNumber(); + } + + @Override + public default TargetBreakpointKindSet getKinds() { + switch (getBreakpointInfo().getType()) { + case BREAKPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.SOFTWARE); + case HW_BREAKPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.EXECUTE); + case HW_WATCHPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.WRITE); + case READ_WATCHPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.READ); + case ACCESS_WATCHPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.READ, + TargetBreakpointKind.WRITE); + default: + return TargetBreakpointKindSet.of(); + } + } + + @Override + public default CompletableFuture init(Map map) { + AddressSpace space = getModel().getAddressSpace("ram"); + return requestNativeAttributes().thenAccept(attrs -> { + if (attrs != null) { + TargetObject addr = (TargetObject) attrs.get("Address"); + TargetObject id = (TargetObject) attrs.get("Id"); + //TargetObject unique = (TargetObject) attrs.get("UniqueID"); + TargetObject enabled = (TargetObject) attrs.get("IsEnabled"); + String addstr = addr.getCachedAttribute(VALUE_ATTRIBUTE_NAME).toString(); + String idstr = id.getCachedAttribute(VALUE_ATTRIBUTE_NAME).toString(); + setBreakpointId(idstr); + //String uidstr = unique.getCachedAttribute(VALUE_ATTRIBUTE_NAME).toString(); + String enstr = enabled.getCachedAttribute(VALUE_ATTRIBUTE_NAME).toString(); + try { + Address address = space.getAddress(addstr); + map.put(ADDRESS_ATTRIBUTE_NAME, address); + } + catch (AddressFormatException e) { + e.printStackTrace(); + } + map.put(AFFECTS_ATTRIBUTE_NAME, doGetAffects()); + map.put(SPEC_ATTRIBUTE_NAME, this); + map.put(KINDS_ATTRIBUTE_NAME, getKinds()); + map.put(BPT_INDEX_ATTRIBUTE_NAME, Long.decode(idstr)); + map.put(ENABLED_ATTRIBUTE_NAME, enstr.equals("-1")); + setEnabled(enstr.equals("-1"), "Refreshed"); + int size = getBreakpointInfo().getSize(); + map.put(LENGTH_ATTRIBUTE_NAME, size); + + String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME); + String display = "[" + idstr + "] " + addstr; + map.put(DISPLAY_ATTRIBUTE_NAME, display); + setModified(map, !display.equals(oldval)); + } + }); + } + + public default Address doGetAddress() { + DbgBreakpointInfo info = getBreakpointInfo(); + return getModel().getAddress("ram", info.addrAsLong()); + } + + public default TargetObjectRefList doGetAffects() { + DbgModelTargetProcess process = getParentProcess(); + return TargetObjectRefList.of(process); + } + + public default void updateInfo(DbgBreakpointInfo oldInfo, DbgBreakpointInfo newInfo, + String reason) { + synchronized (this) { + assert oldInfo == getBreakpointInfo(); + setBreakpointInfo(newInfo); + } + setEnabled(newInfo.isEnabled(), reason); + } + + /** + * Update the enabled field + * + * This does not actually toggle the breakpoint. It just updates the field and calls the proper + * listeners. To actually toggle the breakpoint, use {@link #toggle(boolean)} instead, which if + * effective, should eventually cause this method to be called. + * + * @param enabled true if enabled, false if disabled + * @param reason a description of the cause (not really used, yet) + */ + public default void setEnabled(boolean enabled, String reason) { + setBreakpointEnabled(enabled); + changeAttributes(List.of(), Map.of(ENABLED_ATTRIBUTE_NAME, enabled // + ), reason); + getListeners().fire(TargetBreakpointSpecListener.class).breakpointToggled(this, enabled); + } + + @Override + public default boolean isEnabled() { + return isBreakpointEnabled(); + } + + @Override + public default void addAction(TargetBreakpointAction action) { + getActions().add(action); + } + + @Override + public default void removeAction(TargetBreakpointAction action) { + getActions().remove(action); + } + + public default void breakpointHit() { + getActions().fire.breakpointHit(this, getParentProcess(), null, this); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetConnector.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetConnector.java new file mode 100644 index 0000000000..154c86a3ef --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetConnector.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.iface1.DbgModelSelectableObject; +import ghidra.dbg.target.TargetLauncher; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; + +public interface DbgModelTargetConnector + extends DbgModelSelectableObject, TargetLauncher { + + @Override + public default String getDisplay() { + return getName(); + } + + @Override + public CompletableFuture select(); + + @Override + public TargetParameterMap getParameters(); + + @Override + public CompletableFuture launch(Map args); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetConnectorContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetConnectorContainer.java new file mode 100644 index 0000000000..8c79ea44f3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetConnectorContainer.java @@ -0,0 +1,22 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +public interface DbgModelTargetConnectorContainer extends DbgModelTargetObject { + + public DbgModelTargetAvailable getTargetAttachable(int pid); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetDebugContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetDebugContainer.java new file mode 100644 index 0000000000..29bc65b4a5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetDebugContainer.java @@ -0,0 +1,21 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +public interface DbgModelTargetDebugContainer extends + DbgModelTargetObject { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEnvironmentEx.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEnvironmentEx.java new file mode 100644 index 0000000000..be4545ce00 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEnvironmentEx.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import ghidra.dbg.target.TargetEnvironment; + +public interface DbgModelTargetEnvironmentEx + extends DbgModelTargetObject, TargetEnvironment { + + public void refresh(); + + /* + @Override + public default String getArchitecture() { + return getTypedAttributeNowByName(ARCH_ATTRIBUTE_NAME, String.class, ""); + } + + @Override + public default String getDebugger() { + return getTypedAttributeNowByName(DEBUGGER_ATTRIBUTE_NAME, String.class, ""); + } + + @Override + public default String getOperatingSystem() { + return getTypedAttributeNowByName(OS_ATTRIBUTE_NAME, String.class, ""); + } + */ + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetMemoryContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetMemoryContainer.java new file mode 100644 index 0000000000..07a532abd1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetMemoryContainer.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgModuleMemory; +import ghidra.dbg.target.TargetMemory; +import ghidra.program.model.address.Address; + +public interface DbgModelTargetMemoryContainer + extends DbgModelTargetObject, TargetMemory { + + public DbgModelTargetMemoryRegion getTargetMemory(DbgModuleMemory region); + + @Override + public CompletableFuture readMemory(Address address, int length); + + @Override + public CompletableFuture writeMemory(Address address, byte[] data); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetMemoryRegion.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetMemoryRegion.java new file mode 100644 index 0000000000..889214e5d9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetMemoryRegion.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import ghidra.dbg.target.TargetMemoryRegion; +import ghidra.program.model.address.AddressRange; + +public interface DbgModelTargetMemoryRegion + extends DbgModelTargetObject, TargetMemoryRegion { + + @Override + public AddressRange getRange(); + + @Override + public boolean isReadable(); + + @Override + public boolean isWritable(); + + @Override + public boolean isExecutable(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModule.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModule.java new file mode 100644 index 0000000000..24e726000c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModule.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgModule; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetObject; +import ghidra.program.model.address.*; + +public interface DbgModelTargetModule extends // + DbgModelTargetObject, // + TargetModule { + + DbgModule getDbgModule(); + + @Override + public default CompletableFuture init(Map map) { + AddressSpace space = getModel().getAddressSpace("ram"); + return requestNativeAttributes().thenAccept(attrs -> { + if (attrs != null) { + TargetObject baseOffset2 = (TargetObject) attrs.get("BaseAddress"); + TargetObject nameAttr = (TargetObject) attrs.get("Name"); + TargetObject size = (TargetObject) attrs.get("Size"); + String basestr = baseOffset2 == null ? "0" + : baseOffset2.getCachedAttribute(VALUE_ATTRIBUTE_NAME).toString(); + String namestr = nameAttr == null ? "" + : nameAttr.getCachedAttribute(VALUE_ATTRIBUTE_NAME).toString(); + String sizestr = + size == null ? "1" : size.getCachedAttribute(VALUE_ATTRIBUTE_NAME).toString(); + Long base = Long.parseUnsignedLong(basestr, 16); + Integer sz = Integer.parseInt(sizestr, 16); + Address min = space.getAddress(base); + Address max = min.add(sz - 1); + AddressRange range = new AddressRangeImpl(min, max); + map.put(RANGE_ATTRIBUTE_NAME, range); + + String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME); + map.put(MODULE_NAME_ATTRIBUTE_NAME, namestr); + map.put(DISPLAY_ATTRIBUTE_NAME, namestr); + setModified(map, !namestr.equals(oldval)); + } + }); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleContainer.java new file mode 100644 index 0000000000..f554afc42b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleContainer.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.iface1.DbgModelTargetEventScope; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetModuleContainer; + +public interface DbgModelTargetModuleContainer extends // + //DbgModelTargetObject, + DbgModelTargetEventScope, // + TargetModuleContainer { + + @Override + public CompletableFuture> addSyntheticModule(String name); + + public CompletableFuture getTargetModule(String name); + + public void libraryLoaded(String name); + + public void libraryUnloaded(String name); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleSection.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleSection.java new file mode 100644 index 0000000000..03a7e80ece --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleSection.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import ghidra.dbg.target.TargetSection; +import ghidra.program.model.address.AddressRange; + +public interface DbgModelTargetModuleSection + extends DbgModelTargetObject, TargetSection { + + @Override + public AddressRange getRange(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleSectionContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleSectionContainer.java new file mode 100644 index 0000000000..854849f395 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetModuleSectionContainer.java @@ -0,0 +1,21 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +public interface DbgModelTargetModuleSectionContainer + extends DbgModelTargetObject { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetObject.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetObject.java new file mode 100644 index 0000000000..c1c4f6ec62 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetObject.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugClient.DebugStatus; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.AbstractDbgModel; +import ghidra.dbg.agent.InvalidatableTargetObjectIf; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.CollectionUtils.Delta; +import ghidra.util.datastruct.ListenerSet; + +public interface DbgModelTargetObject extends TargetObject, InvalidatableTargetObjectIf { + + @Override + public AbstractDbgModel getModel(); + + public default DbgManagerImpl getManager() { + return (DbgManagerImpl) getModel().getManager(); + } + + public default CompletableFuture init(Map map) { + return CompletableFuture.completedFuture(null); + } + + public default DbgManagerImpl getManagerWithCheck() { + DbgManagerImpl impl = (DbgManagerImpl) getModel().getManager(); + if (impl == null) { + return impl; + } + DebugStatus status = impl.getControl().getExecutionStatus(); + if (status.equals(DebugStatus.GO)) { + System.err.println("Attempt to access DbgManager while GO"); + throw new RuntimeException("Attempt to access DbgManager while GO"); + } + return impl; + } + + @Override + public CompletableFuture> fetchElements(); + + @Override + public CompletableFuture> fetchAttributes(); + + public TargetObject getImplParent(); + + public Delta changeAttributes(List remove, Map add, String reason); + + public CompletableFuture> requestNativeAttributes(); + + public ListenerSet getListeners(); + + public DbgModelTargetSession getParentSession(); + + public DbgModelTargetProcess getParentProcess(); + + public DbgModelTargetThread getParentThread(); + + public TargetObject getProxy(); + + public void setModified(Map map, boolean b); + + public void setModified(boolean modified); + + public void resetModified(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetProcess.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetProcess.java new file mode 100644 index 0000000000..74b25702bc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetProcess.java @@ -0,0 +1,89 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.dbgeng.DebugSystemObjects; +import agent.dbgeng.manager.DbgCause.Causes; +import agent.dbgeng.manager.DbgEventsListenerAdapter; +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.cmd.DbgProcessSelectCommand; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.manager.impl.DbgProcessImpl; +import agent.dbgeng.model.iface1.*; +import ghidra.dbg.target.TargetAggregate; +import ghidra.dbg.target.TargetProcess; +import ghidra.dbg.util.PathUtils; + +public interface DbgModelTargetProcess extends // + TargetAggregate, // + TargetProcess, // + DbgModelTargetExecutionStateful, // + DbgModelTargetAccessConditioned, // + DbgModelTargetAttacher, // + DbgModelTargetAttachable, // + DbgModelTargetLauncher, // + DbgModelTargetDeletable, // + DbgModelTargetDetachable, // + DbgModelTargetKillable, // + DbgModelTargetResumable, // + DbgModelTargetSteppable, // + DbgModelTargetInterruptible, // + DbgEventsListenerAdapter, // + DbgModelSelectableObject { + + public void processStarted(Long pid); + + public void processExited(Long exitCode); + + public DbgModelTargetThreadContainer getThreads(); + + public DbgModelTargetModuleContainer getModules(); + + public default DbgProcess getProcess() { + DbgManagerImpl manager = getManager(); + DebugSystemObjects so = manager.getSystemObjects(); + try { + String index = PathUtils.parseIndex(getName()); + Integer pid = Integer.decode(index); + DebugProcessId id = so.getProcessIdBySystemId(pid); + if (id == null) { + id = so.getCurrentProcessId(); + } + DbgProcessImpl process = manager.getProcess(id); + if (process == null) { + process = new DbgProcessImpl(manager, id, pid); + manager.addProcess(process, Causes.UNCLAIMED); + } + return process; + } + catch (IllegalArgumentException e) { + return manager.getCurrentProcess(); + } + } + + @Override + public default CompletableFuture select() { + DbgManagerImpl manager = getManager(); + DbgProcess process = getProcess(); + if (process == null) { + process = manager.getEventProcess(); + } + return manager.execute(new DbgProcessSelectCommand(manager, process)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetProcessContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetProcessContainer.java new file mode 100644 index 0000000000..facc3f527a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetProcessContainer.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.manager.DbgEventsListenerAdapter; +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.model.iface1.DbgModelTargetEventScope; + +public interface DbgModelTargetProcessContainer extends // + //DbgModelTargetObject, + DbgModelTargetEventScope, // + DbgEventsListenerAdapter { + + public DbgModelTargetProcess getTargetProcess(DebugProcessId id); + + public DbgModelTargetProcess getTargetProcess(DbgProcess process); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegister.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegister.java new file mode 100644 index 0000000000..13ce18e8fd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegister.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import agent.dbgeng.manager.impl.DbgRegister; +import ghidra.dbg.target.TargetRegister; + +public interface DbgModelTargetRegister + extends DbgModelTargetObject, TargetRegister { + + @Override + public int getBitLength(); + + public DbgRegister getRegister(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegisterBank.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegisterBank.java new file mode 100644 index 0000000000..75c6b978c1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegisterBank.java @@ -0,0 +1,123 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.math.BigInteger; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.*; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.error.DebuggerModelAccessException; +import ghidra.dbg.error.DebuggerRegisterAccessException; +import ghidra.dbg.target.TargetRegisterBank; +import ghidra.dbg.util.ConversionUtils; +import ghidra.util.datastruct.ListenerSet; + +public interface DbgModelTargetRegisterBank + extends DbgModelTargetObject, TargetRegisterBank { + + public DbgModelTargetRegister getTargetRegister(DbgRegister register); + + @Override + public default CompletableFuture> readRegistersNamed( + Collection names) { + DbgManagerImpl manager = getManager(); + if (manager.isWaiting()) { + throw new DebuggerModelAccessException( + "Cannot process command readRegistersNamed while engine is waiting for events"); + } + + AtomicReference> read = new AtomicReference<>(); + return getManager().getRegisterMap(getPath()).thenCompose(valueMap -> { + Map regs = getCachedAttributes(); + Map map = + new HashMap(); + + for (String regname : names) { + Object x = regs.get(regname); + if (!(x instanceof DbgModelTargetRegister)) { + continue; + } + if (!valueMap.containsKey(regname)) { + continue; + } + DbgModelTargetRegister reg = (DbgModelTargetRegister) x; + DbgRegister register = (DbgRegister) valueMap.get(regname); + if (register != null) { + map.put(register, reg); + } + } + read.set(map); + return getParentThread().getThread().readRegisters(map.keySet()); + }).thenApply(vals -> { + Map result = new LinkedHashMap<>(); + for (DbgRegister dbgReg : vals.keySet()) { + DbgModelTargetRegister reg = read.get().get(dbgReg); + String oldval = (String) reg.getCachedAttributes().get(VALUE_ATTRIBUTE_NAME); + BigInteger value = vals.get(dbgReg); + byte[] bytes = ConversionUtils.bigIntegerToBytes(dbgReg.getSize(), value); + result.put(dbgReg.getName(), bytes); + reg.changeAttributes(List.of(), Map.of( // + VALUE_ATTRIBUTE_NAME, value.toString(16) // + ), "Refreshed"); + if (value.longValue() != 0) { + String newval = reg.getName() + " : " + value.toString(16); + reg.changeAttributes(List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, newval // + ), "Refreshed"); + reg.setModified(value.toString(16).equals(oldval)); + } + } + ListenerSet listeners = getListeners(); + if (listeners != null) { + listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, result); + } + return result; + }); + } + + @Override + public default CompletableFuture writeRegistersNamed(Map values) { + DbgThread thread = getParentThread().getThread(); + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + fetchElements().handle(seq::nextIgnore); + }).then(seq -> { + thread.listRegisters().handle(seq::next); + }, TypeSpec.cls(DbgRegisterSet.class)).then((regset, seq) -> { + Map regs = getCachedAttributes(); + Map toWrite = new LinkedHashMap<>(); + for (Map.Entry ent : values.entrySet()) { + String regname = ent.getKey(); + DbgModelTargetRegister reg = (DbgModelTargetRegister) regs.get(regname); + if (reg == null) { + throw new DebuggerRegisterAccessException("No such register: " + regname); + } + BigInteger val = new BigInteger(1, ent.getValue()); + DbgRegister dbgreg = regset.get(regname); + toWrite.put(dbgreg, val); + } + getParentThread().getThread().writeRegisters(toWrite).handle(seq::next); + // TODO: Should probably filter only effective and normalized writes in the callback + }).then(seq -> { + getListeners().fire(TargetRegisterBankListener.class).registersUpdated(this, values); + seq.exit(); + }).finish(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegisterContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegisterContainer.java new file mode 100644 index 0000000000..3836ead6c5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRegisterContainer.java @@ -0,0 +1,127 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.math.BigInteger; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.*; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.error.DebuggerModelAccessException; +import ghidra.dbg.error.DebuggerRegisterAccessException; +import ghidra.dbg.target.TargetRegisterBank; +import ghidra.dbg.target.TargetRegisterContainer; +import ghidra.dbg.util.ConversionUtils; +import ghidra.util.datastruct.ListenerSet; + +public interface DbgModelTargetRegisterContainer + extends DbgModelTargetObject, TargetRegisterContainer, + TargetRegisterBank { + + public DbgModelTargetRegister getTargetRegister(DbgRegister register); + + @Override + public default CompletableFuture> readRegistersNamed( + Collection names) { + DbgManagerImpl manager = getManager(); + if (manager.isWaiting()) { + throw new DebuggerModelAccessException( + "Cannot process command readRegistersNamed while engine is waiting for events"); + } + + // THESE ARE NOT CURRENLY USED - SEE DbgModelTargetRegisterBank + + AtomicReference> read = new AtomicReference<>(); + return getManager().getRegisterMap(getPath()).thenCompose(valueMap -> { + Map regs = getCachedAttributes(); + Map map = + new HashMap(); + + for (String regname : names) { + Object x = regs.get(regname); + if (!(x instanceof DbgModelTargetRegister)) { + continue; + } + if (!valueMap.containsKey(regname)) { + continue; + } + DbgModelTargetRegister reg = (DbgModelTargetRegister) x; + DbgRegister register = (DbgRegister) valueMap.get(regname); + if (register != null) { + map.put(register, reg); + } + } + read.set(map); + return getParentThread().getThread().readRegisters(map.keySet()); + }).thenApply(vals -> { + Map result = new LinkedHashMap<>(); + for (DbgRegister dbgReg : vals.keySet()) { + DbgModelTargetRegister reg = read.get().get(dbgReg); + String oldval = (String) reg.getCachedAttributes().get(VALUE_ATTRIBUTE_NAME); + BigInteger value = vals.get(dbgReg); + byte[] bytes = ConversionUtils.bigIntegerToBytes(dbgReg.getSize(), value); + result.put(dbgReg.getName(), bytes); + reg.changeAttributes(List.of(), Map.of( // + VALUE_ATTRIBUTE_NAME, value.toString(16) // + ), "Refreshed"); + if (value.longValue() != 0) { + String newval = reg.getName() + " : " + value.toString(16); + reg.changeAttributes(List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, newval // + ), "Refreshed"); + reg.setModified(value.toString(16).equals(oldval)); + } + } + ListenerSet listeners = getListeners(); + if (listeners != null) { + listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, result); + } + return result; + }); + } + + @Override + public default CompletableFuture writeRegistersNamed(Map values) { + DbgThread thread = getParentThread().getThread(); + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + fetchElements().handle(seq::nextIgnore); + }).then(seq -> { + thread.listRegisters().handle(seq::next); + }, TypeSpec.cls(DbgRegisterSet.class)).then((regset, seq) -> { + Map regs = getCachedAttributes(); + Map toWrite = new LinkedHashMap<>(); + for (Map.Entry ent : values.entrySet()) { + String regname = ent.getKey(); + DbgModelTargetRegister reg = (DbgModelTargetRegister) regs.get(regname); + if (reg == null) { + throw new DebuggerRegisterAccessException("No such register: " + regname); + } + BigInteger val = new BigInteger(1, ent.getValue()); + DbgRegister dbgreg = regset.get(regname); + toWrite.put(dbgreg, val); + } + getParentThread().getThread().writeRegisters(toWrite).handle(seq::next); + // TODO: Should probably filter only effective and normalized writes in the callback + }).then(seq -> { + getListeners().fire(TargetRegisterBankListener.class).registersUpdated(this, values); + seq.exit(); + }).finish(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRoot.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRoot.java new file mode 100644 index 0000000000..fc94a3db81 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetRoot.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import agent.dbgeng.manager.DbgEventsListenerAdapter; +import agent.dbgeng.model.iface1.*; + +public interface DbgModelTargetRoot extends // + ///DbgModelTargetObject, + DbgModelTargetAccessConditioned, // + DbgModelTargetAttacher, // + DbgModelTargetEventScope, // + DbgModelTargetLauncher, // + DbgModelTargetFocusScope, // + DbgEventsListenerAdapter { + + void setDefaultConnector(DbgModelTargetConnector defaultConnector); + + // getFocus & requestFocus implemented by DbgModelTargetObject & DbgModelTargetFocusScope +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSession.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSession.java new file mode 100644 index 0000000000..32b7792ea4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSession.java @@ -0,0 +1,92 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugClient.DebugOutputFlags; +import agent.dbgeng.dbgeng.DebugSessionId; +import agent.dbgeng.manager.DbgCause.Causes; +import agent.dbgeng.manager.DbgEventsListenerAdapter; +import agent.dbgeng.manager.DbgSession; +import agent.dbgeng.manager.cmd.DbgSessionSelectCommand; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.manager.impl.DbgSessionImpl; +import agent.dbgeng.model.iface1.*; +import ghidra.dbg.target.TargetAggregate; +import ghidra.dbg.target.TargetConsole; +import ghidra.dbg.target.TargetConsole.Channel; +import ghidra.dbg.target.TargetFocusScope.TargetFocusScopeListener; +import ghidra.dbg.util.PathUtils; + +public interface DbgModelTargetSession extends // + DbgModelTargetAccessConditioned, // + //DbgModelTargetFocusScope, // + DbgModelTargetExecutionStateful, // + DbgModelTargetInterpreter, // + DbgModelTargetInterruptible, // + DbgModelTargetResumable, // + DbgEventsListenerAdapter, // + DbgModelSelectableObject, // + TargetFocusScopeListener, // + TargetAggregate { + + DbgModelTargetProcessContainer getProcesses(); + + public default DbgSession getSession() { + DbgManagerImpl manager = getManager(); + try { + String index = PathUtils.parseIndex(getName()); + Integer sid = Integer.decode(index); + DebugSessionId id = new DebugSessionId(sid); + DbgSessionImpl session = (DbgSessionImpl) manager.getSession(id); + if (session == null) { + session = new DbgSessionImpl(manager, id); + manager.addSession(session, Causes.UNCLAIMED); + } + return session; + } + catch (IllegalArgumentException e) { + return manager.getCurrentSession(); + } + } + + @Override + public default void consoleOutput(String output, int mask) { + + Channel chan = TargetConsole.Channel.STDOUT; + if (((mask & DebugOutputFlags.DEBUG_OUTPUT_ERROR.getValue()) // + == DebugOutputFlags.DEBUG_OUTPUT_ERROR.getValue()) || // + ((mask & DebugOutputFlags.DEBUG_OUTPUT_WARNING.getValue()) // + == DebugOutputFlags.DEBUG_OUTPUT_WARNING.getValue())) { + chan = TargetConsole.Channel.STDERR; + } + if (output.contains("loaded *kernel* extension dll for usermode")) { + return; + } + getListeners().fire(TargetInterpreterListener.class).consoleOutput(this, chan, output); + } + + @Override + public default CompletableFuture select() { + DbgManagerImpl manager = getManager(); + DbgSession session = getSession(); + if (session == null) { + session = manager.getEventSession(); + } + return manager.execute(new DbgSessionSelectCommand(manager, session)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionAttributes.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionAttributes.java new file mode 100644 index 0000000000..f4657555db --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionAttributes.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import agent.dbgeng.manager.DbgEventsListenerAdapter; +import agent.dbgeng.model.iface1.DbgModelTargetEnvironment; + +public interface DbgModelTargetSessionAttributes extends + //DbgModelTargetObject, + DbgModelTargetEnvironment, // + DbgEventsListenerAdapter { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionAttributesMachine.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionAttributesMachine.java new file mode 100644 index 0000000000..e064a7c173 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionAttributesMachine.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import agent.dbgeng.manager.DbgEventsListenerAdapter; + +public interface DbgModelTargetSessionAttributesMachine extends // + DbgModelTargetObject, // + DbgEventsListenerAdapter { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionContainer.java new file mode 100644 index 0000000000..3edf89ffa8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSessionContainer.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import agent.dbgeng.dbgeng.DebugSessionId; +import agent.dbgeng.manager.*; + +public interface DbgModelTargetSessionContainer + extends DbgModelTargetObject, DbgEventsListenerAdapter { + + @Override + public default void sessionAdded(DbgSession session, DbgCause cause) { + //refresh(); + } + + @Override + public void sessionRemoved(DebugSessionId sessionId, DbgCause cause); + + public DbgModelTargetSession getTargetSession(DbgSession session); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetStack.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetStack.java new file mode 100644 index 0000000000..fab2d9445a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetStack.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import agent.dbgeng.manager.DbgStackFrame; +import ghidra.dbg.target.TargetStack; + +public interface DbgModelTargetStack + extends DbgModelTargetObject, TargetStack { + + public DbgModelTargetStackFrame getTargetFrame(DbgStackFrame frame); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetStackFrame.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetStackFrame.java new file mode 100644 index 0000000000..5ce2855475 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetStackFrame.java @@ -0,0 +1,100 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgEventsListenerAdapter; +import agent.dbgeng.manager.DbgStackFrame; +import agent.dbgeng.manager.cmd.DbgThreadSelectCommand; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.manager.impl.DbgThreadImpl; +import agent.dbgeng.model.iface1.DbgModelSelectableObject; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetStackFrame; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; + +public interface DbgModelTargetStackFrame extends // + //DbgModelTargetObject, + TargetStackFrame, // + DbgEventsListenerAdapter, // + DbgModelSelectableObject { + + public static final String FUNC_ATTRIBUTE_NAME = "function"; + public static final String FUNC_TABLE_ENTRY_ATTRIBUTE_NAME = "Table Entry"; + public static final String FRAME_OFFSET_ATTRIBUTE_NAME = "Frame Offset"; + public static final String INST_OFFSET_ATTRIBUTE_NAME = "Inst. Offset"; + public static final String RETURN_OFFSET_ATTRIBUTE_NAME = "Return Offset"; + public static final String STACK_OFFSET_ATTRIBUTE_NAME = "Stack Offset"; + public static final String VIRTUAL_ATTRIBUTE_NAME = "Virtual"; + public static final String PARAM0_ATTRIBUTE_NAME = "Param[0]"; + public static final String PARAM1_ATTRIBUTE_NAME = "Param[1]"; + public static final String PARAM2_ATTRIBUTE_NAME = "Param[2]"; + public static final String PARAM3_ATTRIBUTE_NAME = "Param[3]"; + public static final String FROM_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "from"; // TODO + + @Override + public default CompletableFuture select() { + DbgManagerImpl manager = getManager(); + DbgThreadImpl thread = manager.getCurrentThread(); + return manager.execute(new DbgThreadSelectCommand(manager, thread, null)); + } + + @Override + public default CompletableFuture init(Map map) { + AddressSpace space = getModel().getAddressSpace("ram"); + return requestNativeAttributes().thenCompose(attrs -> { + if (attrs == null) { + return CompletableFuture.completedFuture(null); + } + TargetObject attributes = (TargetObject) attrs.get("Attributes"); + if (attributes == null) { + return CompletableFuture.completedFuture(null); + } + return attributes.fetchAttributes(true); + }).thenCompose(subattrs -> { + if (subattrs == null) { + return CompletableFuture.completedFuture(null); + } + TargetObject frameNumber = (TargetObject) subattrs.get("FrameNumber"); + return frameNumber.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenCompose(noval -> { + String nostr = noval.toString(); + TargetObject instructionOffset = (TargetObject) subattrs.get("InstructionOffset"); + return instructionOffset.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenAccept(pcval -> { + String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME); + String pcstr = pcval.toString(); + long pc = Long.parseUnsignedLong(pcstr, 16); + map.put(PC_ATTRIBUTE_NAME, space.getAddress(pc)); + String display = String.format("#%s 0x%s", nostr, pcstr); + map.put(DISPLAY_ATTRIBUTE_NAME, display); + setModified(map, !display.equals(oldval)); + }); + }); + }); + } + + public void setFrame(DbgStackFrame frame); + + public TargetObjectRef getThread(); + + public Address getPC(); + + public DbgModelTargetProcess getProcess(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSymbol.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSymbol.java new file mode 100644 index 0000000000..ebdc7437d1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSymbol.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import ghidra.dbg.target.TargetSymbol; +import ghidra.program.model.address.Address; + +public interface DbgModelTargetSymbol + extends DbgModelTargetObject, TargetSymbol { + + @Override + public boolean isConstant(); + + @Override + public Address getValue(); + + @Override + public long getSize(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSymbolContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSymbolContainer.java new file mode 100644 index 0000000000..995f7617d7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSymbolContainer.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import agent.dbgeng.manager.impl.DbgMinimalSymbol; +import agent.dbgeng.model.impl.DbgModelTargetSymbolImpl; +import ghidra.dbg.target.TargetSymbolNamespace; + +public interface DbgModelTargetSymbolContainer + extends DbgModelTargetObject, TargetSymbolNamespace { + + public DbgModelTargetSymbolImpl getTargetSymbol(DbgMinimalSymbol symbol); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetTTD.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetTTD.java new file mode 100644 index 0000000000..26a00aa696 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetTTD.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.target.TargetObject; + +public interface DbgModelTargetTTD extends DbgModelTargetObject { + + @Override + public default CompletableFuture init(Map map) { + return requestNativeAttributes().thenCompose(attrs -> { + if (attrs == null) { + return CompletableFuture.completedFuture(null); + } + TargetObject attributes = (TargetObject) attrs.get("Position"); + if (attributes == null) { + return CompletableFuture.completedFuture(null); + } + return attributes.fetchAttributes(true); + }).thenCompose(subattrs -> { + if (subattrs == null) { + return CompletableFuture.completedFuture(null); + } + TargetObject seq = (TargetObject) subattrs.get("Sequence"); + return seq.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenCompose(sqval -> { + String sqstr = sqval.toString(); + TargetObject steps = (TargetObject) subattrs.get("Steps"); + return steps.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenAccept(stval -> { + String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME); + String ststr = stval.toString(); + String display = String.format("TTD %s:%s", sqstr, ststr); + map.put(DISPLAY_ATTRIBUTE_NAME, display); + setModified(map, !display.equals(oldval)); + }); + }); + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetThread.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetThread.java new file mode 100644 index 0000000000..1f8008a6d2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetThread.java @@ -0,0 +1,78 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugSystemObjects; +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.cmd.DbgThreadSelectCommand; +import agent.dbgeng.manager.impl.*; +import agent.dbgeng.model.iface1.*; +import agent.dbgeng.model.impl.DbgModelTargetStackImpl; +import ghidra.dbg.target.TargetThread; +import ghidra.dbg.util.PathUtils; + +public interface DbgModelTargetThread extends // + TargetThread, // + DbgModelTargetAccessConditioned, // + DbgModelTargetExecutionStateful, // + DbgModelTargetSteppable, // + DbgEventsListenerAdapter, // + DbgModelSelectableObject { + + public default DbgThread getThread() { + DbgManagerImpl manager = getManager(); + DebugSystemObjects so = manager.getSystemObjects(); + try { + String index = PathUtils.parseIndex(getName()); + int tid = Integer.decode(index); + DebugThreadId id = so.getThreadIdBySystemId(tid); + if (id == null) { + id = so.getCurrentThreadId(); + } + DbgThreadImpl thread = manager.getThread(id); + if (thread == null) { + DbgModelTargetProcess parentProcess = getParentProcess(); + DbgProcessImpl process = (DbgProcessImpl) parentProcess.getProcess(); + thread = new DbgThreadImpl(manager, process, id, tid); + manager.addThread(thread); + + } + return thread; + } + catch (IllegalArgumentException e) { + return manager.getCurrentThread(); + } + } + + public void threadStateChanged(DbgState state, DbgReason reason); + + @Override + public default CompletableFuture select() { + DbgManagerImpl manager = getManager(); + DbgThread thread = getThread(); + return manager.execute(new DbgThreadSelectCommand(manager, thread, null)); + } + + public DbgModelTargetRegisterContainer getRegisters(); + + public DbgModelTargetStackImpl getStack(); + + public String getExecutingProcessorType(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetThreadContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetThreadContainer.java new file mode 100644 index 0000000000..a36da91bd2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetThreadContainer.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.DbgEventsListenerAdapter; +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.model.iface1.DbgModelTargetEventScope; + +public interface DbgModelTargetThreadContainer extends // + //DbgModelTargetObject, + DbgModelTargetEventScope, // + DbgEventsListenerAdapter { + + public void threadCreated(DbgThread thread); + + public void threadExited(DebugThreadId threadId); + + public DbgModelTargetThread getTargetThread(DbgThread thread); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelDefaultTargetModelRoot.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelDefaultTargetModelRoot.java new file mode 100644 index 0000000000..f90368e377 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelDefaultTargetModelRoot.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import ghidra.dbg.target.TargetAggregate; + +public class DbgModelDefaultTargetModelRoot extends DbgModelTargetObjectImpl + implements TargetAggregate { + + public DbgModelDefaultTargetModelRoot(DbgModelImpl model, String typeHint) { + super(model, null, null, typeHint); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImpl.java new file mode 100644 index 0000000000..b9f2ad93f9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImpl.java @@ -0,0 +1,113 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugSessionId; +import agent.dbgeng.manager.DbgCause.Causes; +import agent.dbgeng.manager.DbgManager; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.manager.impl.DbgSessionImpl; +import agent.dbgeng.model.AbstractDbgModel; +import agent.dbgeng.model.iface2.DbgModelTargetSession; +import agent.dbgeng.model.iface2.DbgModelTargetSessionContainer; +import ghidra.dbg.target.TargetObject; +import ghidra.program.model.address.*; + +public class DbgModelImpl extends AbstractDbgModel { + // TODO: Need some minimal memory modeling per architecture on the model/agent side. + // The model must convert to and from Ghidra's address space names + protected static final String SPACE_NAME = "ram"; + + // Don't make this static, so each model has a unique "GDB" space + protected final AddressSpace space = + new GenericAddressSpace(SPACE_NAME, 64, AddressSpace.TYPE_RAM, 0); + protected final AddressFactory addressFactory = + new DefaultAddressFactory(new AddressSpace[] { space }); + + protected final DbgManager dbg; + protected final DbgModelTargetRootImpl root; + protected final DbgModelTargetSessionImpl session; + + protected final CompletableFuture completedRoot; + + public DbgModelImpl() { + this.dbg = DbgManager.newInstance(); + this.root = new DbgModelTargetRootImpl(this); + this.completedRoot = CompletableFuture.completedFuture(root); + DbgSessionImpl s = new DbgSessionImpl((DbgManagerImpl) dbg, new DebugSessionId(0)); + s.add(Causes.UNCLAIMED); + DbgModelTargetSessionContainer sessions = root.sessions; + this.session = (DbgModelTargetSessionImpl) sessions.getTargetSession(s); + } + + @Override + public AddressSpace getAddressSpace(String name) { + if (!SPACE_NAME.equals(name)) { + return null; + } + return space; + } + + // TODO: Place make this a model method? + @Override + public AddressFactory getAddressFactory() { + return addressFactory; + } + + @Override + public CompletableFuture startDbgEng(String[] args) { + return dbg.start(args); + } + + @Override + public boolean isRunning() { + return dbg.isRunning(); + } + + @Override + public void terminate() throws IOException { + dbg.terminate(); + } + + @Override + public CompletableFuture fetchModelRoot() { + return completedRoot; + } + + @Override + public DbgManagerImpl getManager() { + return (DbgManagerImpl) dbg; + } + + @Override + public CompletableFuture close() { + try { + terminate(); + return CompletableFuture.completedFuture(null); + } + catch (Throwable t) { + return CompletableFuture.failedFuture(t); + } + } + + @Override + public DbgModelTargetSession getSession() { + return session; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImplUtils.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImplUtils.java new file mode 100644 index 0000000000..d324153e38 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImplUtils.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.model.AbstractDbgModel; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; + +public enum DbgModelImplUtils { + ; + public static CompletableFuture launch(AbstractDbgModel impl, DbgProcess process, + List args) { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + process.fileExecAndSymbols(args.get(0)); + }).finish(); + } + + public static V noDupMerge(V first, V second) { + throw new AssertionError(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetAvailableContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetAvailableContainerImpl.java new file mode 100644 index 0000000000..a85bb9e587 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetAvailableContainerImpl.java @@ -0,0 +1,65 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.Pair; + +import agent.dbgeng.model.iface2.*; +import ghidra.dbg.target.TargetObject; +import ghidra.util.datastruct.WeakValueHashMap; + +public class DbgModelTargetAvailableContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetAvailableContainer { + + protected final Map attachablesById = + new WeakValueHashMap<>(); + + public DbgModelTargetAvailableContainerImpl(DbgModelTargetRoot root) { + super(root.getModel(), root, "Available", "AvailableContainer"); + changeAttributes(List.of(), List.of(), Map.of( // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.SOLICITED // + ), "Initialized"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listAvailableProcesses().thenAccept(list -> { + List available; + synchronized (this) { + // NOTE: If more details added to entries, should clear attachablesById + available = + list.stream().map(this::getTargetAttachableEx).collect(Collectors.toList()); + } + setElements(available, Map.of(), "Refreshed"); + }); + } + + public synchronized DbgModelTargetAvailable getTargetAttachableEx(Pair pair) { + return attachablesById.computeIfAbsent(pair.getLeft(), + i -> new DbgModelTargetAvailableImpl(this, pair.getLeft(), pair.getRight())); + } + + @Override + public synchronized DbgModelTargetAvailable getTargetAttachable(int pid) { + return attachablesById.computeIfAbsent(pid, + i -> new DbgModelTargetAvailableImpl(this, pid)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetAvailableImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetAvailableImpl.java new file mode 100644 index 0000000000..cfcf8a761e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetAvailableImpl.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.dbgeng.model.iface2.DbgModelTargetAvailable; +import agent.dbgeng.model.iface2.DbgModelTargetAvailableContainer; +import ghidra.dbg.util.PathUtils; + +public class DbgModelTargetAvailableImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetAvailable { + + protected static final String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid"; + // TODO: DESCRIPTION, TYPE, USER? + + protected static String indexAttachable(int pid) { + return Integer.toHexString(pid); + } + + protected static String keyAttachable(int pid) { + return PathUtils.makeKey(indexAttachable(pid)); + } + + protected final int pid; + + public DbgModelTargetAvailableImpl(DbgModelTargetAvailableContainer parent, int pid, + String name) { + super(parent.getModel(), parent, keyAttachable(pid), name); + this.pid = pid; + + this.changeAttributes(List.of(), List.of(), Map.of(// + PID_ATTRIBUTE_NAME, pid, // + DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid) + " : " + name.trim(), + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + public DbgModelTargetAvailableImpl(DbgModelTargetAvailableContainer parent, int pid) { + super(parent.getModel(), parent, keyAttachable(pid), "Attachable"); + this.pid = pid; + + this.changeAttributes(List.of(), List.of(), Map.of(// + PID_ATTRIBUTE_NAME, pid, // + DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public long getPid() { + return pid; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetBreakpointContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetBreakpointContainerImpl.java new file mode 100644 index 0000000000..284feb4fe4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetBreakpointContainerImpl.java @@ -0,0 +1,104 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.dbgeng.manager.DbgCause; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface2.*; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.dbg.target.TargetObject; +import ghidra.util.datastruct.WeakValueHashMap; + +public class DbgModelTargetBreakpointContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetBreakpointContainer { + + protected static final TargetBreakpointKindSet SUPPORTED_KINDS = + TargetBreakpointKindSet.of(TargetBreakpointKind.values()); + + private final Map specsByNumber = new WeakValueHashMap<>(); + + public DbgModelTargetBreakpointContainerImpl(DbgModelTargetDebugContainer debug) { + super(debug.getModel(), debug, "Breakpoints", "BreakpointContainer"); + + getManager().addEventsListener(this); + + changeAttributes(List.of(), List.of(), Map.of( // + // TODO: Seems terrible to duplicate this static attribute on each instance + SUPPORTED_BREAK_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS // + ), "Initialized"); + } + + @Override + public void breakpointCreated(DbgBreakpointInfo info, DbgCause cause) { + changeElements(List.of(), List.of(getTargetBreakpointSpec(info)), Map.of(), "Created"); + } + + @Override + public void breakpointModified(DbgBreakpointInfo newInfo, DbgBreakpointInfo oldInfo, + DbgCause cause) { + getTargetBreakpointSpec(oldInfo).updateInfo(oldInfo, newInfo, "Modified"); + } + + @Override + public void breakpointDeleted(DbgBreakpointInfo info, DbgCause cause) { + synchronized (this) { + getSpecsByNumber().remove(info.getNumber()); + } + changeElements(List.of( // + DbgModelTargetBreakpointSpecImpl.indexBreakpoint(info) // + ), List.of(), Map.of(), "Deleted"); + } + + @Override + public void breakpointHit(DbgBreakpointInfo info, DbgCause cause) { + DbgModelTargetBreakpointSpec spec = getTargetBreakpointSpec(info); + listeners.fire(TargetBreakpointListener.class) + .breakpointHit(this, getParentProcess(), null, spec, spec); + spec.breakpointHit(); + } + + public DbgModelTargetBreakpointSpec getTargetBreakpointSpec(DbgBreakpointInfo info) { + synchronized (this) { + return getSpecsByNumber().computeIfAbsent(info.getNumber(), + i -> new DbgModelTargetBreakpointSpecImpl(this, info)); + } + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + DbgManagerImpl manager = getManager(); + return manager.listBreakpoints().thenAccept(byNumber -> { + List specs; + synchronized (this) { + specs = byNumber.values() + .stream() + .map(this::getTargetBreakpointSpec) + .collect(Collectors.toList()); + } + setElements(specs, Map.of(), "Refreshed"); + }); + } + + public Map getSpecsByNumber() { + return specsByNumber; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetBreakpointSpecImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetBreakpointSpecImpl.java new file mode 100644 index 0000000000..e5b9fffed8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetBreakpointSpecImpl.java @@ -0,0 +1,146 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.model.iface2.DbgModelTargetBreakpointContainer; +import agent.dbgeng.model.iface2.DbgModelTargetBreakpointSpec; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; +import ghidra.util.datastruct.ListenerSet; + +public class DbgModelTargetBreakpointSpecImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetBreakpointSpec { + + protected static String indexBreakpoint(DbgBreakpointInfo info) { + return PathUtils.makeIndex(info.getNumber()); + } + + protected static String keyBreakpoint(DbgBreakpointInfo info) { + return PathUtils.makeKey(indexBreakpoint(info)); + } + + protected DbgBreakpointInfo info; + protected boolean enabled; + + public void changeAttributeSet(String reason) { + this.changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, "[" + info.getNumber() + "] " + info.getLocation(), // + ADDRESS_ATTRIBUTE_NAME, doGetAddress(), // + LENGTH_ATTRIBUTE_NAME, info.getSize(), // + AFFECTS_ATTRIBUTE_NAME, doGetAffects(), // + SPEC_ATTRIBUTE_NAME, this, // + KINDS_ATTRIBUTE_NAME, getKinds() // + ), reason); + this.changeAttributes(List.of(), List.of(), Map.of( // + BPT_TYPE_ATTRIBUTE_NAME, info.getType().name(), // + BPT_DISP_ATTRIBUTE_NAME, info.getDisp().name(), // + BPT_PENDING_ATTRIBUTE_NAME, info.getPending(), // + BPT_TIMES_ATTRIBUTE_NAME, info.getTimes(), // + TargetObject.UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), reason); + } + + private final ListenerSet actions = + new ListenerSet<>(TargetBreakpointAction.class) { + // Use strong references on actions + protected Map createMap() { + return Collections.synchronizedMap(new LinkedHashMap<>()); + } + }; + + public DbgModelTargetBreakpointSpecImpl(DbgModelTargetBreakpointContainer breakpoints, + DbgBreakpointInfo info) { + super(breakpoints.getModel(), breakpoints, keyBreakpoint(info), "BreakpointSpec"); + this.setBreakpointInfo(info); + + updateInfo(null, info, "Created"); + } + + public void updateInfo(DbgBreakpointInfo oldInfo, DbgBreakpointInfo newInfo, String reason) { + synchronized (this) { + assert oldInfo == getBreakpointInfo(); + setBreakpointInfo(newInfo); + } + changeAttributeSet("Refreshed"); + setEnabled(newInfo.isEnabled(), reason); + } + + @Override + public DbgBreakpointInfo getBreakpointInfo() { + return info; + } + + @Override + public void setBreakpointId(String id) { + throw new AssertionError(); + } + + @Override + public void setBreakpointInfo(DbgBreakpointInfo info) { + this.info = info; + } + + /** + * Update the enabled field + * + * This does not actually toggle the breakpoint. It just updates the field and calls the proper + * listeners. To actually toggle the breakpoint, use {@link #toggle(boolean)} instead, which if + * effective, should eventually cause this method to be called. + * + * @param enabled true if enabled, false if disabled + * @param reason a description of the cause (not really used, yet) + */ + public void setEnabled(boolean enabled, String reason) { + setBreakpointEnabled(enabled); + changeAttributes(List.of(), List.of(), Map.of(ENABLED_ATTRIBUTE_NAME, enabled // + ), reason); + getListeners().fire(TargetBreakpointSpecListener.class).breakpointToggled(this, enabled); + } + + @Override + public boolean isBreakpointEnabled() { + return enabled; + } + + @Override + public void setBreakpointEnabled(boolean enabled) { + this.enabled = enabled; + } + + public ListenerSet getActions() { + return actions; + } + + protected CompletableFuture getInfo() { + return getManager().listBreakpoints() + .thenApply(__ -> getManager().getKnownBreakpoints().get(getNumber())); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getInfo().thenAccept(i -> { + synchronized (this) { + setBreakpointInfo(i); + } + changeAttributeSet("Initialized"); + }); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetConnectorContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetConnectorContainerImpl.java new file mode 100644 index 0000000000..75149fa78b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetConnectorContainerImpl.java @@ -0,0 +1,65 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.dbgeng.model.iface2.DbgModelTargetConnector; +import agent.dbgeng.model.iface2.DbgModelTargetRoot; + +public class DbgModelTargetConnectorContainerImpl extends DbgModelTargetObjectImpl { + + protected final DbgModelTargetRoot root; + + private DbgModelTargetConnector defaultConnector; + + protected final DbgModelTargetProcessLaunchConnectorImpl processLauncher; + protected final DbgModelTargetProcessAttachConnectorImpl processAttacher; + protected final DbgModelTargetTraceOrDumpConnectorImpl traceLoader; + protected final DbgModelTargetKernelConnectorImpl kernelAttacher; + + public DbgModelTargetConnectorContainerImpl(DbgModelTargetRoot root) { + super(root.getModel(), root, "Connectors", "ConnectorsContainer"); + this.root = root; + + this.processLauncher = new DbgModelTargetProcessLaunchConnectorImpl(this, "Launch process"); + this.processAttacher = + new DbgModelTargetProcessAttachConnectorImpl(this, "Attach to process"); + this.traceLoader = new DbgModelTargetTraceOrDumpConnectorImpl(this, "Load trace/dump"); + this.kernelAttacher = new DbgModelTargetKernelConnectorImpl(this, "Attach to kernel"); + this.defaultConnector = processLauncher; + + changeAttributes(List.of(), List.of( // + processAttacher, // + processLauncher, // + traceLoader, // + kernelAttacher // + ), Map.of( // + DISPLAY_ATTRIBUTE_NAME, "Connectors" // + ), "Initialized"); + } + + public DbgModelTargetConnector getDefaultConnector() { + return defaultConnector; + } + + public void setDefaultConnector(DbgModelTargetConnector defaultConnector) { + this.defaultConnector = defaultConnector; + root.setDefaultConnector(defaultConnector); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetDebugContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetDebugContainerImpl.java new file mode 100644 index 0000000000..eb47cb3f01 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetDebugContainerImpl.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.dbgeng.model.iface2.DbgModelTargetDebugContainer; +import agent.dbgeng.model.iface2.DbgModelTargetProcess; + +public class DbgModelTargetDebugContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetDebugContainer { + + protected final DbgModelTargetBreakpointContainerImpl breakpoints; + private DbgModelTargetProcess process; + + public DbgModelTargetDebugContainerImpl(DbgModelTargetProcess process) { + super(process.getModel(), process, "Debug", "DebugContainer"); + this.process = process; + + this.breakpoints = new DbgModelTargetBreakpointContainerImpl(this); + + changeAttributes(List.of(), List.of( // + breakpoints // + ), Map.of(), "Initialized"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetKernelConnectorImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetKernelConnectorImpl.java new file mode 100644 index 0000000000..86c73e88d2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetKernelConnectorImpl.java @@ -0,0 +1,82 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface2.DbgModelTargetConnector; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; + +public class DbgModelTargetKernelConnectorImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetConnector { + + protected final DbgModelTargetConnectorContainerImpl connectors; + protected final TargetParameterMap paramDescs; + + public DbgModelTargetKernelConnectorImpl(DbgModelTargetConnectorContainerImpl connectors, + String name) { + super(connectors.getModel(), connectors, name, name); + this.connectors = connectors; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public CompletableFuture select() { + connectors.setDefaultConnector(this); + return CompletableFuture.completedFuture(null); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription flags = ParameterDescription.create(Long.class, "Flags", true, + 0L, "Flags", "0=target 1=eXDI driver"); + ParameterDescription options = ParameterDescription.create(String.class, "Options", + true, "", "Options", "-k connection options"); + map.put("Flags", flags); + map.put("Options", options); + return map; + } + + @Override + public TargetParameterMap getParameters() { + return TargetMethod.getParameters(this); + } + + @Override + public CompletableFuture launch(Map args) { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + DbgManagerImpl manager = getManager(); + manager.addProcess(); + manager.attachKernel(args).handle(seq::nextIgnore); + }).finish().exceptionally((exc) -> { + throw new DebuggerUserException("Launch failed for " + args); + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetMemoryContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetMemoryContainerImpl.java new file mode 100644 index 0000000000..fafc1d0c43 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetMemoryContainerImpl.java @@ -0,0 +1,255 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.google.common.collect.Range; +import com.google.common.collect.RangeSet; + +import agent.dbgeng.manager.DbgModuleMemory; +import agent.dbgeng.manager.cmd.*; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface2.*; +import ghidra.dbg.error.DebuggerMemoryAccessException; +import ghidra.dbg.error.DebuggerModelAccessException; +import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibility; +import ghidra.dbg.target.TargetObject; +import ghidra.program.model.address.Address; +import ghidra.util.datastruct.WeakValueHashMap; + +public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetMemoryContainer { + + protected final DbgModelTargetProcess process; + + protected final Map memoryRegions = + new WeakValueHashMap<>(); + + public DbgModelTargetMemoryContainerImpl(DbgModelTargetProcess process) { + super(process.getModel(), process, "Memory", "MemoryContainer"); + this.process = process; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + //return CompletableFuture.completedFuture(null); + return listMemory().thenAccept(byName -> { + List sections; + synchronized (this) { + sections = byName.stream().map(this::getTargetMemory).collect(Collectors.toList()); + } + setElements(sections, Map.of(), "Refreshed"); + }); + } + + @Override + public synchronized DbgModelTargetMemoryRegion getTargetMemory(DbgModuleMemory section) { + DbgModelTargetMemoryRegionImpl region = memoryRegions.get(section.getName()); + if (region != null && region.isSame(section)) { + return region; + } + region = new DbgModelTargetMemoryRegionImpl(this, section); + memoryRegions.put(section.getName(), region); + return region; + // NB: The following logic will cause errors in setElements because of key re-use + //return memoryRegions.computeIfAbsent(section.getName(), + // n -> new DbgModelTargetMemoryRegionImpl(this, section)); + } + + public CompletableFuture> listMemory() { + DbgManagerImpl manager = getManager(); + if (manager.isKernelMode()) { + return manager.execute(new DbgListKernelMemoryRegionsCommand(manager)); + } + return manager.execute(new DbgListMemoryRegionsCommand(manager)); + } + + public CompletableFuture readVirtualMemory(Address address, int length) { + DbgManagerImpl manager = getManager(); + if (manager.isWaiting()) { + throw new DebuggerModelAccessException( + "Cannot process command readMemory while engine is waiting for events"); + } + ByteBuffer buf = ByteBuffer.allocate(length); + long offset = address.getOffset(); + return process.getProcess().readMemory(offset, buf).thenApply(set -> { + return readAssist(address, buf, offset, set); + }); + } + + private byte[] readAssist(Address address, ByteBuffer buf, long offset, RangeSet set) { + if (set == null) { + return new byte[0]; + } + Range range = set.rangeContaining(offset); + if (range == null) { + throw new DebuggerMemoryAccessException("Cannot read at " + address); + } + listeners.fire(TargetMemoryListener.class).memoryUpdated(this, address, buf.array()); + return Arrays.copyOf(buf.array(), (int) (range.upperEndpoint() - range.lowerEndpoint())); + } + + public CompletableFuture writeVirtualMemory(Address address, byte[] data) { + DbgManagerImpl manager = getManager(); + if (manager.isWaiting()) { + throw new DebuggerModelAccessException( + "Cannot process command writeMemory while engine is waiting for events"); + } + long offset = address.getOffset(); + return process.getProcess().writeMemory(offset, ByteBuffer.wrap(data)).thenAccept(___ -> { + writeAssist(address, data); + }); + } + + private void writeAssist(Address address, byte[] data) { + listeners.fire(TargetMemoryListener.class).memoryUpdated(this, address, data); + } + + @Override + public CompletableFuture readMemory(Address address, int length) { + DbgManagerImpl manager = getManager(); + if (manager.isWaiting()) { + throw new DebuggerModelAccessException( + "Cannot process command readMemory while engine is waiting for events"); + } + ByteBuffer buf = ByteBuffer.allocate(length); + long offset = address.getOffset(); + if (!manager.isKernelMode() || address.getAddressSpace().getName().equals("ram")) { + return readVirtualMemory(address, length); + } + if (address.getAddressSpace().getName().equals("phys")) { + return manager + .execute( + new DbgReadPhysicalMemoryCommand(manager, offset, buf, buf.remaining())) + .thenApply(set -> { + return readAssist(address, buf, offset, set); + }); + } + if (address.getAddressSpace().getName().equals("ctrl")) { + int processor = 0; + return manager + .execute( + new DbgReadControlCommand(manager, offset, buf, buf.remaining(), processor)) + .thenApply(set -> { + return readAssist(address, buf, offset, set); + }); + } + if (address.getAddressSpace().getName().equals("bus")) { + int busDataType = 0; + int busNumber = 0; + int slotNumber = 0; + return manager + .execute(new DbgReadBusDataCommand(manager, offset, buf, buf.remaining(), + busDataType, busNumber, slotNumber)) + .thenApply(set -> { + return readAssist(address, buf, offset, set); + }); + } + if (address.getAddressSpace().getName().equals("io")) { + int interfaceType = 0; + int busNumber = 0; + int addresSpace = 0; + return manager + .execute(new DbgReadIoCommand(manager, offset, buf, buf.remaining(), + interfaceType, busNumber, addresSpace)) + .thenApply(set -> { + return readAssist(address, buf, offset, set); + }); + } + if (address.getAddressSpace().getName().equals("debug")) { + return manager + .execute(new DbgReadDebuggerDataCommand(manager, offset, buf, buf.remaining())) + .thenApply(set -> { + return readAssist(address, buf, offset, set); + }); + } + return CompletableFuture.completedFuture(new byte[length]); + } + + @Override + public CompletableFuture writeMemory(Address address, byte[] data) { + DbgManagerImpl manager = getManager(); + if (manager.isWaiting()) { + throw new DebuggerModelAccessException( + "Cannot process command writeMemory while engine is waiting for events"); + } + ByteBuffer buf = ByteBuffer.wrap(data); + long offset = address.getOffset(); + if (!manager.isKernelMode() || address.getAddressSpace().getName().equals("ram")) { + return writeVirtualMemory(address, data); + } + if (address.getAddressSpace().getName().equals("phys")) { + return manager + .execute( + new DbgWritePhysicalMemoryCommand(manager, offset, buf, buf.remaining())) + .thenAccept(___ -> { + writeAssist(address, data); + }); + } + if (address.getAddressSpace().getName().equals("ctrl")) { + int processor = 0; + return manager + .execute(new DbgWriteControlCommand(manager, offset, buf, buf.remaining(), + processor)) + .thenAccept(___ -> { + writeAssist(address, data); + }); + } + if (address.getAddressSpace().getName().equals("bus")) { + int busDataType = 0; + int busNumber = 0; + int slotNumber = 0; + return manager + .execute(new DbgWriteBusDataCommand(manager, offset, buf, buf.remaining(), + busDataType, busNumber, slotNumber)) + .thenAccept(___ -> { + writeAssist(address, data); + }); + } + if (address.getAddressSpace().getName().equals("io")) { + int interfaceType = 0; + int busNumber = 0; + int addresSpace = 0; + return manager + .execute(new DbgWriteIoCommand(manager, offset, buf, buf.remaining(), + interfaceType, busNumber, addresSpace)) + .thenAccept(___ -> { + writeAssist(address, data); + }); + } + return CompletableFuture.completedFuture(null); + } + + public void invalidateMemoryCaches() { + listeners.fire.invalidateCacheRequested(this); + } + + @Override + public void onRunning() { + invalidateMemoryCaches(); + setAccessibility(TargetAccessibility.INACCESSIBLE); + } + + @Override + protected void update() { + requestElements(true); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetMemoryRegionImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetMemoryRegionImpl.java new file mode 100644 index 0000000000..96c5edc308 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetMemoryRegionImpl.java @@ -0,0 +1,125 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.dbgeng.manager.DbgModuleMemory; +import agent.dbgeng.model.iface2.DbgModelTargetMemoryContainer; +import agent.dbgeng.model.iface2.DbgModelTargetMemoryRegion; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.*; + +public class DbgModelTargetMemoryRegionImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetMemoryRegion { + + protected static String indexSection(DbgModuleMemory section) { + return section.getName(); + } + + protected static String keySection(DbgModuleMemory section) { + return PathUtils.makeKey(indexSection(section)); + } + + protected final DbgModuleMemory section; + protected final AddressRange range; + protected final List protect; + protected final List allocProtect; + private boolean isRead; + private boolean isWrite; + private boolean isExec; + + public DbgModelTargetMemoryRegionImpl(DbgModelTargetMemoryContainer memory, + DbgModuleMemory region) { + super(memory.getModel(), memory, keySection(region), "Region"); + this.section = region; + + this.range = doGetRange(section); + allocProtect = region.getAllocationProtect(); + String apx = ""; + for (String p : allocProtect) { + apx += p + ":"; + } + if (apx.length() > 1) { + apx = apx.substring(0, apx.length() - 1); + } + protect = region.getProtect(); + String ipx = ""; + for (String p : protect) { + ipx += p + ":"; + } + if (ipx.length() > 1) { + ipx = ipx.substring(0, ipx.length() - 1); + } + isRead = region.isRead(); + isWrite = region.isWrite(); + isExec = region.isExec(); + + this.changeAttributes(List.of(), List.of(), Map.of( // + MEMORY_ATTRIBUTE_NAME, memory, // + RANGE_ATTRIBUTE_NAME, doGetRange(section), // + READABLE_ATTRIBUTE_NAME, isReadable(), // + WRITABLE_ATTRIBUTE_NAME, isWritable(), // + EXECUTABLE_ATTRIBUTE_NAME, isExecutable(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + + AddressSpace space = getModel().getAddressSpace("ram"); + this.changeAttributes(List.of(), List.of(), Map.of( // + "BaseAddress", space.getAddress(region.getVmaStart()), // + "EndAddress", space.getAddress(region.getVmaEnd()), // + "RegionSize", Long.toHexString(region.getVmaEnd() - region.getVmaStart()), // + "AllocationBase", space.getAddress(region.getAllocationBase()), // + "AllocationProtect", apx, // + "Protect", ipx, // + "State", region.getState(), // + "Type", region.getType() // + ), "Initialized"); + } + + protected AddressRange doGetRange(DbgModuleMemory s) { + AddressSpace addressSpace = getModel().getAddressSpace("ram"); + Address min = addressSpace.getAddress(s.getVmaStart()); + Address max = addressSpace.getAddress(s.getVmaEnd() - 1); + return new AddressRangeImpl(min, max); + } + + @Override + public AddressRange getRange() { + return range; + } + + @Override + public boolean isReadable() { + return isRead; + } + + @Override + public boolean isWritable() { + return isWrite; + } + + @Override + public boolean isExecutable() { + return isExec; + } + + public boolean isSame(DbgModuleMemory section) { + return range.equals(doGetRange(section)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleContainerImpl.java new file mode 100644 index 0000000000..90320bbc33 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleContainerImpl.java @@ -0,0 +1,128 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgModule; +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.model.iface2.DbgModelTargetModule; +import agent.dbgeng.model.iface2.DbgModelTargetModuleContainer; +import ghidra.async.AsyncFence; +import ghidra.async.AsyncLazyMap; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetObject; +import ghidra.lifecycle.Internal; +import ghidra.util.Msg; + +public class DbgModelTargetModuleContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetModuleContainer { + // NOTE: -file-list-shared-libraries omits the main module and system-supplied DSO. + + protected final DbgProcess process; + + // TODO: Is it possible to load the same object twice? + protected final AsyncLazyMap modulesByName = + new AsyncLazyMap(new HashMap<>(), this::doGetTargetModule); + + public DbgModelTargetModuleContainerImpl(DbgModelTargetProcessImpl process) { + super(process.getModel(), process, "Modules", "ModuleContainer"); + this.process = process.process; + } + + @Override + @Internal + public void libraryLoaded(String name) { + CompletableFuture module; + synchronized (this) { + /** + * It's not a good idea to remove "stale" entries. If the entry's already present, it's + * probably because several modules were loaded at once, at it has already had its + * sections loaded. Removing it will cause it to load all module sections again! + */ + //modulesByName.remove(name); + module = doGetTargetModule(name); + } + module.thenAccept(mod -> { + changeElements(List.of(), List.of(mod), Map.of(), "Loaded"); + getListeners().fire(TargetEventScopeListener.class) + .event(this, null, TargetEventType.MODULE_LOADED, "Library " + name + " loaded", + List.of(mod)); + }).exceptionally(e -> { + Msg.error(this, "Problem getting module for library load: " + name, e); + return null; + }); + } + + @Override + @Internal + public void libraryUnloaded(String name) { + modulesByName.get(name).thenAccept(mod -> { + getListeners().fire(TargetEventScopeListener.class) + .event(this, null, TargetEventType.MODULE_UNLOADED, + "Library " + name + " unloaded", List.of(mod)); + }); + synchronized (this) { + modulesByName.remove(name); + } + changeElements(List.of(name), List.of(), Map.of(), "Unloaded"); + } + + @Override + public boolean supportsSyntheticModules() { + return false; + } + + @Override + public CompletableFuture> addSyntheticModule(String name) { + throw new UnsupportedOperationException("GDB Does not support synthetic modules"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + List result = new ArrayList<>(); + return process.listModules().thenCompose(byName -> { + AsyncFence fence = new AsyncFence(); + synchronized (this) { + modulesByName.retainKeys(byName.keySet()); + for (Map.Entry ent : byName.entrySet()) { + fence.include(getTargetModule(ent.getKey()).thenAccept(module -> { + result.add(module); + })); + } + } + return fence.ready(); + }).thenAccept(__ -> { + changeElements(List.of(), result, Map.of(), "Refreshed"); + }); + } + + protected CompletableFuture doGetTargetModule(String name) { + // Only get here from libraryLoaded or getElements. The known list should be fresh. + DbgModule module = process.getKnownModules().get(name); + if (module == null) { + return CompletableFuture.completedFuture(null); + } + return CompletableFuture.completedFuture(new DbgModelTargetModuleImpl(this, module)); + //TODO: return module.listSections().thenApply(__ -> new DbgModelTargetModule(this, module)); + } + + @Override + public CompletableFuture getTargetModule(String name) { + return modulesByName.get(name); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleImpl.java new file mode 100644 index 0000000000..a2307d508c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleImpl.java @@ -0,0 +1,88 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.dbgeng.manager.*; +import agent.dbgeng.model.iface2.DbgModelTargetModule; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.*; + +public class DbgModelTargetModuleImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetModule { + protected static String indexModule(DbgModule module) { + return module.getName(); + } + + protected static String keyModule(DbgModule module) { + return PathUtils.makeKey(indexModule(module)); + } + + protected final DbgProcess process; + protected final DbgModule module; + + protected final DbgModelTargetSymbolContainerImpl symbols; + //protected final DbgModelTargetModuleSectionContainerImpl sections; + + public DbgModelTargetModuleImpl(DbgModelTargetModuleContainerImpl modules, DbgModule module) { + super(modules.getModel(), modules, keyModule(module), "Module"); + this.process = modules.process; + this.module = module; + + this.symbols = new DbgModelTargetSymbolContainerImpl(this); + //this.sections = new DbgModelTargetModuleSectionContainerImpl(this); + + AddressSpace space = getModel().getAddressSpace("ram"); + + changeAttributes(List.of(), List.of( // + symbols // + // sections.getName(), sections, // + ), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getIndex(), // + MODULE_NAME_ATTRIBUTE_NAME, module.getName(), // + "BaseAddress", space.getAddress(module.getKnownBase()), // + "ImageName", module.getImageName(), // + "TimeStamp", module.getTimeStamp(), // + "Len", Integer.toHexString(module.getSize()) // + ), "Initialized"); + + DbgModuleSection section = new DbgModuleSection(module); + Address min = space.getAddress(section.getStart()); + // Ghidra ranges are not inclusive at the end. + Address max = space.getAddress(section.getStart() + section.getSize() - 1); + AddressRange range = new AddressRangeImpl(min, max); + + changeAttributes(List.of(), List.of(), Map.of( // + RANGE_ATTRIBUTE_NAME, range // + ), "Initialized"); + } + + protected Address doGetBase() { + return getModel().getAddressSpace("ram").getAddress(module.getKnownBase()); + } + + @Override + public DbgModule getDbgModule() { + return module; + } + + public DbgProcess getProcess() { + return process; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleSectionContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleSectionContainerImpl.java new file mode 100644 index 0000000000..8ae5b61e32 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleSectionContainerImpl.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgModule; +import agent.dbgeng.manager.DbgModuleSection; +import agent.dbgeng.model.iface2.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public class DbgModelTargetModuleSectionContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetModuleSectionContainer { + + protected final DbgModule module; + + protected final Map sectionsByStart = + new WeakValueHashMap<>(); + + public DbgModelTargetModuleSectionContainerImpl(DbgModelTargetModule module) { + super(module.getModel(), module, "Sections", "ModuleSections"); + this.module = module.getDbgModule(); + + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return CompletableFuture.completedFuture(null); + /* + return module.listSections().thenAccept(byStart -> { + List sections; + synchronized (this) { + sections = byStart.values() + .stream() + .map(this::getModuleSection) + .collect(Collectors.toList()); + setElements(sections, "Refreshed"); + } + }); + */ + } + + protected synchronized DbgModelTargetModuleSection getModuleSection(DbgModuleSection section) { + return sectionsByStart.computeIfAbsent(section.getStart(), + s -> new DbgModelTargetModuleSectionImpl(this, section)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleSectionImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleSectionImpl.java new file mode 100644 index 0000000000..8a3421fd14 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetModuleSectionImpl.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.dbgeng.manager.DbgModuleSection; +import agent.dbgeng.model.iface2.DbgModelTargetModuleSection; +import ghidra.program.model.address.*; + +public class DbgModelTargetModuleSectionImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetModuleSection { + protected static final String OBJFILE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "objfile"; + + protected AddressRangeImpl range; + + public DbgModelTargetModuleSectionImpl(DbgModelTargetModuleSectionContainerImpl sections, + DbgModuleSection section) { + super(sections.getModel(), sections, section.getName(), "Section"); + + AddressSpace space = getModel().getAddressSpace("ram"); + Address min = space.getAddress(section.getStart()); + // Ghidra ranges are not inclusive at the end. + Address max = space.getAddress(section.getStart() + section.getSize() - 1); + range = new AddressRangeImpl(min, max); + + changeAttributes(List.of(), List.of(), Map.of( // + MODULE_ATTRIBUTE_NAME, sections.getImplParent(), // + RANGE_ATTRIBUTE_NAME, range, // + DISPLAY_ATTRIBUTE_NAME, section.getName(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public AddressRange getRange() { + return range; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetObjectImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetObjectImpl.java new file mode 100644 index 0000000000..840736690e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetObjectImpl.java @@ -0,0 +1,188 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.*; +import agent.dbgeng.model.AbstractDbgModel; +import agent.dbgeng.model.iface1.DbgModelTargetAccessConditioned; +import agent.dbgeng.model.iface1.DbgModelTargetExecutionStateful; +import agent.dbgeng.model.iface2.*; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibility; +import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibilityListener; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; + +public class DbgModelTargetObjectImpl extends DefaultTargetObject + implements DbgModelTargetObject { + + protected TargetAccessibility accessibility = TargetAccessibility.ACCESSIBLE; + protected final DbgStateListener accessListener = this::checkExited; + private boolean modified; + + public DbgModelTargetObjectImpl(AbstractDbgModel impl, TargetObject parent, String name, + String typeHint) { + super(impl, parent, name, typeHint); + getManager().addStateListener(accessListener); + } + + public void setAttribute(String key, String value) { + changeAttributes(List.of(), List.of(), Map.of( // + key, value), "Initialized"); + } + + public void setAccessibility(TargetAccessibility accessibility) { + synchronized (attributes) { + if (this.accessibility == accessibility) { + return; + } + this.accessibility = accessibility; + } + if (this instanceof DbgModelTargetAccessConditioned) { + changeAttributes(List.of(), List.of(), Map.of( // + TargetAccessConditioned.ACCESSIBLE_ATTRIBUTE_NAME, + accessibility == TargetAccessibility.ACCESSIBLE // + ), "Accessibility changed"); + DbgModelTargetAccessConditioned accessConditioned = + (DbgModelTargetAccessConditioned) this; + listeners.fire(TargetAccessibilityListener.class) + .accessibilityChanged(accessConditioned, accessibility); + } + } + + @Override + public AbstractDbgModel getModel() { + return (AbstractDbgModel) model; + } + + public void onRunning() { + setAccessibility(TargetAccessibility.INACCESSIBLE); + } + + public void onStopped() { + setAccessibility(TargetAccessibility.ACCESSIBLE); + update(); + } + + public void onExit() { + setAccessibility(TargetAccessibility.ACCESSIBLE); + } + + protected void update() { + Map existingAttributes = getCachedAttributes(); + Boolean autoupdate = (Boolean) existingAttributes.get("autoupdate"); + if (autoupdate != null && autoupdate) { + requestAttributes(true); + requestElements(true); + } + } + + protected void checkExited(DbgState state, DbgCause cause) { + TargetExecutionState exec = TargetExecutionState.INACTIVE; + switch (state) { + case NOT_STARTED: { + exec = TargetExecutionState.INACTIVE; + break; + } + case STARTING: { + exec = TargetExecutionState.ALIVE; + break; + } + case RUNNING: { + exec = TargetExecutionState.RUNNING; + resetModified(); + onRunning(); + break; + } + case STOPPED: { + exec = TargetExecutionState.STOPPED; + onStopped(); + break; + } + case EXIT: { + exec = TargetExecutionState.TERMINATED; + onExit(); + break; + } + } + if (this instanceof DbgModelTargetExecutionStateful) { + DbgModelTargetExecutionStateful stateful = (DbgModelTargetExecutionStateful) this; + stateful.setExecutionState(exec, "Refreshed"); + } + } + + @Override + public CompletableFuture> requestNativeAttributes() { + throw new AssertionError(); // shouldn't ever be here + } + + @Override + public DbgModelTargetSession getParentSession() { + DbgModelTargetObject test = (DbgModelTargetObject) parent; + while (test != null && !(test instanceof DbgModelTargetSession)) { + test = (DbgModelTargetObject) test.getImplParent(); + } + return test == null ? null : (DbgModelTargetSession) test; + } + + @Override + public DbgModelTargetProcess getParentProcess() { + DbgModelTargetObject test = (DbgModelTargetObject) parent; + while (test != null && !(test instanceof TargetProcess)) { + test = (DbgModelTargetObject) test.getImplParent(); + } + return test == null ? null : (DbgModelTargetProcess) test; + } + + @Override + public DbgModelTargetThread getParentThread() { + DbgModelTargetObject test = (DbgModelTargetObject) parent; + while (test != null && !(test instanceof TargetThread)) { + test = (DbgModelTargetObject) test.getImplParent(); + } + return test == null ? null : (DbgModelTargetThread) test; + } + + @Override + public void setModified(Map map, boolean b) { + if (modified) { + map.put(MODIFIED_ATTRIBUTE_NAME, modified); + listeners.fire.displayChanged(this, getDisplay()); + } + } + + @Override + public void setModified(boolean modified) { + if (modified) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, modified // + ), "Refreshed"); + listeners.fire.displayChanged(this, getDisplay()); + } + } + + @Override + public void resetModified() { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, false // + ), "Refreshed"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessAttachConnectorImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessAttachConnectorImpl.java new file mode 100644 index 0000000000..9ee417dc12 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessAttachConnectorImpl.java @@ -0,0 +1,81 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgProcess; +import agent.dbgeng.manager.impl.DbgProcessImpl; +import agent.dbgeng.model.iface2.DbgModelTargetConnector; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; + +public class DbgModelTargetProcessAttachConnectorImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetConnector { + + protected final DbgModelTargetConnectorContainerImpl connectors; + protected final TargetParameterMap paramDescs; + + public DbgModelTargetProcessAttachConnectorImpl(DbgModelTargetConnectorContainerImpl connectors, + String name) { + super(connectors.getModel(), connectors, name, name); + this.connectors = connectors; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public CompletableFuture select() { + connectors.setDefaultConnector(this); + return CompletableFuture.completedFuture(null); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription param = ParameterDescription.create(String.class, "Pid", true, + "", "Pid", "process id for the target process"); + map.put("Pid", param); + return map; + } + + @Override + public TargetParameterMap getParameters() { + return TargetMethod.getParameters(this); + } + + @Override + public CompletableFuture launch(Map args) { + String pidstr = (String) args.get("Pid"); + int pid = Integer.decode(pidstr); + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + DbgProcess process = new DbgProcessImpl(getManager()); + process.attach(pid).handle(seq::nextIgnore); + }).finish().exceptionally((exc) -> { + throw new DebuggerUserException("Launch failed for " + args); + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessContainerImpl.java new file mode 100644 index 0000000000..83342b1cda --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessContainerImpl.java @@ -0,0 +1,135 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.*; +import agent.dbgeng.model.iface2.*; +import ghidra.dbg.target.TargetObject; +import ghidra.util.datastruct.WeakValueHashMap; + +public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetProcessContainer { + + protected final Map processesById = + new WeakValueHashMap<>(); + + public DbgModelTargetProcessContainerImpl(DbgModelTargetSession session) { + super(session.getModel(), session, "Processes", "ProcessContainer"); + + getManager().addEventsListener(this); + } + + @Override + public void processAdded(DbgProcess proc, DbgCause cause) { + DbgModelTargetProcess process = getTargetProcess(proc); + changeElements(List.of(), List.of(process), Map.of(), "Added"); + process.processStarted(proc.getPid()); + getListeners().fire(TargetEventScopeListener.class) + .event(this, null, TargetEventType.PROCESS_CREATED, "Process " + proc.getId() + + " started " + "notepad.exe" + " pid=" + proc.getPid(), List.of(process)); + } + + @Override + public void processStarted(DbgProcess proc, DbgCause cause) { + DbgModelTargetProcess process = getTargetProcess(proc); + process.processStarted(proc.getPid()); + } + + @Override + public void processExited(DbgProcess proc, DbgCause cause) { + DbgModelTargetProcess process = getTargetProcess(proc); + process.processExited(proc.getExitCode()); + getListeners().fire(TargetEventScopeListener.class) + .event(this, null, TargetEventType.PROCESS_EXITED, + "Process " + proc.getId() + " exited code=" + proc.getExitCode(), + List.of(process)); + } + + @Override + public void processRemoved(DebugProcessId processId, DbgCause cause) { + synchronized (this) { + processesById.remove(processId); + } + changeElements(List.of( // + DbgModelTargetProcessImpl.indexProcess(processId) // + ), List.of(), Map.of(), "Removed"); + } + + @Override + public void threadCreated(DbgThread thread, DbgCause cause) { + DbgModelTargetProcess process = getTargetProcess(thread.getProcess()); + process.getThreads().threadCreated(thread); + } + + @Override + public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause, + DbgReason reason) { + DbgModelTargetProcess process = getTargetProcess(thread.getProcess()); + process.threadStateChanged(thread, state, cause, reason); + } + + @Override + public void threadExited(DebugThreadId threadId, DbgProcess proc, DbgCause cause) { + DbgModelTargetProcess process = getTargetProcess(proc); + process.getThreads().threadExited(threadId); + } + + @Override + public void moduleLoaded(DbgProcess proc, String name, DbgCause cause) { + DbgModelTargetProcess process = getTargetProcess(proc); + process.getModules().libraryLoaded(name); + } + + @Override + public void moduleUnloaded(DbgProcess proc, String name, DbgCause cause) { + DbgModelTargetProcess process = getTargetProcess(proc); + process.getModules().libraryUnloaded(name); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listProcesses().thenAccept(byIID -> { + List processes; + synchronized (this) { + processes = byIID.values() + .stream() + .map(this::getTargetProcess) + .collect(Collectors.toList()); + } + setElements(processes, Map.of(), "Refreshed"); + }); + } + + @Override + public synchronized DbgModelTargetProcess getTargetProcess(DebugProcessId id) { + return processesById.computeIfAbsent(id, + i -> new DbgModelTargetProcessImpl(this, getManager().getKnownProcesses().get(id))); + } + + @Override + public synchronized DbgModelTargetProcess getTargetProcess(DbgProcess process) { + return processesById.computeIfAbsent(process.getId(), + i -> new DbgModelTargetProcessImpl(this, process)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessImpl.java new file mode 100644 index 0000000000..f6b4ad33c4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessImpl.java @@ -0,0 +1,225 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.cmd.DbgProcessSelectCommand; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface1.DbgModelTargetFocusScope; +import agent.dbgeng.model.iface2.*; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.target.*; +import ghidra.dbg.util.PathUtils; + +public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetProcess { + + public static final String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid"; + public static final String EXIT_CODE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "exit_code"; + + protected static String indexProcess(DebugProcessId debugProcessId) { + return PathUtils.makeIndex(debugProcessId.id); + } + + protected static String indexProcess(DbgProcess process) { + return indexProcess(process.getId()); + } + + protected static String keyProcess(DbgProcess process) { + return PathUtils.makeKey(indexProcess(process)); + } + + protected final DbgProcess process; + + protected final DbgModelTargetDebugContainer debug; + protected final DbgModelTargetMemoryContainer memory; + protected final DbgModelTargetModuleContainer modules; + protected final DbgModelTargetThreadContainer threads; + // Note: not sure section info is available from the dbgeng + //protected final DbgModelTargetProcessSectionContainer sections; + + public DbgModelTargetProcessImpl(DbgModelTargetProcessContainer processes, DbgProcess process) { + super(processes.getModel(), processes, keyProcess(process), "Process"); + this.process = process; + + this.debug = new DbgModelTargetDebugContainerImpl(this); + this.memory = new DbgModelTargetMemoryContainerImpl(this); + this.modules = new DbgModelTargetModuleContainerImpl(this); + //this.sections = new DbgModelTargetProcessSectionContainerImpl(this); + this.threads = new DbgModelTargetThreadContainerImpl(this); + + changeAttributes(List.of(), List.of( // + debug, // + memory, // + modules, // + //sections, // + threads // + ), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, PARAMETERS // + ), "Initialized"); + setExecutionState(TargetExecutionState.ALIVE, "Initialized"); + + getManager().addEventsListener(this); + } + + @Override + public String getDisplay() { + if (getManager().isKernelMode()) { + return "[kernel]"; + } + return "[" + process.getId().id + ":0x" + Long.toHexString(process.getPid()) + "]"; + } + + @Override + public void processSelected(DbgProcess eventProcess, DbgCause cause) { + if (eventProcess.equals(process)) { + AtomicReference> scope = new AtomicReference<>(); + AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + DebugModelConventions.findSuitable(DbgModelTargetFocusScope.class, this) + .handle(seq::next); + }, scope).then(seq -> { + scope.get().setFocus(this); + }).finish(); + } + } + + @Override + public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause, + DbgReason reason) { + TargetExecutionState targetState = convertState(state); + setExecutionState(targetState, "ThreadStateChanged"); + } + + @Override + public CompletableFuture launch(List args) { + return DbgModelImplUtils.launch(getModel(), process, args); + } + + @Override + public CompletableFuture resume() { + return process.cont(); + } + + @Override + public CompletableFuture kill() { + return process.kill(); + } + + @Override + public CompletableFuture attach(TypedTargetObjectRef> ref) { + getModel().assertMine(TargetObjectRef.class, ref); + // NOTE: Get the object and type check it myself. + // The typed ref could have been unsafely cast + List tPath = ref.getPath(); + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + getModel().fetchModelObject(tPath).handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + TargetAttachable attachable = + DebuggerObjectModel.requireIface(TargetAttachable.class, obj, tPath); + process.reattach(attachable); + }).finish(); + } + + @Override + public CompletableFuture attach(long pid) { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + process.attach(pid).handle(seq::nextIgnore); + }).finish(); + } + + @Override + public CompletableFuture detach() { + return process.detach(); + } + + @Override + public CompletableFuture delete() { + return process.remove(); + } + + @Override + public CompletableFuture step(TargetStepKind kind) { + DbgThread thread = getManager().getCurrentThread(); + switch (kind) { + case SKIP: + throw new UnsupportedOperationException(kind.name()); + case ADVANCE: // Why no exec-advance in dbgeng? + return thread.console("advance"); + default: + return thread.step(convertToDbg(kind)); + } + } + + @Override + public void processStarted(Long pid) { + if (pid != null) { + changeAttributes(List.of(), List.of(), Map.of( // + PID_ATTRIBUTE_NAME, pid, // + DISPLAY_ATTRIBUTE_NAME, "[0x" + Long.toHexString(pid) + "]" // + ), "Started"); + } + setExecutionState(TargetExecutionState.ALIVE, "Started"); + } + + @Override + public void processExited(Long exitCode) { + if (exitCode != null) { + changeAttributes(List.of(), List.of(), Map.of( // + EXIT_CODE_ATTRIBUTE_NAME, exitCode // + ), "Exited"); + } + setExecutionState(TargetExecutionState.TERMINATED, "Exited"); + } + + @Override + public CompletableFuture select() { + DbgManagerImpl manager = getManager(); + return manager.execute(new DbgProcessSelectCommand(manager, process)); + } + + @Override + public DbgModelTargetThreadContainer getThreads() { + return threads; + } + + @Override + public DbgModelTargetModuleContainer getModules() { + return modules; + } + + @Override + public DbgProcess getProcess() { + return process; + } + + @Override + public TargetAccessibility getAccessibility() { + return accessibility; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessLaunchConnectorImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessLaunchConnectorImpl.java new file mode 100644 index 0000000000..638631de0e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetProcessLaunchConnectorImpl.java @@ -0,0 +1,81 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.iface2.DbgModelTargetConnector; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; + +public class DbgModelTargetProcessLaunchConnectorImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetConnector { + + protected final DbgModelTargetConnectorContainerImpl connectors; + protected final TargetParameterMap paramDescs; + + public DbgModelTargetProcessLaunchConnectorImpl(DbgModelTargetConnectorContainerImpl connectors, + String name) { + super(connectors.getModel(), connectors, name, name); + this.connectors = connectors; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public CompletableFuture select() { + connectors.setDefaultConnector(this); + return CompletableFuture.completedFuture(null); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription param = ParameterDescription.create(String.class, + "CommandLine", true, "", "Cmd", "executable to be launched"); + map.put("Command line", param); + return map; + } + + @Override + public TargetParameterMap getParameters() { + return TargetMethod.getParameters(this); + } + + @Override + public CompletableFuture launch(Map args) { + return launch( + CmdLineParser.tokenize(TargetCmdLineLauncher.PARAMETER_CMDLINE_ARGS.get(args))); + } + + public CompletableFuture launch(List args) { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + getManager().launch(args).handle(seq::nextIgnore); + }).finish().exceptionally((exc) -> { + throw new DebuggerUserException("Launch failed for " + args); + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRegisterContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRegisterContainerImpl.java new file mode 100644 index 0000000000..f66ad20f15 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRegisterContainerImpl.java @@ -0,0 +1,172 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.math.BigInteger; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.dbgeng.manager.DbgThread; +import agent.dbgeng.manager.impl.DbgRegister; +import agent.dbgeng.manager.impl.DbgRegisterSet; +import agent.dbgeng.model.iface2.*; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.error.DebuggerRegisterAccessException; +import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibility; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetRegisterBank; +import ghidra.dbg.util.ConversionUtils; + +public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetRegisterContainer { + + protected final DbgThread thread; + + protected final Map registersByNumber = new HashMap<>(); + protected final Map registersByName = new HashMap<>(); + + public DbgModelTargetRegisterContainerImpl(DbgModelTargetThread thread) { + super(thread.getModel(), thread, "Registers", "RegisterContainer"); + this.thread = thread.getThread(); + + changeAttributes(List.of(), List.of(), Map.of( // + TargetRegisterBank.DESCRIPTIONS_ATTRIBUTE_NAME, this // + ), "Initialized"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return thread.listRegisters().thenAccept(regs -> { + if (regs.size() != registersByNumber.size()) { + registersByNumber.clear(); + registersByName.clear(); + } + List registers; + synchronized (this) { + registers = regs.stream().map(this::getTargetRegister).collect(Collectors.toList()); + } + setElements(registers, Map.of(), "Refreshed"); + if (!getCachedElements().isEmpty()) { + readRegistersNamed(getCachedElements().keySet()); + } + }); + } + + @Override + public synchronized DbgModelTargetRegister getTargetRegister(DbgRegister register) { + DbgModelTargetRegister reg = registersByNumber.computeIfAbsent(register.getNumber(), + n -> new DbgModelTargetRegisterImpl(this, register)); + registersByName.put(register.getName(), reg); + return reg; + } + + @Override + public CompletableFuture> readRegistersNamed( + Collection names) { + return AsyncUtils.sequence(TypeSpec.map(String.class, byte[].class)).then(seq -> { + thread.listRegisters().handle(seq::next); + }, TypeSpec.cls(DbgRegisterSet.class)).then((regs, seq) -> { + if (regs.size() != registersByNumber.size() || getCachedElements().isEmpty()) { + requestElements(true).handle(seq::next); + } + seq.next(null, null); + }).then(seq -> { + Set toRead = new LinkedHashSet<>(); + for (String regname : names) { + DbgModelTargetRegister reg = registersByName.get(regname); + if (reg != null) { + DbgRegister register = reg.getRegister(); + //if (register.isBaseRegister()) { + toRead.add(register); + //} + //throw new DebuggerRegisterAccessException("No such register: " + regname); + } + } + thread.readRegisters(toRead).handle(seq::next); + }, TypeSpec.map(DbgRegister.class, BigInteger.class)).then((vals, seq) -> { + Map result = new LinkedHashMap<>(); + for (DbgRegister dbgReg : vals.keySet()) { + DbgModelTargetRegister reg = registersByNumber.get(dbgReg.getNumber()); + String oldval = (String) reg.getCachedAttributes().get(VALUE_ATTRIBUTE_NAME); + BigInteger value = vals.get(dbgReg); + byte[] bytes = ConversionUtils.bigIntegerToBytes(dbgReg.getSize(), value); + result.put(dbgReg.getName(), bytes); + reg.changeAttributes(List.of(), Map.of( // + VALUE_ATTRIBUTE_NAME, value.toString(16) // + ), "Refreshed"); + if (value.longValue() != 0) { + String newval = reg.getName() + " : " + value.toString(16); + reg.changeAttributes(List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, newval // + ), "Refreshed"); + reg.setModified(!value.toString(16).equals(oldval)); + } + } + listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, result); + seq.exit(result); + }).finish(); + } + + @Override + public CompletableFuture writeRegistersNamed(Map values) { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + thread.listRegisters().handle(seq::next); + }, TypeSpec.cls(DbgRegisterSet.class)).then((regs, seq) -> { + fetchElements().handle(seq::nextIgnore); + }).then(seq -> { + Map regs = getCachedElements(); + Map toWrite = new LinkedHashMap<>(); + for (Map.Entry ent : values.entrySet()) { + String regname = ent.getKey(); + DbgModelTargetRegister reg = (DbgModelTargetRegister) regs.get(regname); + if (reg == null) { + throw new DebuggerRegisterAccessException("No such register: " + regname); + } + BigInteger val = new BigInteger(1, ent.getValue()); + toWrite.put(reg.getRegister(), val); + } + thread.writeRegisters(toWrite).handle(seq::next); + // TODO: Should probably filter only effective and normalized writes in the callback + }).then(seq -> { + listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, values); + seq.exit(); + }).finish(); + } + + /* + public void invalidateRegisterCaches() { + listeners.fire.invalidateCacheRequested(this); + } + */ + + @Override + public void onRunning() { + // NB: We don't want to do this apparently + //invalidateRegisterCaches(); + setAccessibility(TargetAccessibility.INACCESSIBLE); + } + + @Override + public void onStopped() { + setAccessibility(TargetAccessibility.ACCESSIBLE); + if (thread.equals(getManager().getEventThread())) { + readRegistersNamed(getCachedElements().keySet()); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRegisterImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRegisterImpl.java new file mode 100644 index 0000000000..4bf35ec8ea --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRegisterImpl.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.dbgeng.manager.impl.DbgRegister; +import agent.dbgeng.model.iface2.DbgModelTargetRegister; +import agent.dbgeng.model.iface2.DbgModelTargetRegisterContainer; +import ghidra.dbg.util.PathUtils; + +public class DbgModelTargetRegisterImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetRegister { + + protected static String indexRegister(DbgRegister register) { + String name = register.getName(); + if ("".equals(name)) { + return "UNNAMED," + register.getNumber(); + } + return name; + } + + protected static String keyRegister(DbgRegister register) { + return PathUtils.makeKey(indexRegister(register)); + } + + protected final DbgModelTargetRegisterContainer registers; + protected final DbgRegister register; + + protected final int bitLength; + + public DbgModelTargetRegisterImpl(DbgModelTargetRegisterContainer registers, + DbgRegister register) { + super(registers.getModel(), registers, keyRegister(register), "Register"); + this.registers = registers; + this.register = register; + + this.bitLength = register.getSize() * 8; + + changeAttributes(List.of(), List.of(), Map.of( // + CONTAINER_ATTRIBUTE_NAME, registers, // + LENGTH_ATTRIBUTE_NAME, bitLength, // + DISPLAY_ATTRIBUTE_NAME, "[" + register.getName() + "]" // + ), "Initialized"); + } + + @Override + public int getBitLength() { + return bitLength; + } + + @Override + public DbgRegister getRegister() { + return register; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRootImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRootImpl.java new file mode 100644 index 0000000000..875e7e0f1b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetRootImpl.java @@ -0,0 +1,132 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.impl.DbgProcessImpl; +import agent.dbgeng.model.iface1.DbgModelSelectableObject; +import agent.dbgeng.model.iface2.DbgModelTargetConnector; +import agent.dbgeng.model.iface2.DbgModelTargetRoot; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.*; + +public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot + implements DbgModelTargetRoot { + + protected final DbgModelTargetAvailableContainerImpl available; + protected final DbgModelTargetConnectorContainerImpl connectors; + protected final DbgModelTargetSessionContainerImpl sessions; + + protected String debugger = "kd"; // Used by DbgModelTargetEnvironment + + protected DbgModelSelectableObject focus; + + public DbgModelTargetRootImpl(DbgModelImpl impl) { + super(impl, "Debugger"); + + this.available = new DbgModelTargetAvailableContainerImpl(this); + this.connectors = new DbgModelTargetConnectorContainerImpl(this); + this.sessions = new DbgModelTargetSessionContainerImpl(this); + + DbgModelTargetConnector defaultConnector = connectors.getDefaultConnector(); + changeAttributes(List.of(), List.of( // + available, // + connectors, // + sessions // + ), Map.of( // + DISPLAY_ATTRIBUTE_NAME, "Debugger", // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, defaultConnector.getParameters() // + // ARCH_ATTRIBUTE_NAME, "x86_64", // + // DEBUGGER_ATTRIBUTE_NAME, "dbgeng", // + // OS_ATTRIBUTE_NAME, "Windows", // + ), "Initialized"); + impl.getManager().addEventsListener(this); + } + + @Override + public DbgModelSelectableObject getFocus() { + return focus; + } + + @Override + public void setDefaultConnector(DbgModelTargetConnector defaultConnector) { + changeAttributes(List.of(), List.of(), + Map.of(TargetMethod.PARAMETERS_ATTRIBUTE_NAME, defaultConnector.getParameters()), + "Default connector changed"); + } + + @Override + public boolean setFocus(DbgModelSelectableObject sel) { + boolean doFire; + synchronized (this) { + doFire = !Objects.equals(this.focus, sel); + this.focus = sel; + } + if (doFire) { + changeAttributes(List.of(), List.of(), Map.of( // + TargetFocusScope.FOCUS_ATTRIBUTE_NAME, focus // + ), "Focus changed"); + listeners.fire(TargetFocusScopeListener.class).focusChanged(this, sel); + } + return doFire; + } + + @Override + public CompletableFuture launch(Map args) { + DbgModelTargetConnector targetConnector = connectors.getDefaultConnector(); + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + targetConnector.launch(args).handle(seq::nextIgnore); + //getManager().launch(args).handle(seq::nextIgnore); + }).finish().exceptionally((exc) -> { + throw new DebuggerUserException("Launch failed for " + args); + }); + } + + @Override + public CompletableFuture attach(long pid) { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + DbgProcess process = new DbgProcessImpl(getManager()); + process.attach(pid).handle(seq::nextIgnore); + }).finish(); + } + + @Override + public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause, + DbgReason reason) { + DbgProcess process = thread.getProcess(); + changeAttributes(List.of(), List.of(), Map.of( // + TargetEventScope.EVENT_PROCESS_ATTRIBUTE_NAME, Long.toHexString(process.getPid()), // + TargetEventScope.EVENT_THREAD_ATTRIBUTE_NAME, Long.toHexString(thread.getTid()) // + ), reason.desc()); + } + + //@Override + public void refresh() { + // TODO ??? + System.err.println("root:refresh"); + } + + @Override + public TargetAccessibility getAccessibility() { + return accessibility; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionAttributesImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionAttributesImpl.java new file mode 100644 index 0000000000..7d4eb932c7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionAttributesImpl.java @@ -0,0 +1,75 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.iface2.DbgModelTargetSession; +import agent.dbgeng.model.iface2.DbgModelTargetSessionAttributes; + +public class DbgModelTargetSessionAttributesImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetSessionAttributes { + + protected final DbgModelTargetSessionAttributesMachineImpl machineAttributes; + + public DbgModelTargetSessionAttributesImpl(DbgModelTargetSession session) { + super(session.getModel(), session, "Attributes", "SessionAttributes"); + + this.machineAttributes = new DbgModelTargetSessionAttributesMachineImpl(this); + + changeAttributes(List.of(), List.of( // + machineAttributes // + ), Map.of( // + ARCH_ATTRIBUTE_NAME, "x86_64", // + DEBUGGER_ATTRIBUTE_NAME, "dbgeng", // + OS_ATTRIBUTE_NAME, "Windows", // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + + getManager().addEventsListener(this); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return CompletableFuture.completedFuture(null); + } + + /* + @Override + public String getArchitecture() { + return machineAttributes.getTypedAttributeNowByName(ARCH_ATTRIBUTE_NAME, String.class, ""); + } + + @Override + public String getDebugger() { + return machineAttributes.getTypedAttributeNowByName(DEBUGGER_ATTRIBUTE_NAME, String.class, + ""); + } + + @Override + public String getOperatingSystem() { + return machineAttributes.getTypedAttributeNowByName(OS_ATTRIBUTE_NAME, String.class, ""); + } + */ + + @Override + public void refresh() { + machineAttributes.refresh(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionAttributesMachineImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionAttributesMachineImpl.java new file mode 100644 index 0000000000..3675afc630 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionAttributesMachineImpl.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +import agent.dbgeng.dbgeng.DebugControl; +import agent.dbgeng.impl.dbgeng.client.DebugClientInternal; +import agent.dbgeng.impl.dbgeng.client.DebugClientInternal.DebugClass; +import agent.dbgeng.jna.dbgeng.WinNTExtra.Machine; +import agent.dbgeng.manager.*; +import agent.dbgeng.model.iface2.DbgModelTargetSessionAttributes; +import agent.dbgeng.model.iface2.DbgModelTargetSessionAttributesMachine; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; + +public class DbgModelTargetSessionAttributesMachineImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetSessionAttributesMachine { + + String ARCH_ATTRIBUTE_NAME = "Arch"; + String DEBUGGER_ATTRIBUTE_NAME = "Debugger"; + String OS_ATTRIBUTE_NAME = "OS"; + + public DbgModelTargetSessionAttributesMachineImpl(DbgModelTargetSessionAttributes attributes) { + super(attributes.getModel(), attributes, "Machine", "SessionMachineAttributes"); + + changeAttributes(List.of(), List.of(), Map.of( // + ARCH_ATTRIBUTE_NAME, "x86_64", // + DEBUGGER_ATTRIBUTE_NAME, "dbgeng", // + OS_ATTRIBUTE_NAME, "Windows" // + ), "Initialized"); + + getManager().addEventsListener(this); + } + + @Override + public void sessionAdded(DbgSession session, DbgCause cause) { + refresh(); + } + + @Override + public void processAdded(DbgProcess process, DbgCause cause) { + refresh(); + } + + public void refresh() { + DebugControl control = getManager().getControl(); + int processorType = control.getActualProcessorType(); + if (processorType < 0) { + return; + } + Machine machine = Machine.getByNumber(processorType); + int debuggeeType = control.getDebuggeeType(); + DebugClass debugClass = DebugClientInternal.DebugClass.values()[debuggeeType]; + changeAttributes(List.of(), List.of(), Map.of( // + ARCH_ATTRIBUTE_NAME, machine.description, // + "Mode", debugClass.toString() // + ), "Refreshed"); + + AtomicReference capture = new AtomicReference<>(); + AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + getManager().consoleCapture("vertarget").handle(seq::next); + }, capture).then(seq -> { + changeAttributes(List.of(), List.of(), Map.of( // + OS_ATTRIBUTE_NAME, capture.get()), "Refreshed"); + }).finish(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionContainerImpl.java new file mode 100644 index 0000000000..995aff7ac3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionContainerImpl.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugSessionId; +import agent.dbgeng.manager.DbgCause; +import agent.dbgeng.manager.DbgSession; +import agent.dbgeng.model.iface2.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public class DbgModelTargetSessionContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetSessionContainer { + + protected final Map sessionsById = + new WeakValueHashMap<>(); + + public DbgModelTargetSessionContainerImpl(DbgModelTargetRoot root) { + super(root.getModel(), root, "Sessions", "SessionContainer"); + + getManager().addEventsListener(this); + } + + @Override + public void sessionAdded(DbgSession sess, DbgCause cause) { + DbgModelTargetSession session = getTargetSession(sess); + changeElements(List.of(), List.of(session), Map.of(), "Added"); + } + + @Override + public void sessionRemoved(DebugSessionId sessionId, DbgCause cause) { + synchronized (this) { + sessionsById.remove(sessionId); + } + changeElements(List.of( // + DbgModelTargetSessionImpl.indexSession(sessionId) // + ), List.of(), Map.of(), "Removed"); + } + + @Override + public synchronized DbgModelTargetSession getTargetSession(DbgSession session) { + DebugSessionId id = session.getId(); + return sessionsById.computeIfAbsent(id, i -> new DbgModelTargetSessionImpl(this, session)); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return CompletableFuture.completedFuture(null); + /* + DbgManagerImpl manager = getManager(); + if (manager.checkAccessProhibited()) { + return CompletableFuture.completedFuture(this.elementsView); + } + return manager.listSessions().thenApply(byIID -> { + List sessions; + synchronized (this) { + sessions = byIID.values() + .stream() + .map(this::getTargetSession) + .collect(Collectors.toList()); + } + setElements(sessions, "Refreshed"); + return this.elementsView; + }); + */ + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionImpl.java new file mode 100644 index 0000000000..e0bcb59b3d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionImpl.java @@ -0,0 +1,97 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugSessionId; +import agent.dbgeng.manager.*; +import agent.dbgeng.model.iface1.DbgModelSelectableObject; +import agent.dbgeng.model.iface2.DbgModelTargetProcessContainer; +import agent.dbgeng.model.iface2.DbgModelTargetSession; +import ghidra.dbg.util.PathUtils; + +public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetSession { + + protected static final String DBG_PROMPT = "(kd)"; + // NB: This should almost certainly always be implemented by the root of the object tree + + protected static String indexSession(DebugSessionId debugSystemId) { + return PathUtils.makeIndex(debugSystemId.id); + } + + protected static String indexSession(DbgSession session) { + return indexSession(session.getId()); + } + + protected static String keySession(DbgSession session) { + return PathUtils.makeKey(indexSession(session)); + } + + protected final DbgModelTargetSessionAttributesImpl attributes; + protected final DbgModelTargetProcessContainerImpl processes; + private DbgModelSelectableObject focus; + + protected String debugger = "kd"; // Used by DbgModelTargetEnvironment + + public DbgModelTargetSessionImpl(DbgModelTargetSessionContainerImpl sessions, + DbgSession session) { + super(sessions.getModel(), sessions, keySession(session), "Session"); + + this.attributes = new DbgModelTargetSessionAttributesImpl(this); + this.processes = new DbgModelTargetProcessContainerImpl(this); + + changeAttributes(List.of(), List.of( // + attributes, // + processes // + ), Map.of( // + PROMPT_ATTRIBUTE_NAME, DBG_PROMPT, // + //STATE_ATTRIBUTE_NAME, TargetExecutionState.RUNNING, // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + + getManager().addEventsListener(this); + } + + @Override + public CompletableFuture select() { + //DbgManagerImpl manager = getManager(); + //DbgProcessImpl process = manager.getCurrentProcess(); + //return manager.execute(new DbgProcessSelectCommand(manager, process)); + return CompletableFuture.completedFuture(null); + } + + @Override + public TargetAccessibility getAccessibility() { + return accessibility; + } + + @Override + public DbgModelTargetProcessContainer getProcesses() { + return processes; + } + + @Override + public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause, + DbgReason reason) { + TargetExecutionState targetState = convertState(state); + setExecutionState(targetState, "ThreadStateChanged"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetStackFrameImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetStackFrameImpl.java new file mode 100644 index 0000000000..75c5b6d91f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetStackFrameImpl.java @@ -0,0 +1,162 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.cmd.DbgThreadSelectCommand; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface1.DbgModelTargetFocusScope; +import agent.dbgeng.model.iface2.*; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; + +public class DbgModelTargetStackFrameImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetStackFrame { + + protected static String indexFrame(DbgStackFrame frame) { + return PathUtils.makeIndex(frame.getLevel()); + } + + protected static String keyFrame(DbgStackFrame frame) { + return PathUtils.makeKey(indexFrame(frame)); + } + + protected final DbgModelTargetThread thread; + + protected DbgStackFrame frame; + protected Address pc; + protected String func; + protected String display; + + private Long funcTableEntry = -1L; + private Long frameOffset = -1L; + private Long returnOffset = -1L; + private Long stackOffset = -1L; + private Boolean virtual = false; + private long[] params = new long[4]; + + public DbgModelTargetStackFrameImpl(DbgModelTargetStack stack, DbgModelTargetThread thread, + DbgStackFrame frame) { + super(stack.getModel(), stack, keyFrame(frame), "StackFrame"); + this.thread = thread; + this.pc = getModel().getAddressSpace("ram").getAddress(-1); + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, display = computeDisplay(frame), // + PC_ATTRIBUTE_NAME, pc // + ), "Initialized"); + setFrame(frame); + + getManager().addEventsListener(this); + } + + protected static String computeDisplay(DbgStackFrame frame) { + if (frame.getFunction() == null) { + return String.format("#%d 0x%s", frame.getLevel(), frame.getAddress().toString(16)); + } + return String.format("#%d 0x%s in %s ()", frame.getLevel(), frame.getAddress().toString(16), + frame.getFunction()); + } + + @Override + public void threadSelected(DbgThread eventThread, DbgStackFrame eventFrame, DbgCause cause) { + if (eventFrame != null && eventFrame.equals(frame)) { + AtomicReference> scope = new AtomicReference<>(); + AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + DebugModelConventions.findSuitable(DbgModelTargetFocusScope.class, this) + .handle(seq::next); + }, scope).then(seq -> { + scope.get().setFocus(this); + }).finish(); + } + } + + @Override + public void setFrame(DbgStackFrame frame) { + BigInteger address = frame.getAddress(); + long lval = address == null ? -1 : address.longValue(); + this.pc = getModel().getAddressSpace("ram").getAddress(lval); + this.func = frame.getFunction(); + if (func == null) { + func = "UNKNOWN"; + } + this.funcTableEntry = frame.getFuncTableEntry(); + this.frameOffset = frame.getFrameOffset(); + this.returnOffset = frame.getReturnOffset(); + this.stackOffset = frame.getStackOffset(); + this.virtual = frame.getVirtual(); + this.params = frame.getParams(); + // TODO: module? "from" + this.frame = frame; + + changeAttributes(List.of(), List.of(), Map.of( // + PC_ATTRIBUTE_NAME, pc, // + DISPLAY_ATTRIBUTE_NAME, display = computeDisplay(frame), // + FUNC_ATTRIBUTE_NAME, func, // + FUNC_TABLE_ENTRY_ATTRIBUTE_NAME, Long.toHexString(funcTableEntry), // + INST_OFFSET_ATTRIBUTE_NAME, Long.toHexString(lval), // + FRAME_OFFSET_ATTRIBUTE_NAME, Long.toHexString(frameOffset), // + RETURN_OFFSET_ATTRIBUTE_NAME, Long.toHexString(returnOffset), // + STACK_OFFSET_ATTRIBUTE_NAME, Long.toHexString(stackOffset), // + VIRTUAL_ATTRIBUTE_NAME, virtual // + ), "Refreshed"); + changeAttributes(List.of(), List.of(), Map.of( // + PARAM0_ATTRIBUTE_NAME, Long.toHexString(params[0]), // + PARAM1_ATTRIBUTE_NAME, Long.toHexString(params[1]), // + PARAM2_ATTRIBUTE_NAME, Long.toHexString(params[2]), // + PARAM3_ATTRIBUTE_NAME, Long.toHexString(params[3]) // + ), "Refreshed"); + } + + @Override + public CompletableFuture select() { + DbgManagerImpl manager = getManager(); + return manager + .execute(new DbgThreadSelectCommand(manager, thread.getThread(), frame.getLevel())); + } + + @Override + public TargetObjectRef getThread() { + return thread.getParent(); + } + + @Override + public Address getPC() { + return pc; + } + + @Override + public DbgModelTargetProcess getProcess() { + return ((DbgModelTargetThreadImpl) thread).getProcess(); + } + + /* + public void invalidateRegisterCaches() { + listeners.fire.invalidateCacheRequested(this); + } + */ + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetStackImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetStackImpl.java new file mode 100644 index 0000000000..8cd3c18e9b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetStackImpl.java @@ -0,0 +1,102 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.dbgeng.manager.DbgStackFrame; +import agent.dbgeng.model.iface2.*; +import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibility; +import ghidra.dbg.target.TargetObject; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +public class DbgModelTargetStackImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetStack { + + protected final DbgModelTargetThread thread; + + protected final Map framesByLevel = + new WeakValueHashMap<>(); + + public DbgModelTargetStackImpl(DbgModelTargetThread thread, DbgModelTargetProcess process) { + super(thread.getModel(), thread, "Stack", "Stack"); + this.thread = thread; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return thread.getThread().listStackFrames().thenAccept(f -> { + List frames; + synchronized (this) { + frames = f.stream().map(this::getTargetFrame).collect(Collectors.toList()); + } + // TODO: This might be a case where "move" is useful + setElements(frames, Map.of(), "Refreshed"); + }); + } + + @Override + public synchronized DbgModelTargetStackFrame getTargetFrame(DbgStackFrame frame) { + return framesByLevel.compute(frame.getLevel(), (l, f) -> { + if (f == null) { + return new DbgModelTargetStackFrameImpl(this, thread, frame); + } + f.setFrame(frame); + return f; + }); + } + + /* + public void invalidateRegisterCaches() { + setElements(List.of(), Map.of(), "Invalidated"); + for (DbgModelTargetStackFrameImpl frame : framesByLevel.values()) { + frame.invalidateRegisterCaches(); + } + } + */ + + @Override + public void onRunning() { + // NB: We don't want to do this apparently + //invalidateRegisterCaches(); + setAccessibility(TargetAccessibility.INACCESSIBLE); + } + + @Override + public void onStopped() { + setAccessibility(TargetAccessibility.ACCESSIBLE); + if (thread.getThread().getId().equals(getManager().getEventThread().getId())) { + update(); + } + } + + /** + * Re-fetch the stack frames, generating events for updates + * + * GDB doesn't produce stack change events, but they should only ever happen by running a + * target. Thus, every time we're STOPPED, this method should be called. + */ + public void update() { + requestElements(true).exceptionally(e -> { + Msg.error(this, "Could not update stack " + this + " on STOPPED"); + return null; + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSymbolContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSymbolContainerImpl.java new file mode 100644 index 0000000000..40875c3101 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSymbolContainerImpl.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.dbgeng.manager.impl.DbgMinimalSymbol; +import agent.dbgeng.model.iface2.DbgModelTargetSymbolContainer; +import ghidra.dbg.target.TargetObject; +import ghidra.util.datastruct.WeakValueHashMap; + +public class DbgModelTargetSymbolContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetSymbolContainer { + + protected final DbgModelTargetModuleImpl module; + + protected final Map symbolsByName = new WeakValueHashMap<>(); + + public DbgModelTargetSymbolContainerImpl(DbgModelTargetModuleImpl module) { + super(module.getModel(), module, "Symbols", "SymbolContainer"); + this.module = module; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return module.module.listMinimalSymbols().thenAccept(byName -> { + List symbols; + synchronized (this) { + symbols = byName.values() + .stream() + .map(this::getTargetSymbol) + .collect(Collectors.toList()); + } + setElements(symbols, Map.of(), "Refreshed"); + }); + } + + @Override + public synchronized DbgModelTargetSymbolImpl getTargetSymbol(DbgMinimalSymbol symbol) { + return symbolsByName.computeIfAbsent(symbol.getName(), + n -> new DbgModelTargetSymbolImpl(this, symbol)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSymbolImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSymbolImpl.java new file mode 100644 index 0000000000..2dd01f21ae --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSymbolImpl.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.dbgeng.manager.impl.DbgMinimalSymbol; +import agent.dbgeng.model.iface2.DbgModelTargetSymbol; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; + +public class DbgModelTargetSymbolImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetSymbol { + protected static String indexSymbol(DbgMinimalSymbol symbol) { + return symbol.getName(); + } + + protected static String keySymbol(DbgMinimalSymbol symbol) { + return PathUtils.makeKey(indexSymbol(symbol)); + } + + protected final boolean constant; + protected final Address value; + protected final int size; + + public DbgModelTargetSymbolImpl(DbgModelTargetSymbolContainerImpl symbols, + DbgMinimalSymbol symbol) { + super(symbols.getModel(), symbols, keySymbol(symbol), "Symbol"); + this.constant = false; + this.value = symbols.getModel().getAddressSpace("ram").getAddress(symbol.getAddress()); + this.size = 0; + + changeAttributes(List.of(), List.of(), Map.of( // + // TODO: DATA_TYPE + VALUE_ATTRIBUTE_NAME, value, // + SIZE_ATTRIBUTE_NAME, size, // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public boolean isConstant() { + return constant; + } + + @Override + public Address getValue() { + return value; + } + + @Override + public long getSize() { + return size; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetThreadContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetThreadContainerImpl.java new file mode 100644 index 0000000000..5a72a43ae0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetThreadContainerImpl.java @@ -0,0 +1,127 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.reason.*; +import agent.dbgeng.model.iface2.*; +import ghidra.dbg.target.TargetObject; +import ghidra.util.datastruct.WeakValueHashMap; + +// TODO: Should TargetThreadContainer be a thing? +public class DbgModelTargetThreadContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetThreadContainer { + + protected final DbgProcess process; + + protected final Map threadsById = + new WeakValueHashMap<>(); + + public DbgModelTargetThreadContainerImpl(DbgModelTargetProcessImpl process) { + super(process.getModel(), process, "Threads", "ThreadContainer"); + this.process = process.process; + + getManager().addEventsListener(this); + } + + @Override + public void threadCreated(DbgThread thread) { + changeElements(List.of(), List.of(getTargetThread(thread)), Map.of(), "Created"); + DbgModelTargetThread targetThread = getTargetThread(thread); + changeElements(List.of(), List.of(targetThread), Map.of(), "Created"); + targetThread.threadStateChanged(DbgState.STARTING, DbgReason.getReason(null)); + getListeners().fire(TargetEventScopeListener.class) + .event(this, targetThread, TargetEventType.THREAD_CREATED, + "Thread " + thread.getId() + " started", List.of(targetThread)); + } + + @Override + public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause, + DbgReason reason) { + DbgModelTargetThread targetThread = getTargetThread(thread); + targetThread.threadStateChanged(state, reason); + TargetEventType eventType = getEventType(state, cause, reason); + getListeners().fire(TargetEventScopeListener.class) + .event(this, targetThread, eventType, "Thread " + thread.getId() + " state changed", + List.of(targetThread)); + } + + @Override + public void threadExited(DebugThreadId threadId) { + DbgModelTargetThread targetThread = threadsById.get(threadId); + if (targetThread != null) { + getListeners().fire(TargetEventScopeListener.class) + .event(this, targetThread, TargetEventType.THREAD_EXITED, + "Thread " + threadId + " exited", List.of(targetThread)); + } + synchronized (this) { + threadsById.remove(threadId); + } + changeElements(List.of( // + DbgModelTargetThreadImpl.indexThread(threadId) // + ), List.of(), Map.of(), "Exited"); + } + + private TargetEventType getEventType(DbgState state, DbgCause cause, DbgReason reason) { + switch (state) { + case RUNNING: + return TargetEventType.RUNNING; + case STOPPED: + case EXIT: + if (reason instanceof DbgEndSteppingRangeReason) { + return TargetEventType.STEP_COMPLETED; + } + if (reason instanceof DbgSignalReceivedReason) { + return TargetEventType.SIGNAL; + } + if (reason instanceof DbgExitedReason) { + return TargetEventType.EXCEPTION; + } + if (reason instanceof DbgExitNormallyReason) { + return TargetEventType.THREAD_EXITED; + } + return TargetEventType.STOPPED; + default: + break; + } + return null; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return process.listThreads().thenAccept(byTID -> { + List threads; + synchronized (this) { + threads = + byTID.values().stream().map(this::getTargetThread).collect(Collectors.toList()); + } + setElements(threads, Map.of(), "Refreshed"); + }); + } + + @Override + public synchronized DbgModelTargetThread getTargetThread(DbgThread thread) { + return threadsById.computeIfAbsent(thread.getId(), + i -> new DbgModelTargetThreadImpl(this, (DbgModelTargetProcess) parent, thread)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetThreadImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetThreadImpl.java new file mode 100644 index 0000000000..00709fb10c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetThreadImpl.java @@ -0,0 +1,184 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import agent.dbgeng.dbgeng.DebugThreadId; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.DbgManager.ExecSuffix; +import agent.dbgeng.manager.cmd.DbgThreadSelectCommand; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface1.DbgModelTargetFocusScope; +import agent.dbgeng.model.iface2.*; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.target.TargetEnvironment; +import ghidra.dbg.util.PathUtils; + +public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetThread { + + protected static final TargetStepKindSet SUPPORTED_KINDS = TargetStepKindSet.of( // + TargetStepKind.ADVANCE, TargetStepKind.FINISH, TargetStepKind.LINE, TargetStepKind.OVER, + TargetStepKind.OVER_LINE, TargetStepKind.RETURN, TargetStepKind.UNTIL); + + protected static String indexThread(DebugThreadId debugThreadId) { + return PathUtils.makeIndex(debugThreadId.id); + } + + protected static String indexThread(DbgThread thread) { + return indexThread(thread.getId()); + } + + protected static String keyThread(DbgThread thread) { + return PathUtils.makeKey(indexThread(thread)); + } + + protected final DbgThread thread; + + protected final DbgModelTargetRegisterContainer registers; + protected final DbgModelTargetStackImpl stack; + + private DbgModelTargetProcess process; + + public DbgModelTargetThreadImpl(DbgModelTargetThreadContainer threads, + DbgModelTargetProcess process, DbgThread thread) { + super(threads.getModel(), threads, keyThread(thread), "Thread"); + this.process = process; + this.thread = thread; + + this.registers = new DbgModelTargetRegisterContainerImpl(this); + this.stack = new DbgModelTargetStackImpl(this, process); + + changeAttributes(List.of(), List.of( // + registers, // + stack // + ), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS // + ), "Initialized"); + setExecutionState(convertState(thread.getState()), "Initialized"); + // TODO: Stack (Registers) + + getManager().addEventsListener(this); + } + + @Override + public String getDisplay() { + if (getManager().isKernelMode()) { + return "[PR" + thread.getId().id + "]"; + } + return "[" + thread.getId().id + ":0x" + Long.toHexString(thread.getTid()) + "]"; + } + + @Override + public void threadSelected(DbgThread eventThread, DbgStackFrame frame, DbgCause cause) { + if (eventThread.equals(thread)) { + AtomicReference> scope = new AtomicReference<>(); + AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + DebugModelConventions.findSuitable(DbgModelTargetFocusScope.class, this) + .handle(seq::next); + }, scope).then(seq -> { + scope.get().setFocus(this); + }).finish(); + } + } + + @Override + public void threadStateChanged(DbgState state, DbgReason reason) { + TargetExecutionState targetState = convertState(state); + String executionType = thread.getExecutingProcessorType().description; + changeAttributes(List.of(), List.of(), Map.of( // + TargetEnvironment.ARCH_ATTRIBUTE_NAME, executionType // + ), reason.desc()); + setExecutionState(targetState, reason.desc()); + } + + @Override + public ExecSuffix convertToDbg(TargetStepKind kind) { + switch (kind) { + case FINISH: + return ExecSuffix.FINISH; + case INTO: + return ExecSuffix.STEP_INSTRUCTION; + case LINE: + return ExecSuffix.STEP; + case OVER: + return ExecSuffix.NEXT_INSTRUCTION; + case OVER_LINE: + return ExecSuffix.NEXT; + case RETURN: + return ExecSuffix.RETURN; + case UNTIL: + return ExecSuffix.UNTIL; + default: + throw new AssertionError(); + } + } + + @Override + public CompletableFuture step(TargetStepKind kind) { + switch (kind) { + case SKIP: + throw new UnsupportedOperationException(kind.name()); + case ADVANCE: // Why no exec-advance in GDB/MI? + return thread.console("advance"); + default: + return thread.step(convertToDbg(kind)); + } + } + + @Override + public CompletableFuture select() { + DbgManagerImpl manager = getManager(); + return manager.execute(new DbgThreadSelectCommand(manager, thread, null)); + } + + @Override + public DbgModelTargetRegisterContainer getRegisters() { + return registers; + } + + @Override + public DbgModelTargetStackImpl getStack() { + return stack; + } + + @Override + public DbgThread getThread() { + return thread; + } + + public DbgModelTargetProcess getProcess() { + return process; + } + + @Override + public TargetAccessibility getAccessibility() { + return accessibility; + } + + @Override + public String getExecutingProcessorType() { + return thread.getExecutingProcessorType().description; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetTraceOrDumpConnectorImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetTraceOrDumpConnectorImpl.java new file mode 100644 index 0000000000..8e9c409090 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetTraceOrDumpConnectorImpl.java @@ -0,0 +1,79 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.iface2.DbgModelTargetConnector; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; + +public class DbgModelTargetTraceOrDumpConnectorImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetConnector { + + protected final DbgModelTargetConnectorContainerImpl connectors; + protected final TargetParameterMap paramDescs; + + public DbgModelTargetTraceOrDumpConnectorImpl(DbgModelTargetConnectorContainerImpl connectors, + String name) { + super(connectors.getModel(), connectors, name, name); + this.connectors = connectors; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public CompletableFuture select() { + connectors.setDefaultConnector(this); + return CompletableFuture.completedFuture(null); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription p1 = ParameterDescription.create(String.class, "CommandLine", + true, ".opendump", "Cmd", "native loader command"); + ParameterDescription p2 = ParameterDescription.create(String.class, "TraceOrDump", + true, "", "File", "trace or dump to be loaded"); + map.put("CommandLine", p1); + map.put("TraceOrDump", p2); + return map; + } + + @Override + public TargetParameterMap getParameters() { + return TargetMethod.getParameters(this); + } + + @Override + public CompletableFuture launch(Map args) { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + getManager().openFile(args).handle(seq::nextIgnore); + }).finish().exceptionally((exc) -> { + throw new DebuggerUserException("Launch failed for " + args); + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/sctldbg/cpp/sctldbg.cpp b/Ghidra/Debug/Debugger-agent-dbgeng/src/sctldbg/cpp/sctldbg.cpp new file mode 100644 index 0000000000..747c9f9203 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/sctldbg/cpp/sctldbg.cpp @@ -0,0 +1,315 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#define INITGUID +#include +#include + +#include + +JavaVM* jvm = NULL; +JNIEnv* env = NULL; + +char JDK_JVM_DLL_PATH[] = "\\jre\\bin\\server\\jvm.dll"; +char JRE_JVM_DLL_PATH[] = "\\bin\\server\\jvm.dll"; + +char MAIN_CLASS[] = "sctldbgeng/sctl/DbgEngSctlServer"; + +char CP_PREFIX[] = "-Djava.class.path="; + +typedef jint (_cdecl *CreateJavaVMFunc)(JavaVM**, void**, void*); + + +#define CHECK_RC(v, f, x) do { \ + HRESULT ___hr = (x); \ + if (___hr < 0) { \ + fprintf(stderr, "FAILED on line %d: HRESULT=%08x\n", __LINE__, ___hr); \ + goto f; \ + } else if (___hr == S_OK) { \ + v = 1; \ + } else { \ + v = 0; \ + } \ +} while (0) + + +#if 0 +class MyEventCallbacks : public DebugBaseEventCallbacks { +public: + STDMETHOD_(ULONG, AddRef)(THIS) { + InterlockedIncrement(&m_ulRefCount); + return m_ulRefCount; + } + + STDMETHOD_(ULONG, Release)(THIS) { + ULONG ulRefCount = InterlockedDecrement(&m_ulRefCount); + if (m_ulRefCount == 0) { + delete this; + } + return ulRefCount; + } + + STDMETHOD(GetInterestMask)(_Out_ PULONG Mask) { + *Mask = DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_CREATE_THREAD; + return S_OK; + } + + STDMETHOD(CreateProcess)( + THIS_ + _In_ ULONG64 ImageFileHandle, + _In_ ULONG64 Handle, + _In_ ULONG64 BaseOffset, + _In_ ULONG ModuleSize, + _In_ PCSTR ModuleName, + _In_ PCSTR ImageName, + _In_ ULONG CheckSum, + _In_ ULONG TimeDateStamp, + _In_ ULONG64 InitialThreadHandle, + _In_ ULONG64 ThreadDataOffset, + _In_ ULONG64 StartOffset + ) { + UNREFERENCED_PARAMETER(ImageFileHandle); + UNREFERENCED_PARAMETER(Handle); + UNREFERENCED_PARAMETER(BaseOffset); + UNREFERENCED_PARAMETER(ModuleSize); + UNREFERENCED_PARAMETER(ModuleName); + UNREFERENCED_PARAMETER(ImageName); + UNREFERENCED_PARAMETER(CheckSum); + UNREFERENCED_PARAMETER(TimeDateStamp); + UNREFERENCED_PARAMETER(InitialThreadHandle); + UNREFERENCED_PARAMETER(ThreadDataOffset); + UNREFERENCED_PARAMETER(StartOffset); + return DEBUG_STATUS_BREAK; + } + + STDMETHOD(CreateThread)( + THIS_ + _In_ ULONG64 Handle, + _In_ ULONG64 DataOffset, + _In_ ULONG64 StartOffset + ) { + UNREFERENCED_PARAMETER(Handle); + UNREFERENCED_PARAMETER(DataOffset); + UNREFERENCED_PARAMETER(StartOffset); + return DEBUG_STATUS_BREAK; + } +private: + ULONG m_ulRefCount = 0; +}; + +int main_exp00(int argc, char** argv) { + PDEBUG_CLIENT5 pClient5 = NULL; + PDEBUG_CONTROL4 pControl4 = NULL; + PDEBUG_SYMBOLS3 pSymbols3 = NULL; + int ok = 0; + + CHECK_RC(ok, EXIT, DebugCreate(IID_IDebugClient5, (PVOID*) &pClient5)); + CHECK_RC(ok, EXIT, pClient5->QueryInterface(IID_IDebugControl4, (PVOID*) &pControl4)); + CHECK_RC(ok, EXIT, pClient5->QueryInterface(IID_IDebugSymbols3, (PVOID*) &pSymbols3)); + + pClient5->SetEventCallbacks(new MyEventCallbacks()); + + CHECK_RC(ok, EXIT, pControl4->Execute(DEBUG_OUTCTL_ALL_CLIENTS, ".create notepad", DEBUG_EXECUTE_ECHO)); + CHECK_RC(ok, EXIT, pControl4->WaitForEvent(0, INFINITE)); + CHECK_RC(ok, EXIT, pControl4->Execute(DEBUG_OUTCTL_ALL_CLIENTS, "g", DEBUG_EXECUTE_ECHO)); + CHECK_RC(ok, EXIT, pControl4->WaitForEvent(0, INFINITE)); + + ULONG64 ul64MatchHandle = 0; + CHECK_RC(ok, EXIT, pSymbols3->StartSymbolMatch("*", &ul64MatchHandle)); + while (true) { + char aBuffer[1024] = { 0 }; + ULONG64 ul64Offset = 0; + CHECK_RC(ok, FINISH, pSymbols3->GetNextSymbolMatch(ul64MatchHandle, aBuffer, sizeof(aBuffer), NULL, &ul64Offset)); + printf("%016x: %s\n", ul64Offset, aBuffer); + } +FINISH: + + fprintf(stderr, "SUCCESS\n"); +EXIT: + pClient5->SetEventCallbacks(NULL); + pControl4->Release(); + pClient5->Release(); + return 0; +} + +int main_exp01(int argc, char** argv) { + PDEBUG_CLIENT5 pClient5 = NULL; + int ok = 0; + + CHECK_RC(ok, EXIT, DebugCreate(IID_IDebugClient5, (PVOID*) &pClient5)); + + CHECK_RC(ok, EXIT, pClient5->StartProcessServerWide(DEBUG_CLASS_USER_WINDOWS, L"tcp:port=11200", NULL)); + CHECK_RC(ok, EXIT, pClient5->WaitForProcessServerEnd(INFINITE)); +EXIT: + if (pClient5 != NULL) { + pClient5->Release(); + } + return 0; +} +#endif + +int main_sctldbg(int argc, char** argv) { + if (argc < 1) { + fprintf(stderr, "Something is terribly wrong: argc == 0\n"); + } + char* env_java_home = getenv("JAVA_HOME"); + if (env_java_home == NULL) { + fprintf(stderr, "JAVA_HOME is not set\n"); + fflush(stderr); + return -1; + } + char* java_home = strdup(env_java_home); + size_t home_len = strlen(java_home); + if (java_home[home_len - 1] == '\\') { + java_home[home_len - 1] = '\0'; + } + size_t full_len = home_len + sizeof(JDK_JVM_DLL_PATH); + char* full_path = new char[full_len]; + HMODULE jvmDll = NULL; + // Try the JRE path first; + strcpy_s(full_path, full_len, java_home); + strcat_s(full_path, full_len, JRE_JVM_DLL_PATH); + fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path); + fflush(stderr); + jvmDll = LoadLibraryA(full_path); + if (jvmDll == NULL) { + // OK, then try the JDK path + strcpy_s(full_path, full_len, java_home); + strcat_s(full_path, full_len, JDK_JVM_DLL_PATH); + fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path); + fflush(stderr); + jvmDll = LoadLibraryA(full_path); + } + + free(full_path); + free(java_home); + + if (jvmDll == NULL) { + fprintf(stderr, "Could not find the jvm.dll\n"); + fflush(stderr); + return -1; + } + fprintf(stderr, "Found it!\n"); + fflush(stderr); + +#define USE_EXE_AS_JAR +#ifdef USE_EXE_AS_JAR + DWORD fullpath_len = GetFullPathNameA(argv[0], 0, NULL, NULL); + char* fullpath = new char[fullpath_len]; + GetFullPathNameA(argv[0], fullpath_len, fullpath, NULL); + size_t cp_opt_len = sizeof(CP_PREFIX) + strlen(fullpath); + char* cp_opt = new char[cp_opt_len]; + strcpy_s(cp_opt, cp_opt_len, CP_PREFIX); + strcat_s(cp_opt, cp_opt_len, fullpath); + fflush(stderr); +#endif + + JavaVMOption options[2]; + JavaVMInitArgs vm_args = { 0 }; + vm_args.version = JNI_VERSION_1_8; + vm_args.nOptions = sizeof(options)/sizeof(options[0]); + vm_args.options = options; + options[0].optionString = "-Xrs"; +#ifdef USE_EXE_AS_JAR + fprintf(stderr, "Classpath: %s\n", cp_opt); + options[1].optionString = cp_opt; +#else + options[1].optionString = "-Djava.class.path=sctldbgeng.jar"; +#endif + //options[2].optionString = "-verbose:class"; + vm_args.ignoreUnrecognized = false; + CreateJavaVMFunc create_jvm = NULL; + //create_jvm = JNI_CreateJavaVM; + create_jvm = (CreateJavaVMFunc) GetProcAddress(jvmDll, "JNI_CreateJavaVM"); + jint jni_result = create_jvm(&jvm, (void**)&env, &vm_args); + +#ifdef USE_EXE_AS_JAR + free(cp_opt); +#endif + + if (jni_result != JNI_OK) { + jvm = NULL; + fprintf(stderr, "Could not initialize JVM: %d: ", jni_result); + switch (jni_result) { + case JNI_ERR: + fprintf(stderr, "unknown error"); + break; + case JNI_EDETACHED: + fprintf(stderr, "thread detached from the VM"); + break; + case JNI_EVERSION: + fprintf(stderr, "JNI version error"); + break; + case JNI_ENOMEM: + fprintf(stderr, "not enough memory"); + break; + case JNI_EEXIST: + fprintf(stderr, "VM already created"); + break; + case JNI_EINVAL: + fprintf(stderr, "invalid arguments"); + break; + } + fprintf(stderr, "\n"); + fflush(stderr); + return -1; + } + + jclass mainCls = env->FindClass(MAIN_CLASS); + if (mainCls == NULL) { + fprintf(stderr, "Could not find main class: %s\n", MAIN_CLASS); + jvm->DestroyJavaVM(); + return -1; + } + + jmethodID mainMeth = env->GetStaticMethodID(mainCls, "main", "([Ljava/lang/String;)V"); + if (mainMeth == NULL) { + fprintf(stderr, "No main(String[] args) method in main class\n"); + jvm->DestroyJavaVM(); + return -1; + } + + jclass stringCls = env->FindClass("java/lang/String"); + + jobjectArray jargs = env->NewObjectArray(argc - 1, stringCls, NULL); + for (int i = 1; i < argc; i++) { + jstring a = env->NewStringUTF(argv[i]); + if (a == NULL) { + fprintf(stderr, "Could not create Java string for arguments.\n"); + jvm->DestroyJavaVM(); + return -1; + } + env->SetObjectArrayElement(jargs, i - 1, a); + } + + env->CallStaticVoidMethod(mainCls, mainMeth, (jvalue*) jargs); + + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + jvm->DestroyJavaVM(); + + return 0; +} + +int main(int argc, char** argv) { + main_sctldbg(argc, argv); +} + diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/DummyProc.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/DummyProc.java new file mode 100644 index 0000000000..749015715c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/DummyProc.java @@ -0,0 +1,51 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng; + +import static org.junit.Assume.assumeTrue; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinNT.HANDLE; + +public class DummyProc implements AutoCloseable { + final Process process; + public final int pid; + + public DummyProc(String... args) throws IOException, NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException { + process = new ProcessBuilder(args).start(); + + Class cls = process.getClass(); + assumeTrue(cls.getName().equals("java.lang.ProcessImpl")); + Field handleFld = cls.getDeclaredField("handle"); + handleFld.setAccessible(true); + long handle = handleFld.getLong(process); + pid = Kernel32.INSTANCE.GetProcessId(new HANDLE(new Pointer(handle))); + } + + @Override + public void close() throws Exception { + if (!process.destroyForcibly().waitFor(1000, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Could not terminate process " + pid); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTest.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTest.java new file mode 100644 index 0000000000..547401bbdf --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTest.java @@ -0,0 +1,899 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; + +import java.io.*; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import org.junit.*; + +import com.sun.jna.Native; +import com.sun.jna.platform.win32.COM.COMException; +import com.sun.jna.win32.StdCallLibrary; + +import agent.dbgeng.DummyProc; +import agent.dbgeng.dbgeng.DebugBreakpoint.BreakType; +import agent.dbgeng.dbgeng.DebugClient.*; +import agent.dbgeng.dbgeng.DebugDataSpaces.*; +import agent.dbgeng.dbgeng.DebugModule.DebugModuleName; +import agent.dbgeng.dbgeng.DebugRegisters.DebugRegisterDescription; +import agent.dbgeng.dbgeng.DebugRegisters.DebugRegisterSource; +import agent.dbgeng.dbgeng.DebugValue.DebugInt64Value; +import agent.dbgeng.dbgeng.DebugValue.DebugValueType; +import agent.dbgeng.dbgeng.util.DebugEventCallbacksAdapter; +import ghidra.comm.util.BitmaskSet; +import ghidra.framework.Application; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; + +public class DbgEngTest extends AbstractGhidraHeadlessIntegrationTest { + + private interface DummyLibIf extends StdCallLibrary { + } + + public static void assumeDbgengDLLLoadable() { + try { + Native.load("dbgeng.dll", DummyLibIf.class); + + } + catch (UnsatisfiedLinkError ex) { + assumeTrue("dbgeng.dll not found. Probably not Windows here.", false); + } + } + + protected static DebugClient cachedClient = null; + + protected DebugClient doDebugCreate() { + System.out.println("Creating a client"); + return DbgEng.debugCreate().createClient(); + } + + protected DebugClient debugCreate() { + //if (cachedClient == null) { + cachedClient = doDebugCreate(); + //} + return cachedClient; + } + + protected DebugClient client; + protected DebugControl control; + + @Before + public void setUp() { + assumeDbgengDLLLoadable(); + client = debugCreate(); + control = client.getControl(); + } + + @Test + public void testPrintln() { + CompletableFuture cb = new CompletableFuture<>(); + client.setOutputCallbacks(new DebugOutputCallbacks() { + @Override + public void output(int mask, String text) { + System.out.print(text); + cb.complete(text); + } + }); + control.outln("Hello, World!"); + String back = cb.getNow(null); + // NOTE: I'd like to be precise wrt/ new lines, but it seems to vary with version. + assertEquals("Hello, World!", back.trim()); + } + + @Test + public void testGetProcessSystemIds() { + List procs = client.getRunningProcesses(client.getLocalServer()); + System.out.println("Total: " + procs.size()); + procs.sort(null); + for (DebugRunningProcess p : procs) { + System.out.println("ID: " + p.getSystemId()); + } + } + + @Test + public void testGetProcessDescriptions() { + List procs = client.getRunningProcesses(client.getLocalServer()); + System.out.println("Total: " + procs.size()); + procs.sort(null); + for (DebugRunningProcess p : procs) { + try { + System.out.println(p.getFullDescription()); + } + catch (COMException e) { + System.out.println("Error with PID " + p.getSystemId() + ": " + e.getMessage()); + } + } + } + + public static abstract class NoisyDebugEventCallbacksAdapter + extends DebugEventCallbacksAdapter { + final DebugStatus defaultStatus; + + public NoisyDebugEventCallbacksAdapter(DebugStatus defaultStatus) { + this.defaultStatus = defaultStatus; + } + + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + Msg.info(this, "createProcess: " + debugProcessInfo); + return defaultStatus; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + Msg.info(this, "createThread: " + debugThreadInfo); + return defaultStatus; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + Msg.info(this, "exitProcess: " + Integer.toHexString(exitCode)); + return defaultStatus; + } + + @Override + public DebugStatus breakpoint(DebugBreakpoint bp) { + Msg.info(this, "breakpoint: " + bp); + return defaultStatus; + } + + @Override + public DebugStatus changeDebuggeeState(BitmaskSet flags, + long argument) { + Msg.info(this, "changeDebuggeeState: " + flags + ", " + argument); + return defaultStatus; + } + + @Override + public DebugStatus changeEngineState(BitmaskSet flags, long argument) { + Msg.info(this, "changeEngineState: " + flags + ", " + argument); + return defaultStatus; + } + + @Override + public DebugStatus changeSymbolState(BitmaskSet flags, long argument) { + Msg.info(this, "changeSymbolState: " + flags + ", " + argument); + return defaultStatus; + } + + @Override + public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { + Msg.info(this, "exception: " + exception + ", " + firstChance); + return defaultStatus; + } + + @Override + public DebugStatus exitThread(int exitCode) { + Msg.info(this, "exitThread: " + Integer.toHexString(exitCode)); + return defaultStatus; + } + + @Override + public DebugStatus loadModule(DebugModuleInfo debugModuleInfo) { + Msg.info(this, "loadModule: " + debugModuleInfo); + return defaultStatus; + } + + @Override + public DebugStatus sessionStatus(SessionStatus status) { + Msg.info(this, "sessionStatus: " + status); + return defaultStatus; + } + + @Override + public DebugStatus systemError(int error, int level) { + Msg.info(this, "systemError: " + error + ", " + level); + return defaultStatus; + } + + @Override + public DebugStatus unloadModule(String imageBaseName, long baseOffset) { + Msg.info(this, "unloadModule: " + imageBaseName + ", " + baseOffset); + return defaultStatus; + } + } + + protected class ProcMaker implements AutoCloseable { + public ProcMaker(String cmdLine) { + this.cmdLine = cmdLine; + } + + final String cmdLine; + + final CompletableFuture procInfo = new CompletableFuture<>(); + final CompletableFuture threadInfo = new CompletableFuture<>(); + final CompletableFuture procExit = new CompletableFuture<>(); + + StringBuilder outputCapture = null; + + public void start() { + client.setEventCallbacks(new NoisyDebugEventCallbacksAdapter(DebugStatus.NO_CHANGE) { + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + super.createProcess(debugProcessInfo); + procInfo.complete(debugProcessInfo); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + super.createThread(debugThreadInfo); + threadInfo.complete(debugThreadInfo); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + super.exitProcess(exitCode); + procExit.complete(exitCode); + return DebugStatus.BREAK; + } + }); + client.setOutputCallbacks(new DebugOutputCallbacks() { + @Override + public void output(int mask, String text) { + System.out.print(text); + if (outputCapture != null) { + outputCapture.append(text); + } + } + }); + + Msg.debug(this, "Starting " + cmdLine + " with client " + client); + control.execute(".create " + cmdLine); + control.waitForEvent(); + DebugProcessInfo pi = procInfo.getNow(null); + assertNotNull(pi); + control.execute("g"); + control.waitForEvent(); + DebugThreadInfo ti = threadInfo.getNow(null); + assertNotNull(ti); + } + + public void kill() { + Msg.debug(this, "Killing " + cmdLine); + control.execute(".kill"); + control.waitForEvent(); + Integer exitCode = procExit.getNow(null); + client.setOutputCallbacks(null); + assertNotNull(exitCode); + } + + public List execCapture(String command) { + try { + outputCapture = new StringBuilder(); + control.execute(command); + return Arrays.asList(outputCapture.toString().split("\n")); + } + finally { + outputCapture = null; + } + } + + @Override + public void close() { + if (procInfo.isDone() && !procExit.isDone()) { + kill(); + } + } + } + + @Test + public void testDescribeVector256Register() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugRegisters regs = client.getRegisters(); + int index = regs.getIndexByName("ymm0"); + DebugRegisterDescription desc = regs.getDescription(index); + Msg.debug(this, "desc=" + desc); + // This seems wrong, but I'm curious about the value + assertEquals(DebugValueType.VECTOR128, desc.type); + + DebugValue value = regs.getValue(index); + Msg.debug(this, "value=" + value); + } + } + + @Test + public void testGetSingleRegister() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + List out = maker.execCapture("r"); + String expected = + out.stream().filter(s -> s.startsWith("rax")).findAny().get().split("\\s+")[0]; + + DebugRegisters regs = client.getRegisters(); + DebugInt64Value raxVal = (DebugInt64Value) regs.getValueByName("rax"); + + String actual = String.format("rax=%016x", raxVal.longValue()); + assertEquals(expected, actual); + } + } + + @Test + public void testGetRegisters() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + List out = maker.execCapture("r"); + String expected = out.stream().filter(s -> s.startsWith("rax")).findAny().get(); + + DebugRegisters regs = client.getRegisters(); + List indices = new ArrayList<>(); + int raxIdx = regs.getIndexByName("rax"); + int rbxIdx = regs.getIndexByName("rbx"); + int rcxIdx = regs.getIndexByName("rcx"); + indices.add(raxIdx); + indices.add(rbxIdx); + indices.add(rcxIdx); + Map values = + regs.getValues(DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE, indices); + + String actual = String.format("rax=%016x rbx=%016x rcx=%016x", + ((DebugInt64Value) values.get(raxIdx)).longValue(), + ((DebugInt64Value) values.get(rbxIdx)).longValue(), + ((DebugInt64Value) values.get(rcxIdx)).longValue()); + assertEquals(expected, actual); + } + } + + @Test + public void testSetSingleRegister() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugRegisters regs = client.getRegisters(); + regs.setValueByName("rax", new DebugInt64Value(0x0102030405060708L)); + + List out = maker.execCapture("r"); + String actual = + out.stream().filter(s -> s.startsWith("rax")).findAny().get().split("\\s+")[0]; + assertEquals("rax=0102030405060708", actual); + } + } + + @Test + public void testSetRegisters() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugRegisters regs = client.getRegisters(); + // Purposefully choosing non-linked variant. + // Want to know that order does not make a difference. + Map values = new HashMap<>(); + values.put(regs.getIndexByName("rax"), new DebugInt64Value(0x0102030405060708L)); + values.put(regs.getIndexByName("rbx"), new DebugInt64Value(0x1122334455667788L)); + values.put(regs.getIndexByName("rcx"), new DebugInt64Value(0x8877665544332211L)); + regs.setValues(DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE, values); + + List out = maker.execCapture("r"); + String actual = out.stream().filter(s -> s.startsWith("rax")).findAny().get(); + assertEquals("rax=0102030405060708 rbx=1122334455667788 rcx=8877665544332211", actual); + } + } + + @Test + public void testQueryVirtual() { + // Also, an experiment to figure out how it works + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + List collected1 = new ArrayList<>(); + try { + long last = 0; + long offset = 0; + do { + System.out.print(Long.toHexString(offset) + ": "); + DebugMemoryBasicInformation info = client.getDataSpaces().queryVirtual(offset); + //System.out.println(info); + System.out.println(Long.toHexString(info.baseAddress) + "-" + + Long.toHexString(info.regionSize) + ": " + info.state); + if (info.baseAddress != offset) { + System.out.println(" !!!"); + } + collected1.add(info); + last = offset; + offset += info.regionSize; + } + while (Long.compareUnsigned(last, offset) < 0); + } + catch (COMException e) { + if (!e.getMessage().contains("HRESULT: 80004002")) { + throw e; + } + } + + List collected2 = new ArrayList<>(); + for (DebugMemoryBasicInformation info : client.getDataSpaces().iterateVirtual(0)) { + collected2.add(info); + } + + assertTrue(collected1.size() > 0); + assertEquals(collected1, collected2); + + // For comparison + client.getControl().execute("!address"); + } + } + + @Test + public void testModules() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + for (DebugModule mod : client.getSymbols().iterateModules(0)) { + System.out.println(mod.getIndex() + ": " + Long.toHexString(mod.getBase()) + ": " + + mod.getName(DebugModuleName.MODULE)); + System.out.println(" Img: " + mod.getName(DebugModuleName.IMAGE)); + System.out.println(" Load: " + mod.getName(DebugModuleName.LOADED_IMAGE)); + } + } + } + + @Test(expected = COMException.class) + public void testModuleOutOfBounds() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugModule umod = client.getSymbols() + .getModuleByIndex( + client.getSymbols().getNumberLoadedModules() + 1); + System.out.println(umod.getBase()); + } + } + + @Test + public void testQueryVirtualWithModule() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + for (DebugMemoryBasicInformation info : client.getDataSpaces().iterateVirtual(0)) { + if (info.state != PageState.FREE) { + DebugModule mod = null; + String name = "[NONE]"; + try { + mod = client.getSymbols().getModuleByOffset(info.baseAddress, 0); + name = mod.getName(DebugModuleName.IMAGE); + } + catch (COMException e) { + name = "[ERR:" + e + "]"; + } + System.out.println(String.format("%016x", info.baseAddress) + ":" + + Long.toHexString(info.regionSize) + ":" + info.state + " from " + name + + " " + info.type + info.protect); + } + } + } + } + + @Test + public void testSymbols() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + Set symbols = new LinkedHashSet<>(); + Set modules = new LinkedHashSet<>(); + for (DebugSymbolName sym : client.getSymbols().iterateSymbolMatches("*")) { + String[] parts = sym.name.split("!"); + symbols.add(sym); + modules.add(parts[0]); + } + System.out.println("Total Symbols: " + symbols.size()); + System.out.println("Total Modules (by symbol name): " + modules.size()); + + // These make assumptions that could be broken later. + // It used to expect at least 10 modules (devised when testing on Win7). Now it's 5! + assertTrue("Fewer than 1000 symbols: " + symbols.size(), symbols.size() > 1000); + assertTrue("Fewer than 3 modules: " + modules.size(), modules.size() > 3); + } + } + + @Test + public void testSymbolInfo() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + int count = 0; + for (DebugSymbolId symid : client.getSymbols().getSymbolIdsByName("ntdll!*")) { + //System.out.println(symid); + DebugSymbolEntry syment = client.getSymbols().getSymbolEntry(symid); + if (syment.typeId != 0) { + System.out.println(" " + syment); + } + count++; + } + + assertTrue(count > 10); + } + } + + @Test + public void testReadMemory() throws FileNotFoundException, IOException { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + control.execute(".server tcp:port=54321"); + int len = 256; + + DebugModule notepadModule = client.getSymbols().getModuleByModuleName("notepad", 0); + System.out.println("Base: " + Long.toHexString(notepadModule.getBase())); + ByteBuffer data = ByteBuffer.allocate(len); + client.getDataSpaces().readVirtual(notepadModule.getBase(), data, data.remaining()); + System.out.println(NumericUtilities.convertBytesToString(data.array())); + + // TODO: Avoid hardcoding path to notepad + try (FileInputStream fis = new FileInputStream("C:\\Windows\\notepad.exe")) { + byte[] fromFile = new byte[len]; + fis.read(fromFile); + // TODO: Note sure why, but this seems to be the case after it's loaded + //ByteBuffer toWriteBase = ByteBuffer.wrap(fromFile).order(ByteOrder.LITTLE_ENDIAN); + //toWriteBase.putLong(280, notepadModule.getBase()); + System.out.println(NumericUtilities.convertBytesToString(fromFile)); + assertArrayEquals(fromFile, data.array()); + } + + data.clear(); + data.putInt(0x12345678); + client.getDataSpaces().readVirtual(notepadModule.getBase(), data, data.remaining()); + data.flip(); + + assertEquals(0x12345678, data.getInt()); + } + } + + @Test + public void testWriteMemory() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + // TODO: How to write to protected memory? + // Debugger should be able to modify program code. + DebugMemoryBasicInformation writable = null; + space: for (DebugMemoryBasicInformation info : client.getDataSpaces() + .iterateVirtual( + 0)) { + for (PageProtection prot : info.protect) { + if (prot.isWrite()) { + writable = info; + break space; + } + } + } + if (writable == null) { + throw new AssertionError("No writable pages?"); + } + System.out.println("writable: " + writable); + ByteBuffer toWrite = ByteBuffer.allocate(10); + toWrite.putInt(0x12345678); + toWrite.putInt(0x89abcdef); + toWrite.putShort((short) 0x5555); + toWrite.flip(); + client.getDataSpaces().writeVirtual(writable.baseAddress, toWrite, toWrite.remaining()); + + ByteBuffer toRead = ByteBuffer.allocate(10); + client.getDataSpaces().readVirtual(writable.baseAddress, toRead, toRead.remaining()); + + assertArrayEquals(toWrite.array(), toRead.array()); + } + } + + @Test + public void testBreakpoints() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugBreakpoint bpt = control.addBreakpoint(BreakType.CODE); + System.out.println("Breakpoint id: " + bpt.getId()); + System.out.println("Flags: " + bpt.getFlags()); + DebugBreakpoint bpt2 = control.getBreakpointById(bpt.getId()); + assertEquals(bpt, bpt2); + } + } + + @Test + public void testFreezeUnfreeze() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + // Trying to see if any events will help me track frozen threads + System.out.println("****Freezing"); + control.execute("~0 f"); + System.out.println("****Unfreezing"); + control.execute("~0 u"); + System.out.println("****Done"); + // Well, that result stinks. + // There is no event to tell me about frozenness + } + } + + @Test + @Ignore("I can't find a reliable means to detect the last thread. " + + "There's supposed to be an initial break, but it is rarely reported. " + + "I thought about toolhelp, but that presumes local live debugging.") + public void testMultiThreadAttach() throws Exception { + // I need to see how to attach to multi-threaded processes. There must be some event + // or condition to indicate when all threads have been discovered. + String specimen = + Application.getOSFile("sctldbgeng", "expCreateThreadSpin.exe").getCanonicalPath(); + client.setOutputCallbacks(new DebugOutputCallbacks() { + @Override + public void output(int mask, String text) { + System.out.print(text); + System.out.flush(); + } + }); + client.setEventCallbacks(new DebugEventCallbacksAdapter() { + @Override + public DebugStatus breakpoint(DebugBreakpoint bp) { + control.outln("*** Breakpoint: " + bp); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { + control.outln("*** Exception: " + exception + "," + firstChance); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + control.outln("*** CreateThread: " + debugThreadInfo); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + control.outln("*** CreateProcess: " + debugProcessInfo); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitThread(int exitCode) { + control.outln("*** ExitThread: code=" + exitCode + ", " + + client.getSystemObjects().getEventThread()); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + control.outln("*** ExitProcess: code=" + exitCode + ", " + + client.getSystemObjects().getEventProcess()); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus changeEngineState(BitmaskSet flags, + long argument) { + if (flags.contains(ChangeEngineState.EXECUTION_STATUS)) { + control.outln( + "*** ExecutionStatus: " + control.getExecutionStatus()); + } + return DebugStatus.NO_CHANGE; + } + }); + try (DummyProc proc = new DummyProc(specimen)) { + System.out.println("Started " + specimen + " with PID=" + proc.pid); + Thread.sleep(1000); + System.out.println("Attaching..."); + client.attachProcess(client.getLocalServer(), proc.pid, BitmaskSet.of()); + if (true) { + for (int i = 0; i < 10; i++) { + System.out.println("WAIT " + i + "..."); + control.waitForEvent(100); + System.out.println("STATUS: " + control.getExecutionStatus()); + System.out.println("DONE " + i); + // control.execute("~*"); + } + } + } + finally { + client.setEventCallbacks(null); + } + } + + protected static class BreakAllCallbacks extends DebugEventCallbacksAdapter { + protected final DebugControl control; + protected final DebugRegisters regs; + protected final DebugSystemObjects objs; + + volatile protected long currentThread = 0; + volatile protected DebugProcessInfo createdProc = null; + volatile protected String lastReg = null; + + public BreakAllCallbacks(DebugClient client) { + this.control = client.getControl(); + this.regs = client.getRegisters(); + this.objs = client.getSystemObjects(); + } + + @Override + public DebugStatus breakpoint(DebugBreakpoint bp) { + control.outln("*** Breakpoint: " + bp); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus changeDebuggeeState(BitmaskSet flags, + long argument) { + control.outln("*** ChangeDebuggeeState: " + flags + "," + argument); + if (flags.contains(ChangeDebuggeeState.REGISTERS) && + !flags.contains(ChangeDebuggeeState.REFRESH)) { + DebugRegisterDescription description = regs.getDescription((int) argument); + control.outln(" Process: " + objs.getEventProcess()); + control.outln(" Thread: " + Long.toHexString(objs.getCurrentThreadSystemId())); + control.outln(" Reg: " + description); + control.outln(" Val: " + NumericUtilities + .convertBytesToString(regs.getValue((int) argument).encodeAsBytes(), "")); + lastReg = description.name; + } + return DebugStatus.BREAK; + } + + @Override + public DebugStatus changeEngineState(BitmaskSet flags, long argument) { + control.outln("*** ChangeEngineState: " + flags + "," + Long.toHexString(argument)); + if (flags.contains(ChangeEngineState.CURRENT_THREAD)) { + currentThread = argument; + } + return DebugStatus.BREAK; + } + + @Override + public DebugStatus changeSymbolState(BitmaskSet flags, long argument) { + control.outln("*** ChangeSymbolState: " + flags + "," + argument); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + control.outln("*** CreateProcess: " + debugProcessInfo); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + control.outln("*** CreateThread: " + debugThreadInfo); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { + control.outln("*** Exception: " + exception + "," + firstChance); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + control.outln("*** ExitProcess: code=" + exitCode); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitThread(int exitCode) { + control.outln("*** ExitThread: code=" + exitCode); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus loadModule(DebugModuleInfo debugModuleInfo) { + control.outln("*** LoadModule: " + debugModuleInfo); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus sessionStatus(SessionStatus status) { + control.outln("*** SessionStatus: " + status); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus systemError(int error, int level) { + control.outln("*** SystemError: " + error + "," + level); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus unloadModule(String imageBaseName, long baseOffset) { + control.outln("*** UnloadModule: " + imageBaseName + "," + baseOffset); + return DebugStatus.BREAK; + } + } + + protected static class ConsoleOutputCallbacks implements DebugOutputCallbacks { + @Override + public void output(int mask, String text) { + System.out.print(text); + System.out.flush(); + } + } + + @Test + public void testAttachLaunch() throws Exception { + final String specimenX = "C:\\windows\\write.exe"; + final String specimenA = "C:\\windows\\notepad.exe"; + final String specimenC = "C:\\windows\\system32\\win32calc.exe"; + + try (DummyProc proc = new DummyProc(specimenX)) { + client.setOutputCallbacks(new ConsoleOutputCallbacks()); + BreakAllCallbacks cb = new BreakAllCallbacks(client); + client.setEventCallbacks(cb); + System.out.println("Started " + specimenA + " with PID=" + proc.pid); + Thread.sleep(1000); + + //System.out.println("Attaching..."); + //client.attachProcess(client.getLocalServer(), proc.pid, BitmaskSet.of()); + client.createProcess(client.getLocalServer(), specimenA, + BitmaskSet.of(DebugCreateFlags.DEBUG_PROCESS)); + + cb.lastReg = null; + //while (cb.lastReg == null) { + while (true) { + if (cb.currentThread != 0) { + client.getControl().execute("~* r rip"); + } + client.getControl().waitForEvent(); + } +/* + final Map ripValsPreLaunch = new HashMap<>(); + for (DebugThreadId tid : client.getSystemObjects().getThreads()) { + client.getSystemObjects().setCurrentThreadId(tid); + byte[] val = client.getRegisters().getValue(16).encodeAsBytes(); + ripValsPreLaunch.put(tid, new BigInteger(1, val).toString(16)); + } + + System.out.println("Creating..."); + client.createProcess(client.getLocalServer(), specimenC, + BitmaskSet.of(DebugCreateFlags.DEBUG_PROCESS)); + + cb.lastReg = null; + while (cb.lastReg == null) { + System.err.println(client.getSystemObjects().getCurrentProcessId()); + client.getControl().waitForEvent(); + client.getSystemObjects().setCurrentProcessId(new DebugProcessId(1)); + DebugEventInformation info = client.getControl().getLastEventInformation(); + System.err.println(info.getProcessId()); + } + + client.getSystemObjects().setCurrentProcessId(new DebugProcessId(0)); + final Map ripValsPostLaunch = new HashMap<>(); + for (DebugThreadId tid : client.getSystemObjects().getThreads()) { + client.getSystemObjects().setCurrentThreadId(tid); + byte[] val = client.getRegisters().getValue(16).encodeAsBytes(); + ripValsPostLaunch.put(tid, new BigInteger(1, val).toString(16)); + } + + assertEquals(ripValsPreLaunch, ripValsPostLaunch); + */ + } + finally { + client.setOutputCallbacks(null); + client.setEventCallbacks(null); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTestOld.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTestOld.java new file mode 100644 index 0000000000..2085936a69 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTestOld.java @@ -0,0 +1,675 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import static org.junit.Assert.*; + +import java.io.*; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import org.junit.*; + +import com.sun.jna.platform.win32.COM.COMException; + +import agent.dbgeng.DummyProc; +import agent.dbgeng.dbgeng.DebugBreakpoint.BreakType; +import agent.dbgeng.dbgeng.DebugClient.*; +import agent.dbgeng.dbgeng.DebugDataSpaces.*; +import agent.dbgeng.dbgeng.DebugModule.DebugModuleName; +import agent.dbgeng.dbgeng.DebugRegisters.DebugRegisterSource; +import agent.dbgeng.dbgeng.DebugValue.DebugInt64Value; +import agent.dbgeng.dbgeng.util.DebugEventCallbacksAdapter; +import ghidra.comm.util.BitmaskSet; +import ghidra.framework.Application; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; + +public class DbgEngTestOld extends AbstractGhidraHeadlessIntegrationTest { + protected static DebugClient cachedClient = null; + + protected DebugClient doDebugCreate() { + System.out.println("Creating a client"); + return DbgEng.debugCreate().createClient(); + } + + protected DebugClient debugCreate() { + //if (cachedClient == null) { + cachedClient = doDebugCreate(); + //} + return cachedClient; + } + + protected DebugClient client; + protected DebugControl control; + + @Before + public void setUp() { + DbgEngTest.assumeDbgengDLLLoadable(); + client = debugCreate(); + control = client.getControl(); + } + + @Test + public void testPrintln() { + CompletableFuture cb = new CompletableFuture<>(); + client.setOutputCallbacks(new DebugOutputCallbacks() { + @Override + public void output(int mask, String text) { + System.out.print(text); + cb.complete(text); + } + }); + control.outln("Hello, World!"); + String back = cb.getNow(null); + // NOTE: I'd like to be precise wrt/ new lines, but it seems to vary with version. + assertEquals("Hello, World!", back.trim()); + } + + @Test + public void testGetProcessSystemIds() { + List procs = client.getRunningProcesses(client.getLocalServer()); + System.out.println("Total: " + procs.size()); + procs.sort(null); + for (DebugRunningProcess p : procs) { + System.out.println("ID: " + p.getSystemId()); + } + } + + @Test + public void testGetProcessDescriptions() { + List procs = client.getRunningProcesses(client.getLocalServer()); + System.out.println("Total: " + procs.size()); + procs.sort(null); + for (DebugRunningProcess p : procs) { + try { + System.out.println(p.getFullDescription()); + } + catch (COMException e) { + System.out.println("Error with PID " + p.getSystemId() + ": " + e.getMessage()); + } + } + } + + public static abstract class NoisyDebugEventCallbacksAdapter + extends DebugEventCallbacksAdapter { + final DebugStatus defaultStatus; + + public NoisyDebugEventCallbacksAdapter(DebugStatus defaultStatus) { + this.defaultStatus = defaultStatus; + } + + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + Msg.info(this, "createProcess: " + debugProcessInfo); + return defaultStatus; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + Msg.info(this, "createThread: " + debugThreadInfo); + return defaultStatus; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + Msg.info(this, "exitProcess: " + Integer.toHexString(exitCode)); + return defaultStatus; + } + + @Override + public DebugStatus breakpoint(DebugBreakpoint bp) { + Msg.info(this, "breakpoint: " + bp); + return defaultStatus; + } + + @Override + public DebugStatus changeDebuggeeState(BitmaskSet flags, + long argument) { + Msg.info(this, "changeDebuggeeState: " + flags + ", " + argument); + return defaultStatus; + } + + @Override + public DebugStatus changeEngineState(BitmaskSet flags, long argument) { + Msg.info(this, "changeEngineState: " + flags + ", " + argument); + return defaultStatus; + } + + @Override + public DebugStatus changeSymbolState(BitmaskSet flags, long argument) { + Msg.info(this, "changeSymbolState: " + flags + ", " + argument); + return defaultStatus; + } + + @Override + public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { + Msg.info(this, "exception: " + exception + ", " + firstChance); + return defaultStatus; + } + + @Override + public DebugStatus exitThread(int exitCode) { + Msg.info(this, "exitThread: " + Integer.toHexString(exitCode)); + return defaultStatus; + } + + @Override + public DebugStatus loadModule(DebugModuleInfo debugModuleInfo) { + Msg.info(this, "loadModule: " + debugModuleInfo); + return defaultStatus; + } + + @Override + public DebugStatus sessionStatus(SessionStatus status) { + Msg.info(this, "sessionStatus: " + status); + return defaultStatus; + } + + @Override + public DebugStatus systemError(int error, int level) { + Msg.info(this, "systemError: " + error + ", " + level); + return defaultStatus; + } + + @Override + public DebugStatus unloadModule(String imageBaseName, long baseOffset) { + Msg.info(this, "unloadModule: " + imageBaseName + ", " + baseOffset); + return defaultStatus; + } + } + + protected class ProcMaker implements AutoCloseable { + public ProcMaker(String cmdLine) { + this.cmdLine = cmdLine; + } + + final String cmdLine; + + final CompletableFuture procInfo = new CompletableFuture<>(); + final CompletableFuture threadInfo = new CompletableFuture<>(); + final CompletableFuture procExit = new CompletableFuture<>(); + + StringBuilder outputCapture = null; + + public void start() { + client.setEventCallbacks(new NoisyDebugEventCallbacksAdapter(DebugStatus.NO_CHANGE) { + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + super.createProcess(debugProcessInfo); + procInfo.complete(debugProcessInfo); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + super.createThread(debugThreadInfo); + threadInfo.complete(debugThreadInfo); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + super.exitProcess(exitCode); + procExit.complete(exitCode); + return DebugStatus.BREAK; + } + }); + client.setOutputCallbacks(new DebugOutputCallbacks() { + @Override + public void output(int mask, String text) { + System.out.print(text); + if (outputCapture != null) { + outputCapture.append(text); + } + } + }); + + Msg.debug(this, "Starting " + cmdLine + " with client " + client); + control.execute(".create " + cmdLine); + control.waitForEvent(); + DebugProcessInfo pi = procInfo.getNow(null); + assertNotNull(pi); + control.execute("g"); + control.waitForEvent(); + DebugThreadInfo ti = threadInfo.getNow(null); + assertNotNull(ti); + } + + public void kill() { + Msg.debug(this, "Killing " + cmdLine); + control.execute(".kill"); + control.waitForEvent(); + Integer exitCode = procExit.getNow(null); + client.setOutputCallbacks(null); + assertNotNull(exitCode); + } + + public List execCapture(String command) { + try { + outputCapture = new StringBuilder(); + control.execute(command); + return Arrays.asList(outputCapture.toString().split("\n")); + } + finally { + outputCapture = null; + } + } + + @Override + public void close() { + if (procInfo.isDone() && !procExit.isDone()) { + kill(); + } + } + } + + @Test + public void testGetSingleRegister() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + List out = maker.execCapture("r"); + String expected = + out.stream().filter(s -> s.startsWith("rax")).findAny().get().split("\\s+")[0]; + + DebugRegisters regs = client.getRegisters(); + DebugInt64Value raxVal = (DebugInt64Value) regs.getValueByName("rax"); + + String actual = String.format("rax=%016x", raxVal.longValue()); + assertEquals(expected, actual); + } + } + + @Test + public void testGetRegisters() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + List out = maker.execCapture("r"); + String expected = out.stream().filter(s -> s.startsWith("rax")).findAny().get(); + + DebugRegisters regs = client.getRegisters(); + List indices = new ArrayList<>(); + int raxIdx = regs.getIndexByName("rax"); + int rbxIdx = regs.getIndexByName("rbx"); + int rcxIdx = regs.getIndexByName("rcx"); + indices.add(raxIdx); + indices.add(rbxIdx); + indices.add(rcxIdx); + Map values = + regs.getValues(DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE, indices); + + String actual = String.format("rax=%016x rbx=%016x rcx=%016x", + ((DebugInt64Value) values.get(raxIdx)).longValue(), + ((DebugInt64Value) values.get(rbxIdx)).longValue(), + ((DebugInt64Value) values.get(rcxIdx)).longValue()); + assertEquals(expected, actual); + } + } + + @Test + public void testSetSingleRegister() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugRegisters regs = client.getRegisters(); + regs.setValueByName("rax", new DebugInt64Value(0x0102030405060708L)); + + List out = maker.execCapture("r"); + String actual = + out.stream().filter(s -> s.startsWith("rax")).findAny().get().split("\\s+")[0]; + assertEquals("rax=0102030405060708", actual); + } + } + + @Test + public void testSetRegisters() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugRegisters regs = client.getRegisters(); + // Purposefully choosing non-linked variant. + // Want to know that order does not make a difference. + Map values = new HashMap<>(); + values.put(regs.getIndexByName("rax"), new DebugInt64Value(0x0102030405060708L)); + values.put(regs.getIndexByName("rbx"), new DebugInt64Value(0x1122334455667788L)); + values.put(regs.getIndexByName("rcx"), new DebugInt64Value(0x8877665544332211L)); + regs.setValues(DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE, values); + + List out = maker.execCapture("r"); + String actual = out.stream().filter(s -> s.startsWith("rax")).findAny().get(); + assertEquals("rax=0102030405060708 rbx=1122334455667788 rcx=8877665544332211", actual); + } + } + + @Test + public void testQueryVirtual() { + // Also, an experiment to figure out how it works + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + List collected1 = new ArrayList<>(); + try { + long last = 0; + long offset = 0; + do { + System.out.print(Long.toHexString(offset) + ": "); + DebugMemoryBasicInformation info = client.getDataSpaces().queryVirtual(offset); + System.out.println(info); + collected1.add(info); + last = offset; + offset += info.regionSize; + } + while (Long.compareUnsigned(last, offset) < 0); + } + catch (COMException e) { + if (!e.getMessage().contains("HRESULT: 80004002")) { + throw e; + } + } + + List collected2 = new ArrayList<>(); + for (DebugMemoryBasicInformation info : client.getDataSpaces().iterateVirtual(0)) { + collected2.add(info); + } + + assertTrue(collected1.size() > 0); + assertEquals(collected1, collected2); + } + } + + @Test + public void testModules() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + for (DebugModule mod : client.getSymbols().iterateModules(0)) { + System.out.println(mod.getIndex() + ": " + Long.toHexString(mod.getBase()) + ": " + + mod.getName(DebugModuleName.MODULE)); + System.out.println(" Img: " + mod.getName(DebugModuleName.IMAGE)); + System.out.println(" Load: " + mod.getName(DebugModuleName.LOADED_IMAGE)); + } + } + } + + @Test(expected = COMException.class) + public void testModuleOutOfBounds() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugModule umod = client.getSymbols() + .getModuleByIndex( + client.getSymbols().getNumberLoadedModules() + 1); + System.out.println(umod.getBase()); + } + } + + @Test + public void testQueryVirtualWithModule() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + for (DebugMemoryBasicInformation info : client.getDataSpaces().iterateVirtual(0)) { + if (info.state != PageState.FREE) { + DebugModule mod = null; + String name = "[NONE]"; + try { + mod = client.getSymbols().getModuleByOffset(info.baseAddress, 0); + name = mod.getName(DebugModuleName.IMAGE); + } + catch (COMException e) { + name = "[ERR:" + e + "]"; + } + System.out.println(String.format("%016x", info.baseAddress) + ":" + + Long.toHexString(info.regionSize) + ":" + info.state + " from " + name + + " " + info.type + info.protect); + } + } + } + } + + @Test + public void testSymbols() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + Set symbols = new LinkedHashSet<>(); + Set modules = new LinkedHashSet<>(); + for (DebugSymbolName sym : client.getSymbols().iterateSymbolMatches("*")) { + String[] parts = sym.name.split("!"); + symbols.add(sym); + modules.add(parts[0]); + } + System.out.println("Total Symbols: " + symbols.size()); + System.out.println("Total Modules (by symbol name): " + modules.size()); + + // These make assumptions that could be broken later. + // It used to expect at least 10 modules (devised when testing on Win7). Now it's 5! + assertTrue("Fewer than 1000 symbols: " + symbols.size(), symbols.size() > 1000); + assertTrue("Fewer than 3 modules: " + modules.size(), modules.size() > 3); + } + } + + @Test + public void testSymbolInfo() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + int count = 0; + for (DebugSymbolId symid : client.getSymbols().getSymbolIdsByName("ntdll!*")) { + //System.out.println(symid); + DebugSymbolEntry syment = client.getSymbols().getSymbolEntry(symid); + if (syment.typeId != 0) { + System.out.println(" " + syment); + } + count++; + } + + assertTrue(count > 10); + } + } + + @Test + public void testReadMemory() throws FileNotFoundException, IOException { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + control.execute(".server tcp:port=54321"); + int len = 256; + + DebugModule notepadModule = client.getSymbols().getModuleByModuleName("notepad", 0); + System.out.println("Base: " + Long.toHexString(notepadModule.getBase())); + ByteBuffer data = ByteBuffer.allocate(len); + client.getDataSpaces().readVirtual(notepadModule.getBase(), data, data.remaining()); + System.out.println(NumericUtilities.convertBytesToString(data.array())); + + // TODO: Avoid hardcoding path to notepad + try (FileInputStream fis = new FileInputStream("C:\\Windows\\notepad.exe")) { + byte[] fromFile = new byte[len]; + fis.read(fromFile); + // TODO: Note sure why, but this seems to be the case after it's loaded + //ByteBuffer toWriteBase = ByteBuffer.wrap(fromFile).order(ByteOrder.LITTLE_ENDIAN); + //toWriteBase.putLong(280, notepadModule.getBase()); + System.out.println(NumericUtilities.convertBytesToString(fromFile)); + assertArrayEquals(fromFile, data.array()); + } + + data.clear(); + data.putInt(0x12345678); + client.getDataSpaces().readVirtual(notepadModule.getBase(), data, data.remaining()); + data.flip(); + + assertEquals(0x12345678, data.getInt()); + } + } + + @Test + public void testWriteMemory() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + // TODO: How to write to protected memory? + // Debugger should be able to modify program code. + DebugMemoryBasicInformation writable = null; + space: for (DebugMemoryBasicInformation info : client.getDataSpaces() + .iterateVirtual( + 0)) { + for (PageProtection prot : info.protect) { + if (prot.isWrite()) { + writable = info; + break space; + } + } + } + if (writable == null) { + throw new AssertionError("No writable pages?"); + } + System.out.println("writable: " + writable); + ByteBuffer toWrite = ByteBuffer.allocate(10); + toWrite.putInt(0x12345678); + toWrite.putInt(0x89abcdef); + toWrite.putShort((short) 0x5555); + toWrite.flip(); + client.getDataSpaces().writeVirtual(writable.baseAddress, toWrite, toWrite.remaining()); + + ByteBuffer toRead = ByteBuffer.allocate(10); + client.getDataSpaces().readVirtual(writable.baseAddress, toRead, toRead.remaining()); + + assertArrayEquals(toWrite.array(), toRead.array()); + } + } + + @Test + public void testBreakpoints() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugBreakpoint bpt = control.addBreakpoint(BreakType.CODE); + System.out.println("Breakpoint id: " + bpt.getId()); + System.out.println("Flags: " + bpt.getFlags()); + DebugBreakpoint bpt2 = control.getBreakpointById(bpt.getId()); + assertEquals(bpt, bpt2); + } + } + + @Test + public void testFreezeUnfreeze() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + // Trying to see if any events will help me track frozen threads + System.out.println("****Freezing"); + control.execute("~0 f"); + System.out.println("****Unfreezing"); + control.execute("~0 u"); + System.out.println("****Done"); + // Well, that result stinks. + // There is no event to tell me about frozenness + } + } + + @Test + @Ignore("I can't find a reliable means to detect the last thread. " + + "There's supposed to be an initial break, but it is rarely reported. " + + "I thought about toolhelp, but that presumes local live debugging.") + public void testMultiThreadAttach() throws Exception { + // I need to see how to attach to multi-threaded processes. There must be some event + // or condition to indicate when all threads have been discovered. + String specimen = + Application.getOSFile("sctldbgeng", "expCreateThreadSpin.exe").getCanonicalPath(); + client.setOutputCallbacks(new DebugOutputCallbacks() { + @Override + public void output(int mask, String text) { + System.out.print(text); + System.out.flush(); + } + }); + client.setEventCallbacks(new DebugEventCallbacksAdapter() { + @Override + public DebugStatus breakpoint(DebugBreakpoint bp) { + control.outln("*** Breakpoint: " + bp); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { + control.outln("*** Exception: " + exception + "," + firstChance); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + control.outln("*** CreateThread: " + debugThreadInfo); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + control.outln("*** CreateProcess: " + debugProcessInfo); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitThread(int exitCode) { + control.outln("*** ExitThread: code=" + exitCode + ", " + + client.getSystemObjects().getEventThread()); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + control.outln("*** ExitProcess: code=" + exitCode + ", " + + client.getSystemObjects().getEventProcess()); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus changeEngineState(BitmaskSet flags, + long argument) { + if (flags.contains(ChangeEngineState.EXECUTION_STATUS)) { + control.outln( + "*** ExecutionStatus: " + control.getExecutionStatus()); + } + return DebugStatus.NO_CHANGE; + } + }); + try (DummyProc proc = new DummyProc(specimen)) { + System.out.println("Started " + specimen + " with PID=" + proc.pid); + Thread.sleep(1000); + System.out.println("Attaching..."); + client.attachProcess(client.getLocalServer(), proc.pid, BitmaskSet.of()); + if (true) { + for (int i = 0; i < 10; i++) { + System.out.println("WAIT " + i + "..."); + control.waitForEvent(100); + System.out.println("STATUS: " + control.getExecutionStatus()); + System.out.println("DONE " + i); + // control.execute("~*"); + } + } + } + finally { + client.setEventCallbacks(null); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/AbstractDbgManagerTest.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/AbstractDbgManagerTest.java new file mode 100644 index 0000000000..f61c3d2956 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/AbstractDbgManagerTest.java @@ -0,0 +1,449 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import static agent.dbgeng.testutil.DummyProc.runProc; +import static ghidra.async.AsyncUtils.sequence; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.commons.lang3.tuple.Pair; +import org.junit.*; + +import com.google.common.collect.*; + +import agent.dbgeng.dbgeng.DbgEngTest; +import agent.dbgeng.dbgeng.DebugProcessId; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.DbgManager.ExecSuffix; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.testutil.DummyProc; +import ghidra.async.AsyncFence; +import ghidra.async.TypeSpec; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; +import ghidra.util.Msg; + +public abstract class AbstractDbgManagerTest extends AbstractGhidraHeadlessIntegrationTest { + protected static final int TIMEOUT = 2000 * 1000; + + protected abstract CompletableFuture startManager(DbgManager manager); + + protected void stopManager() throws IOException { + // Nothing by default + } + + protected T waitOn(CompletableFuture future) throws Throwable { + try { + return future.get(TIMEOUT, TimeUnit.MILLISECONDS); + } + catch (ExecutionException e) { + throw e.getCause(); + } + } + + @Before + public void setUpDbgManagerTest() { + DbgEngTest.assumeDbgengDLLLoadable(); + } + + @After + public void tearDownDbgManagerTest() throws IOException { + stopManager(); + } + + @Test + public void testAddProcess() throws Throwable { + try (DbgManager mgr = DbgManager.newInstance()) { + DbgProcess process = waitOn(sequence(TypeSpec.cls(DbgProcess.class)).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.addProcess().handle(seq::exit); + }).finish()); + assertEquals(2, process.getId()); + assertEquals(Set.of(1, 2), mgr.getKnownProcesses().keySet()); + } + } + + @Test + public void testRemoveProcess() throws Throwable { + try (DbgManager mgr = DbgManager.newInstance()) { + waitOn(sequence(TypeSpec.VOID).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.addProcess().handle(seq::next); + }, TypeSpec.cls(DbgProcess.class)).then((inf, seq) -> { + assertEquals(2, mgr.getKnownProcesses().size()); + inf.remove().handle(seq::next); + }).then(seq -> { + assertEquals(1, mgr.getKnownProcesses().size()); + assertEquals(1, mgr.currentProcess().getId()); + seq.exit(); + }).finish()); + assertEquals(Set.of(1), mgr.getKnownProcesses().keySet()); + } + } + + @Test + public void testRemoveCurrentProcess() throws Throwable { + try (DbgManager mgr = DbgManager.newInstance()) { + List selEvtIds = new ArrayList<>(); + mgr.addEventsListener(new DbgEventsListenerAdapter() { + @Override + public void processSelected(DbgProcess process, DbgCause cause) { + selEvtIds.add(process.getId()); + } + }); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + assertEquals(List.of(1), selEvtIds); + mgr.addProcess().handle(seq::nextIgnore); + }).then(seq -> { + assertEquals(2, mgr.getKnownProcesses().size()); + mgr.currentProcess().remove().handle(seq::next); + }).then(seq -> { + assertEquals(1, mgr.getKnownProcesses().size()); + assertEquals(2, mgr.currentProcess().getId()); + seq.exit(); + }).finish()); + assertEquals(Set.of(2), mgr.getKnownProcesses().keySet()); + assertEquals(List.of(1, 2), selEvtIds); + } + } + + @Test + public void testConsoleCapture() throws Throwable { + try (DbgManager mgr = DbgManager.newInstance()) { + String out = waitOn(sequence(TypeSpec.STRING).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.consoleCapture("echo test").handle(seq::exit); + }).finish()); + assertEquals("test", out.trim()); + } + } + + @Test + public void testListProcesses() throws Throwable { + try (DbgManager mgr = DbgManager.newInstance()) { + Map processes = + waitOn(sequence(TypeSpec.obj((Map) null)).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.listProcesses().handle(seq::exit); + }).finish()); + assertEquals(new HashSet<>(Arrays.asList(new Integer[] { 1 })), processes.keySet()); + } + } + + @Test + public void testListAvailableProcesses() throws Throwable { + try (DbgManager mgr = DbgManager.newInstance()) { + List> pids = + waitOn(sequence(TypeSpec.obj((List>) null)).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.listAvailableProcesses().handle(seq::exit); + }).finish()); + assertTrue(pids.get(0).getLeft().equals(0)); // Weak check, but on Linux, 1 (init) is always running + } + } + + @Test + public void testStart() throws Throwable { + try (DbgManager mgr = DbgManager.newInstance()) { + DbgThread thread = waitOn(sequence(TypeSpec.cls(DbgThread.class)).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.currentProcess().fileExecAndSymbols("/usr/bin/echo").handle(seq::next); + }).then(seq -> { + mgr.console("break main").handle(seq::next); + }).then(seq -> { + mgr.currentProcess().run().handle(seq::exit); + }).finish()); + assertNotNull(thread.getProcess().getPid()); + } + } + + @Test + public void testAttachDetach() throws Throwable { + try (DummyProc echo = runProc("dd"); DbgManager mgr = DbgManager.newInstance()) { + AtomicReference> threads = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.currentProcess().attach((int) echo.pid).handle(seq::next); + }, threads).then(seq -> { + // Attach stops the process, so no need to wait for STOPPED or prompt + AsyncFence fence = new AsyncFence(); + for (DbgThread t : threads.get()) { + assertEquals(echo.pid, (long) t.getProcess().getPid()); + fence.include(t.detach()); + } + fence.ready().handle(seq::exit); + }).finish()); + } + } + + public void stupidSleep(long millis) { + try { + Thread.sleep(millis); + } + catch (InterruptedException e) { + // Whatever + } + } + + @Test + public void testInsertListDeleteBreakpoint() throws Throwable { + AtomicReference breakpoint = new AtomicReference<>(); + try (DbgManager mgr = DbgManager.newInstance()) { + waitOn(sequence(TypeSpec.VOID).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.currentProcess().fileExecAndSymbols("/usr/bin/echo").handle(seq::next); + }).then(seq -> { + mgr.insertBreakpoint("main").handle(seq::next); + }, breakpoint).then(seq -> { + mgr.listBreakpoints().handle(seq::next); + }, TypeSpec.obj((Map) null)).then((bl, seq) -> { + assertEquals(Map.of(1L, breakpoint.get()), bl); + mgr.deleteBreakpoints(breakpoint.get().getNumber()).handle(seq::next); + }).then(seq -> { + mgr.listBreakpoints().handle(seq::next); + }, TypeSpec.obj((Map) null)).then((bl, seq) -> { + assertEquals(Map.of(), bl); + seq.exit(); + }).finish()); + } + } + + @Test + public void testListReadWriteReadRegisters() throws Throwable { + AtomicReference thread = new AtomicReference<>(); + AtomicReference regs = new AtomicReference<>(); + AtomicReference> read = new AtomicReference<>(); + try (DbgManager mgr = DbgManager.newInstance()) { + waitOn(sequence(TypeSpec.VOID).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.currentProcess().fileExecAndSymbols("/usr/bin/echo").handle(seq::next); + }).then(seq -> { + mgr.insertBreakpoint("main").handle(seq::nextIgnore); + }).then(seq -> { + mgr.currentProcess().run().handle(seq::next); + }, thread).then(seq -> { + mgr.waitForState(DbgState.STOPPED).handle(seq::next); + }).then(seq -> { + mgr.waitForPrompt().handle(seq::next); + }).then(seq -> { + thread.get().listRegisters().handle(seq::next); + }, regs).then(seq -> { + Set toRead = new HashSet<>(); + toRead.add(regs.get().get("eflags")); + toRead.add(regs.get().get("rax")); + thread.get().readRegisters(toRead).handle(seq::next); + }, read).then(seq -> { + // Verify eflags is rendered numerically + assertNotNull(read.get().get(regs.get().get("eflags"))); + assertNotNull(read.get().get(regs.get().get("rax"))); + Map toWrite = new HashMap<>(); + // NOTE: Not all flags are mutable from user-space. + // Turns out GDB/MI does not honor this, but CLI does.... + toWrite.put(regs.get().get("eflags"), BigInteger.valueOf(0L)); + toWrite.put(regs.get().get("rax"), BigInteger.valueOf(0x1122334455667788L)); + thread.get().writeRegisters(toWrite).handle(seq::next); + }).then(seq -> { + Set toRead = new HashSet<>(); + toRead.add(regs.get().get("eflags")); + // Verify register structure is reflected in API + toRead.add(regs.get().get("eax")); + thread.get().readRegisters(toRead).handle(seq::next); + }, read).then(seq -> { + // IF and that other reserved bit cannot be cleared + // Verified the same behavior in vanilla GDB at the CLI. + assertEquals(0x202L, read.get().get(regs.get().get("eflags")).longValue()); + assertEquals(0x55667788L, read.get().get(regs.get().get("eax")).longValue()); + seq.exit(); + }).finish()); + } + } + + @Test + public void testWriteReadMemory() throws Throwable { + AtomicReference thread = new AtomicReference<>(); + AtomicLong addr = new AtomicLong(); + ByteBuffer rBuf = ByteBuffer.allocate(1024); + try (DbgManager mgr = DbgManager.newInstance()) { + waitOn(sequence(TypeSpec.VOID).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.currentProcess().fileExecAndSymbols("/usr/bin/echo").handle(seq::next); + }).then(seq -> { + mgr.insertBreakpoint("main").handle(seq::nextIgnore); + }).then(seq -> { + mgr.currentProcess().run().handle(seq::next); + }, thread).then(seq -> { + mgr.waitForState(DbgState.STOPPED).handle(seq::next); + }).then(seq -> { + mgr.waitForPrompt().handle(seq::next); + }).then(seq -> { + mgr.currentProcess().evaluate("(long)main").handle(seq::next); + }, TypeSpec.STRING).then((str, seq) -> { + addr.set(Long.parseLong(str)); + ByteBuffer buf = ByteBuffer.allocate(1024); + buf.order(ByteOrder.LITTLE_ENDIAN); + for (int i = 0; i < 10; i++) { + buf.putInt(i); + } + buf.flip(); + thread.get().writeMemory(addr.get(), buf).handle(seq::next); + }).then(seq -> { + thread.get().readMemory(addr.get(), rBuf).handle(seq::next); + }, TypeSpec.obj((RangeSet) null)).then((rng, seq) -> { + rBuf.flip(); + rBuf.order(ByteOrder.LITTLE_ENDIAN); + RangeSet exp = TreeRangeSet.create(); + exp.add(Range.closedOpen(addr.get(), addr.get() + 1024)); + assertEquals(exp, rng); + for (int i = 0; i < 10; i++) { + assertEquals(i, rBuf.getInt()); + } + seq.exit(); + }).finish()); + } + } + + @Test + public void testContinue() throws Throwable { + AtomicReference thread = new AtomicReference<>(); + try (DbgManager mgr = DbgManager.newInstance()) { + waitOn(sequence(TypeSpec.VOID).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.currentProcess().fileExecAndSymbols("/usr/bin/echo").handle(seq::next); + }).then(seq -> { + mgr.insertBreakpoint("main").handle(seq::nextIgnore); + }).then(seq -> { + mgr.currentProcess().run().handle(seq::next); + }, thread).then(seq -> { + mgr.waitForState(DbgState.STOPPED).handle(seq::next); + }).then(seq -> { + mgr.waitForPrompt().handle(seq::next); + }).then(seq -> { + thread.get().cont().handle(seq::next); + }).then(seq -> { + mgr.waitForState(DbgState.STOPPED).handle(seq::next); + }).then(seq -> { + assertEquals(0L, (long) mgr.currentProcess().getExitCode()); + seq.exit(); + }).finish()); + } + } + + @Test + public void testStep() throws Throwable { + AtomicReference thread = new AtomicReference<>(); + try (DbgManager mgr = DbgManager.newInstance()) { + waitOn(sequence(TypeSpec.VOID).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.currentProcess().fileExecAndSymbols("/usr/bin/echo").handle(seq::next); + }).then(seq -> { + mgr.insertBreakpoint("main").handle(seq::nextIgnore); + }).then(seq -> { + mgr.currentProcess().run().handle(seq::next); + }, thread).then(seq -> { + mgr.waitForState(DbgState.STOPPED).handle(seq::next); + }).then(seq -> { + mgr.waitForPrompt().handle(seq::next); + }).then(seq -> { + thread.get().step(ExecSuffix.NEXT_INSTRUCTION).handle(seq::next); + }).then(seq -> { + mgr.waitForState(DbgState.STOPPED).handle(seq::next); + }).then(seq -> { + assertNull(mgr.currentProcess().getExitCode()); + seq.exit(); + }).finish()); + } + } + + @Test + public void testThreadSelect() throws Throwable { + AtomicReference thread = new AtomicReference<>(); + try (DbgManager mgr = DbgManager.newInstance()) { + waitOn(sequence(TypeSpec.VOID).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.currentProcess().fileExecAndSymbols("/usr/bin/echo").handle(seq::next); + }).then(seq -> { + mgr.insertBreakpoint("main").handle(seq::nextIgnore); + }).then(seq -> { + mgr.currentProcess().run().handle(seq::next); + }, thread).then(seq -> { + mgr.waitForState(DbgState.STOPPED).handle(seq::next); + }).then(seq -> { + mgr.waitForPrompt().handle(seq::next); + }).then(seq -> { + thread.get().select().handle(seq::next); + }).finish()); + } + } + + @Test + public void testListFrames() throws Throwable { + AtomicReference thread = new AtomicReference<>(); + try (DbgManager mgr = DbgManager.newInstance()) { + waitOn(sequence(TypeSpec.VOID).then(seq -> { + startManager(mgr).handle(seq::next); + }).then(seq -> { + mgr.currentProcess().fileExecAndSymbols("/usr/bin/echo").handle(seq::next); + }).then(seq -> { + mgr.insertBreakpoint("main").handle(seq::nextIgnore); + }).then(seq -> { + mgr.currentProcess().run().handle(seq::next); + }, thread).then(seq -> { + mgr.waitForState(DbgState.STOPPED).handle(seq::next); + }).then(seq -> { + mgr.waitForPrompt().handle(seq::next); + }).then(seq -> { + mgr.insertBreakpoint("write").handle(seq::nextIgnore); + }).then(seq -> { + mgr.currentProcess().cont().handle(seq::next); + }).then(seq -> { + mgr.waitForState(DbgState.STOPPED).handle(seq::next); + }).then(seq -> { + thread.get().listStackFrames().handle(seq::next); + }, TypeSpec.cls(DbgStackFrame.class).list()).then((stack, seq) -> { + Msg.debug(this, "Got stack:"); + for (DbgStackFrame frame : stack) { + Msg.debug(this, " " + frame); + } + assertEquals("write", stack.get(0).getFunction()); + assertEquals("main", stack.get(stack.size() - 1).getFunction()); + seq.exit(); + }).finish()); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/SpawnedDbgManagerTest.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/SpawnedDbgManagerTest.java new file mode 100644 index 0000000000..92da464037 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/SpawnedDbgManagerTest.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.DbgManager; + +public class SpawnedDbgManagerTest extends AbstractDbgManagerTest { + @Override + protected CompletableFuture startManager(DbgManager manager) { + return manager.start(new String[] {}); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/AbstractModelForDbgTest.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/AbstractModelForDbgTest.java new file mode 100644 index 0000000000..b028d4de55 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/AbstractModelForDbgTest.java @@ -0,0 +1,1070 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model; + +import static agent.dbgeng.testutil.DummyProc.runProc; +import static org.junit.Assert.*; + +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import org.junit.*; + +import agent.dbgeng.dbgeng.DbgEngTest; +import agent.dbgeng.model.iface1.DbgModelTargetLauncher; +import agent.dbgeng.testutil.DummyProc; +import ghidra.async.*; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.DebugModelConventions.AllRequiredAccess; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.attributes.TargetObjectRefList; +import ghidra.dbg.error.DebuggerModelNoSuchPathException; +import ghidra.dbg.error.DebuggerModelTypeException; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibility; +import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.dbg.target.TargetConsole.Channel; +import ghidra.dbg.target.TargetFocusScope.TargetFocusScopeListener; +import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.target.TargetObject.TargetObjectListener; +import ghidra.dbg.util.AllTargetObjectListenerAdapter; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; +import ghidra.util.*; + +public abstract class AbstractModelForDbgTest + extends AbstractGhidraHeadlessIntegrationTest { + protected static final Map AMD64_TEST_REG_VALUES = Map.of( // + "rax", NumericUtilities.convertStringToBytes("0123456789abcdef"), // + "ymm0", NumericUtilities.convertStringToBytes("" + // + "0011223344556677" + "8899aabbccddeeff")); +// TODO: for some reason, the dbgeng thinks ymm0 is VECTOR128 +//"0123456789abcdef" + "fedcba9876543210" + "0011223344556677" + "8899aabbccddeeff")); + protected static final long TIMEOUT_MILLISECONDS = + SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE; + + protected static Map hexlify(Map map) { + return map.entrySet() + .stream() + .collect(Collectors.toMap(Entry::getKey, + e -> NumericUtilities.convertBytesToString(e.getValue()))); + } + + public interface ModelHost extends AutoCloseable { + DebuggerObjectModel getModel(); + + CompletableFuture init(); + } + + protected abstract ModelHost modelHost() throws Exception; + + protected static class CatchOffThread implements AutoCloseable { + protected Throwable caught; + + void catching(Runnable runnable) { + try { + runnable.run(); + } + catch (Throwable e) { + caught = e; + } + } + + @Override + public void close() throws Exception { + if (caught != null) { + throw new AssertionError("Off-thread exception", caught); + } + } + } + + protected static T waitOn(CompletableFuture future) throws Throwable { + try { + return future.get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); + } + catch (ExecutionException e) { + throw e.getCause(); + } + } + + @Before + public void setUpDbgEngTest() { + DbgEngTest.assumeDbgengDLLLoadable(); + } + + @Test + public void testInitFinish() throws Throwable { + try (ModelHost m = modelHost()) { + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).finish()); + } + } + + @Test + public void testNonExistentPathGivesNull() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + model.fetchModelObject("Doesn't exist").handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + assertNull(obj); + seq.exit(); + }).finish()); + } + } + + @Test + public void testSessionLaunch() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference> launcher = new AtomicReference<>(); + TypeSpec> t = TypeSpec.auto(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) + .handle(seq::next); + }, launcher).then(seq -> { + Msg.debug(this, "Launching..."); + launcher.get().launch("notepad", "junk.txt").handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting Processes (after launch)..."); + model.fetchObjectElements(List.of("Sessions", "[0]")) + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, t).then((children, seq) -> { + Msg.debug(this, "Processes after: " + children); + assertEquals(1, children.size()); + seq.exit(); + }).finish()); + } + } + + @Test + public void testProcessLaunch() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference session = new AtomicReference<>(); + AtomicReference rootAccess = new AtomicReference<>(); + AtomicReference> launcher = new AtomicReference<>(); + AtomicReference launcherAccess = new AtomicReference<>(); + TypeSpec> t = TypeSpec.auto(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, rootAccess).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + rootAccess.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + model.fetchModelObject(List.of("Sessions", "[0]")).handle(seq::next); + }, session).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, + session.get()).handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + assertTrue(obj.getInterfaceNames().contains("Launcher")); + launcher.set((DbgModelTargetLauncher) obj); + Msg.debug(this, "Tracking process access..."); + DebugModelConventions.trackAccessibility(obj).handle(seq::next); + }, launcherAccess).then(seq -> { + Msg.debug(this, "Waiting for process access..."); + launcherAccess.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + launcher.get().launch("notepad", "junk.txt").handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Waiting for session access (again)..."); + rootAccess.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting Processes (after launch)..."); + model.fetchObjectElements(List.of("Sessions", "[0]", "Processes")) + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, t).then((elements, seq) -> { + Msg.debug(this, "Processes after: " + elements); + assertEquals(1, elements.size()); + seq.exit(); + }).finish()); + } + } + + @Test + public void testListAvailableProcesses() throws Throwable { + try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + model.fetchObjectElements(List.of("Sessions", "[0]", "Available")) + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((available, seq) -> { + assertTrue(available.containsKey(Long.toString(np.pid))); + seq.exit(); + }).finish()); + } + } + + @Test + public void testListProcesses() throws Throwable { + try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference proc = new AtomicReference<>(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) + .handle( + seq::next); + }, proc).then(seq -> { + Msg.debug(this, "Attaching to bogus path..."); + TargetAttacher attacher = proc.get().as(TargetAttacher.tclass); + attacher.attach( + model.createRef("Sessions", "[0]", "Available", "[" + np.pid + "]") + .as( + TargetAttachable.tclass)) + .handle( + seq::nextIgnore); + }).then(seq -> { + model.fetchObjectElements(List.of("Sessions", "[0]", "Processes")) + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + // NB: listProcesses will fail if no process is being debugged + }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((processes, seq) -> { + assertEquals(1, processes.size()); + seq.exit(); + }).finish()); + } + } + + @Test + public void testSessionAttachKill() throws Throwable { + try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference> attacher = new AtomicReference<>(); + AtomicReference> attachable = new AtomicReference<>(); + TypeSpec> t = TypeSpec.auto(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { +// Msg.debug(this, "Getting Processes (before attach)..."); +// model.getObjectElements(List.of("Sessions", "[0]", "Processes")).handle(seq::next); +// }, t).then((children, seq) -> { +// Msg.debug(this, "Processes before: " + children); +// assertEquals(1, children.size()); + + AsyncFence fence = new AsyncFence(); + Msg.debug(this, "Finding TargetAttacher..."); + fence.include(DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) + .thenAccept(a -> { + Msg.debug(this, " Got TargetAttacher: " + a); + attacher.set(a); + })); + fence.include(model.fetchModelObject("Sessions", "[0]", "Available", + "[" + np.pid + "]") + .thenAccept(o -> { + Msg.debug(this, " Got Attachable: " + o); + assertTrue(o.getInterfaceNames().contains("Attachable")); + attachable.set((TargetAttachable) o); + })); + fence.ready().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Attaching..."); + attacher.get().attach(attachable.get()).handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Waiting for session access (again)..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting Processes (after attach)..."); + model.fetchObjectElements(List.of("Sessions", "[0]", "Processes")) + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, t).then((elements, seq) -> { + Msg.debug(this, "Processes after: " + elements); + assertEquals(1, elements.size()); + Msg.debug(this, "Killing..."); + TargetObject attached = elements.get("0"); + assertTrue(attached.getInterfaceNames().contains("Killable")); + TargetKillable killable = (TargetKillable) attached; + killable.kill().handle(seq::next); + }).finish()); + } + } + + @Test + public void testProcessAttachKill() throws Throwable { + try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference obj = new AtomicReference<>(); + TypeSpec> t = TypeSpec.auto(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) + .handle( + seq::next); + }, obj).then(seq -> { + Msg.debug(this, "Attaching..."); + TargetAttacher attacher = obj.get().as(TargetAttacher.tclass); + attacher.attach(model.createRef("Sessions", "[0]", "Available", "[" + np.pid + "]") + .as(TargetAttachable.tclass)) + .handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Waiting for session access (again, again)..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting Processes (after attach)..."); + model.fetchObjectElements(List.of("Sessions", "[0]", "Processes")) + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, t).then((elements, seq) -> { + Msg.debug(this, "Processes after: " + elements); + assertEquals(1, elements.size()); + Msg.debug(this, "Killing..."); + TargetObject attached = elements.get("0"); + assertTrue(attached.getInterfaceNames().contains("Killable")); + TargetKillable killable = (TargetKillable) attached; + killable.kill().handle(seq::next); + }).finish()); + } + } + + @Test + public void testProcessAttachContKill() throws Throwable { + try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference obj = new AtomicReference<>(); + TypeSpec> t = TypeSpec.auto(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) + .handle( + seq::next); + }, obj).then(seq -> { + Msg.debug(this, "Attaching..."); + TargetAttacher attacher = obj.get().as(TargetAttacher.tclass); + attacher.attach( + model.createRef("Sessions", "[0]", "Available", "[" + np.pid + "]") + .as( + TargetAttachable.tclass)) + .handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Waiting for session access (again, again)..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting Process 1..."); + model.fetchModelObject("Sessions", "[0]", "Processes", "[0]").handle(seq::next); + }, obj).then(seq -> { + Msg.debug(this, "Resuming..."); + TargetResumable resumable = obj.get().as(TargetResumable.tclass); + resumable.resume().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for session access (after resume)..."); + access.get().waitValue(TargetAccessibility.INACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting Processes (after attach)..."); + model.fetchObjectElements(List.of("Sessions", "[0]", "Processes")) + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, t).then((elements, seq) -> { + Msg.debug(this, "Processes after: " + elements); + assertEquals(1, elements.size()); + Msg.debug(this, "Killing..."); + TargetObject attached = elements.get("0"); + assertTrue(attached.getInterfaceNames().contains("Killable")); + TargetKillable killable = (TargetKillable) attached; + killable.kill().handle(seq::nextIgnore); + }).finish()); + } + } + + @Test + public void testLaunchContExit() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference obj = new AtomicReference<>(); + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) + .handle(seq::next); + }, obj).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = obj.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "notepad junk.txt")) + .handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Waiting for session access (again)..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting Process 1..."); + model.fetchModelObject("Sessions", "[0]", "Processes", "[0]").handle(seq::next); + }, obj).then(seq -> { + Msg.debug(this, "Resuming..."); + TargetResumable resumable = obj.get().as(TargetResumable.tclass); + resumable.resume().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for session access (after resume)..."); + access.get().waitValue(TargetAccessibility.INACCESSIBLE).handle(seq::next); + }).finish()); + } + } + + @Test(expected = DebuggerModelNoSuchPathException.class) + public void testAttachNoObjectErr() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference proc = new AtomicReference<>(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) + .handle( + seq::next); + }, proc).then(seq -> { + Msg.debug(this, "Attaching to bogus path..."); + TargetAttacher attacher = proc.get().as(TargetAttacher.tclass); + attacher.attach(model.createRef("Sessions", "[0]", "Available", "Process -1") + .as(TargetAttachable.tclass)) + .handle(seq::nextIgnore); + }).finish()); + } + } + + @Test(expected = DebuggerModelTypeException.class) + public void testAttachNonAttachableErr() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference proc = new AtomicReference<>(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) + .handle( + seq::next); + }, proc).then(seq -> { + Msg.debug(this, "Attaching to bogus path..."); + TargetAttacher attacher = proc.get().as(TargetAttacher.tclass); + attacher.attach(model.createRef("Sessions", "[0]", "Available") + .as( + TargetAttachable.tclass)) + .handle(seq::nextIgnore); + }).finish()); + fail("Exception expected"); + } + } + + @Test + @Ignore("for developer workstation") + public void stressTestExecute() throws Throwable { + for (int i = 0; i < 100; i++) { + testExecute(); + } + } + + //@Test + public void testExecute() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AsyncReference lastOut = new AsyncReference<>(); + + AllTargetObjectListenerAdapter l = new AllTargetObjectListenerAdapter() { + @Override + public void consoleOutput(TargetObject interpreter, Channel channel, + String out) { + Msg.debug(this, "Got " + channel + " output: " + out); + lastOut.set(out, null); + } + }; + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object..."); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + root.get().addListener(l); + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Running command..."); + TargetInterpreter interpreter = root.get().as(TargetInterpreter.tclass); + interpreter.execute(".echo xyzzy").handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for expected output..."); + lastOut.waitValue("xyzzy\n").handle(seq::next); + }).finish()); + } + } + + @Test + public void testExecuteCapture() throws Throwable { + try (ModelHost m = modelHost(); CatchOffThread offThread = new CatchOffThread()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + + AllTargetObjectListenerAdapter l = new AllTargetObjectListenerAdapter() { + @Override + public void consoleOutput(TargetObject interpreter, Channel channel, + String out) { + Msg.debug(this, "Got " + channel + " output: " + out); + if (!out.contains("test")) { + return; + } + offThread.catching(() -> fail("Unexpected output:" + out)); + } + }; + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object..."); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + root.get().addListener(l); + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Running command with capture..."); + TargetInterpreter interpreter = root.get().as(TargetInterpreter.tclass); + interpreter.executeCapture(".echo xyzzy").handle(seq::next); + }, TypeSpec.STRING).then((out, seq) -> { + Msg.debug(this, "Captured: " + out); + assertTrue(out.contains("xyzzy")); + seq.exit(); + }).finish()); + } + } + + @Test + public void testGetBreakKinds() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference> breaks = new AtomicReference<>(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object..."); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding breakpoint container..."); + DebugModelConventions.findSuitable(TargetBreakpointContainer.class, root.get()) + .handle(seq::next); + }, breaks).then(seq -> { + Msg.debug(this, "Got: " + breaks); + TargetBreakpointKindSet kinds = breaks.get().getSupportedBreakpointKinds(); + Msg.debug(this, "Supports: " + kinds); + assertEquals(4, kinds.size()); + seq.exit(); + }).finish()); + } + } + + public static final TypeSpec>> BL_COL_SPEC = + null; + + @Test + public void testPlaceBreakpoint() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference> launcher = new AtomicReference<>(); + AtomicReference> breaks = new AtomicReference<>(); + AtomicReference> eff = new AtomicReference<>(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object..."); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) + .handle( + seq::next); + }, launcher).then(seq -> { + Msg.debug(this, "Launching..."); + launcher.get() + .launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "notepad junk.txt")) + .handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Finding breakpoint container..."); + DebugModelConventions.findSuitable(TargetBreakpointContainer.class, root.get()) + .handle(seq::next); + }, breaks).then(seq -> { + Msg.debug(this, "Placing breakpoint..."); + breaks.get() + .placeBreakpoint("0x7ff7d52c8987", + Set.of(TargetBreakpointKind.SOFTWARE)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting breakpoint specs..."); + breaks.get() + .fetchElements() + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((specs, seq) -> { + Msg.debug(this, "Got specs: " + specs); + assertEquals(1, specs.size()); + TargetBreakpointSpec spec = specs.get("0").as(TargetBreakpointSpec.tclass); + spec.getLocations().handle(seq::next); + }, BL_COL_SPEC).then((es, seq) -> { + Msg.debug(this, "Got effectives: " + es); + assertEquals(1, es.size()); + eff.set(es.iterator().next()); + Address addr = eff.get().getAddress(); + Msg.debug(this, "Got address: " + addr); + TargetObjectRefList list = eff.get().getAffects(); + Msg.debug(this, "Got affects: " + list); + assertEquals(1, list.size()); + seq.exit(); + }).finish()); + } + } + + @Test + public void testStack() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference> breaks = new AtomicReference<>(); + AtomicReference> launcher = new AtomicReference<>(); + AtomicReference obj = new AtomicReference<>(); + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) + .handle( + seq::next); + }, launcher).then(seq -> { + Msg.debug(this, "Launching..."); + launcher.get().launch("notepad", "junk.txt").handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Waiting for session access (again)..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding breakpoint container..."); + DebugModelConventions.findSuitable(TargetBreakpointContainer.class, root.get()) + .handle(seq::next); + }, breaks).then(seq -> { + Msg.debug(this, "Placing breakpoint..."); + breaks.get() + .placeBreakpoint("0x7ff7d52c8987", + Set.of(TargetBreakpointKind.SOFTWARE)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting Process 1..."); + model.fetchModelObject("Sessions", "[0]", "Processes", "[0]").handle(seq::next); + }, obj).then(seq -> { + Msg.debug(this, "Resuming..."); + TargetResumable resumable = obj.get().as(TargetResumable.tclass); + resumable.resume().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for session access (after resume)..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + obj.get() + .fetchSubElements("Threads", "[0]", "Stack") + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((frames, seq) -> { + Msg.debug(this, "Got stack:"); + for (Map.Entry ent : frames.entrySet()) { + TargetStackFrame frame = ent.getValue().as(TargetStackFrame.tclass); + Msg.debug(this, ent.getKey() + ": " + frame.getProgramCounter()); + } + long offset = frames.get("0") + .getTypedAttributeNowByName("pc", Address.class, + null) + .getOffset(); + assertEquals(0x7fffadd6e890L, offset); + //assertEquals("main", frames.get("" + (frames.size() - 1)) + // .getTypedAttributeNowByName("function", String.class, null)); + seq.exit(); + }).finish()); + } + } + + @Test + public void testRegisters() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference> launcher = new AtomicReference<>(); + AtomicReference proc = new AtomicReference<>(); + AtomicReference> bank = new AtomicReference<>(); + Set> descs = new LinkedHashSet<>(); + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) + .handle( + seq::next); + }, launcher).then(seq -> { + Msg.debug(this, "Launching..."); + launcher.get().launch("notepad", "junk.txt").handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Waiting for session access (again)..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting Process 1..."); + model.fetchModelObject(List.of("Sessions", "[0]", "Processes", "[0]")) + .handle( + seq::next); + }, proc).then(seq -> { + proc.get().fetchSuccessor("Threads", "[0]", "Stack", "[0]").thenAccept(top -> { + bank.set(top.as(TargetRegisterBank.tclass)); + }).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Got bank: " + bank.get()); + Msg.debug(this, "Descriptions ref: " + bank.get().getDescriptions()); + bank.get().getDescriptions().fetch().handle(seq::next); + }, TypeSpec.cls(TargetRegisterContainer.wclass)).then((cont, seq) -> { + Msg.debug(this, "Register descriptions: " + cont); + cont.getRegisters().thenAccept(descs::addAll).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Elements: "); + for (TargetRegister reg : descs) { + Msg.debug(this, " " + reg.getIndex() + ": " + reg.getBitLength()); + } + bank.get().readRegisters(descs).handle(seq::next); + }, TypeSpec.map(String.class, byte[].class)).then((data, seq) -> { + Msg.debug(this, "Values: "); + for (Map.Entry ent : data.entrySet()) { + Msg.debug(this, " " + ent.getKey() + " = " + + NumericUtilities.convertBytesToString(ent.getValue())); + } + // TODO: Implement Environment, and port these tests + Msg.debug(this, "Writing two registers, general and vector"); + bank.get().writeRegistersNamed(AMD64_TEST_REG_VALUES).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Flushing cache"); + bank.get().invalidateCaches().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Re-reading values"); + // NOTE: Can't reliably step here since rax may be referenced by the instruction + bank.get().readRegistersNamed(AMD64_TEST_REG_VALUES.keySet()).handle(seq::next); + }, TypeSpec.map(String.class, byte[].class)).then((data, seq) -> { + assertEquals(hexlify(AMD64_TEST_REG_VALUES), hexlify(data)); + seq.exit(); + }).finish()); + } + } + + @Test + public void testFocusProcesses() throws Throwable { + try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AtomicReference root = new AtomicReference<>(); + AtomicReference> scope = new AtomicReference<>(); + AtomicReference access = new AtomicReference<>(); + AtomicReference processes = new AtomicReference<>(); + AtomicReference obj1 = new AtomicReference<>(); + AtomicReference obj2 = new AtomicReference<>(); + AsyncReference, Void> focusProcPath = new AsyncReference<>(); + AsyncReference processCount = new AsyncReference<>(); + + TargetObjectListener procListener = new TargetObjectListener() { + @Override + public void elementsChanged(TargetObject parent, Collection removed, + Map added) { + processCount.set(processes.get().getCachedElements().size(), null); + } + }; + TargetFocusScopeListener focusListener = new TargetFocusScopeListener() { + @Override + public void focusChanged(TargetFocusScope object, TargetObjectRef focused) { + // Truncate the path to the parent process + focusProcPath.set(focused.getPath().subList(0, 2), null); + } + }; + + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting session root object"); + model.fetchModelObject("Sessions", "[0]").handle(seq::next); + }, root).then(seq -> { + scope.set(root.get().as(TargetFocusScope.tclass)); + scope.get().addListener(focusListener); + Msg.debug(this, "Tracking session access..."); + DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); + }, access).then(seq -> { + Msg.debug(this, "Waiting for session access..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) + .handle( + seq::next); + }, obj1).then(seq -> { + Msg.debug(this, "Attaching..."); + TargetAttacher attacher = obj1.get().as(TargetAttacher.tclass); + attacher.attach( + model.createRef("Sessions", "[0]", "Available", "[" + np.pid + "]") + .as( + TargetAttachable.tclass)) + .handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Getting processes container"); + model.fetchModelObject("Sessions", "[0]", "Processes").handle(seq::next); + }, processes).then(seq -> { + processes.get().addListener(procListener); + Msg.debug(this, "Finding TargetLauncher..."); + DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) + .handle( + seq::next); + }, obj2).then(seq -> { + Msg.debug(this, "Creating another process"); + TargetLauncher launcher = obj2.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "notepad junk.txt")) + .handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Waiting for session access (again)..."); + access.get().waitValue(TargetAccessibility.ACCESSIBLE).handle(seq::next); + }).then(seq -> { + assertTrue(PathUtils.isAncestor(List.of("Sessions", "[0]", "Processes", "[1]"), + scope.get().getFocus().getPath())); + // Redundant, but verifies the listener is keeping up + assertEquals(List.of("Sessions", "[0]", "Processes", "[1]"), focusProcPath.get()); + + Msg.debug(this, "Requesting focus on process 0"); + AsyncFence fence = new AsyncFence(); + TargetObjectRef i2 = model.createRef("Sessions", "[0]", "Processes", "[0]"); + fence.include(focusProcPath.waitValue(i2.getPath())); + fence.include(scope.get().requestFocus(i2)); + fence.ready().handle(seq::next); + }).then(seq -> { + assertTrue(PathUtils.isAncestor(List.of("Sessions", "[0]", "Processes", "[0]"), + scope.get().getFocus().getPath())); + // Redundant, but verifies the listener is keeping up + assertEquals(List.of("Sessions", "[0]", "Processes", "[0]"), focusProcPath.get()); + seq.exit(); + }).finish()); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/GadpForDbgTest.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/GadpForDbgTest.java new file mode 100644 index 0000000000..76326a626d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/GadpForDbgTest.java @@ -0,0 +1,125 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import agent.dbgeng.gadp.DbgEngGadpServer; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.gadp.client.GadpClient; +import ghidra.dbg.gadp.client.GadpClientTestHelper; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.util.Msg; + +public class GadpForDbgTest extends AbstractModelForDbgTest { + + static class DbgGadpModelHost implements ModelHost { + final DbgEngGadpServer server; + final SocketAddress addr; + final AsynchronousSocketChannel socket; + final GadpClient client; + + DbgGadpModelHost() throws Exception { + server = DbgEngGadpServer.newInstance(new InetSocketAddress("localhost", 0)); + server.startDbgEng(new String[] {}) + .get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); + addr = server.getLocalAddress(); + + socket = AsynchronousSocketChannel.open(); + client = new GadpClient("Test", socket); + } + + @Override + public CompletableFuture init() { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + AsyncUtils.completable(TypeSpec.VOID, socket::connect, addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).finish(); + } + + @Override + public DebuggerObjectModel getModel() { + return client; + } + + @Override + public void close() throws Exception { + // Not too eww + Msg.debug(this, "Disconnecting..."); + try { + waitOn(client.close()); + } + catch (Exception e) { + throw e; + } + catch (Throwable e) { + throw new AssertionError(e); + } + server.terminate(); + } + } + + @Override + protected DbgGadpModelHost modelHost() throws Exception { + return new DbgGadpModelHost(); + } + + @Test + public void testBadRequest() throws Throwable { + try (DbgGadpModelHost m = modelHost()) { + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Sending bogus message..."); + GadpClientTestHelper.sendChecked(m.client, Gadp.ErrorRequest.newBuilder(), null) + .handle(seq::nextIgnore); + }).finish()); + fail("Exception expected"); + } + catch (AssertionError e) { + assertEquals( + "Client implementation sent an invalid request: " + + "BAD_REQUEST: Unrecognized request: ERROR_REQUEST", + e.getMessage()); + } + } + + @Test + public void testPing() throws Throwable { + try (DbgGadpModelHost m = modelHost()) { + waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + m.init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Pinging..."); + m.client.ping("Hello, Ghidra Async Debugging!").handle(seq::next); + }).finish()); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/ModelForDbgTest.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/ModelForDbgTest.java new file mode 100644 index 0000000000..6aaf905bba --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/model/ModelForDbgTest.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.impl.DbgModelImpl; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.util.Msg; + +public class ModelForDbgTest extends AbstractModelForDbgTest { + + static class DbgGadpModelHost implements ModelHost { + final DbgModelImpl model; + + DbgGadpModelHost() { + model = new DbgModelImpl(); + } + + @Override + public CompletableFuture init() { + Msg.debug(this, "Starting dbgeng..."); + return model.startDbgEng(new String[] {}); + } + + @Override + public DebuggerObjectModel getModel() { + return model; + } + + @Override + public void close() throws Exception { + model.terminate(); + } + } + + @Override + protected ModelHost modelHost() throws Exception { + return new DbgGadpModelHost(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/testutil/DummyProc.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/testutil/DummyProc.java new file mode 100644 index 0000000000..1adb15354c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/testutil/DummyProc.java @@ -0,0 +1,80 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.testutil; + +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinNT.HANDLE; + +// TODO: Factor this with ghidra.dbg.util.DummyProc (Framework-Debugging) +// Need to work out OS-specific nuances. +public class DummyProc implements AutoCloseable { + final Process process; + public final long pid; + + static ProcessBuilder addWindows64Path(ProcessBuilder builder) { + builder.environment() + .put("PATH", + System.getenv("PATH") + ":" + System.getProperty("user.dir") + + "/build/os/linux64"); + return builder; + } + + public static DummyProc runProc(String... args) throws NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException, IOException { + DummyProc proc = new DummyProc(args); + return proc; + } + + DummyProc(String... args) throws IOException, NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException { + //args[0] = which(args[0]); + process = new ProcessBuilder(args).start(); + + @SuppressWarnings("hiding") + long pid = -1; + try { + //Field pidFld = process.getClass().getDeclaredField("pid"); + //pidFld.setAccessible(true); + //pid = pidFld.getLong(process); + Class cls = process.getClass(); + assumeTrue(cls.getName().equals("java.lang.ProcessImpl")); + Field handleFld = cls.getDeclaredField("handle"); + handleFld.setAccessible(true); + long handle = handleFld.getLong(process); + pid = Kernel32.INSTANCE.GetProcessId(new HANDLE(new Pointer(handle))); + } + catch (NoSuchFieldException | SecurityException e) { + assumeNoException(e); + } + this.pid = pid; + } + + @Override + public void close() throws Exception { + if (!process.destroyForcibly().waitFor(1000, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Could not terminate process " + pid); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/Module.manifest b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/Module.manifest new file mode 100644 index 0000000000..8f0afe45a1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/Module.manifest @@ -0,0 +1,2 @@ +MODULE FILE LICENSE: lib/jna-5.4.0.jar Apache License 2.0 +MODULE FILE LICENSE: lib/jna-platform-5.4.0.jar Apache License 2.0 diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/build.gradle b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/build.gradle new file mode 100644 index 0000000000..c676513403 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/build.gradle @@ -0,0 +1,13 @@ +apply from: "${rootProject.projectDir}/gradle/javaProject.gradle" +apply from: "${rootProject.projectDir}/gradle/jacocoProject.gradle" +apply from: "${rootProject.projectDir}/gradle/javaTestProject.gradle" +apply from: "${rootProject.projectDir}/gradle/distributableGhidraModule.gradle" + +apply plugin: 'eclipse' +eclipse.project.name = 'Debug Debugger-agent-dbgmodel-traceloader' + +dependencies { + compile project(":Debugger") + compile project(":Debugger-agent-dbgeng") + compile project(':Debugger-agent-dbgmodel') +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/certification.manifest b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/certification.manifest new file mode 100644 index 0000000000..337bafc340 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/certification.manifest @@ -0,0 +1,6 @@ +##VERSION: 2.0 +##MODULE IP: Apache License 2.0 +.classpath||NONE||reviewed||END| +.project||NONE||reviewed||END| +Module.manifest||GHIDRA||||END| +build.gradle||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/ghidra_scripts/PopulateTraceLocal.java b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/ghidra_scripts/PopulateTraceLocal.java new file mode 100644 index 0000000000..e4fc8ab489 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/ghidra_scripts/PopulateTraceLocal.java @@ -0,0 +1,409 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.io.File; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.*; + +import com.google.common.collect.Range; + +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgeng.dbgeng.DebugControl; +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.bridge.HostDataModelAccess; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.bridge.HDMAUtil; +import ghidra.app.script.GhidraScript; +import ghidra.app.services.DebuggerTraceManagerService; +import ghidra.program.model.address.*; +import ghidra.program.model.lang.*; +import ghidra.trace.database.DBTrace; +import ghidra.trace.model.Trace; +import ghidra.trace.model.memory.*; +import ghidra.trace.model.modules.TraceModule; +import ghidra.trace.model.modules.TraceModuleManager; +import ghidra.trace.model.thread.TraceThread; +import ghidra.trace.model.thread.TraceThreadManager; +import ghidra.trace.model.time.TraceTimeManager; +import ghidra.util.Msg; +import ghidra.util.database.UndoableTransaction; + +/** + * This script populates a trace database for demonstrations purposes and opens it in the current + * tool. + * + *

+ * Your current tool had better be the "TraceBrowser"! The demonstration serves two purposes. 1) It + * puts interesting data into the TraceBrowser and leaves some annotations as an exercise. 2) It + * demonstrates how a decent portion the Trace API works. + * + *

+ * A Trace is basically a collection of observations of memory and registers over the lifetime of an + * application or computer system. In Ghidra, the Trace object also supports many of the same + * annotations as does Program. In the same way that Program brings knowledge markup to an image of + * bytes, Trace brings knowledge markup to bytes observed over time. + * + *

+ * Effectively, if you take the cross-product of Program with time and add Threads, Breakpoints, + * etc., you get Trace. It's a lot. In order to use all the UI components which take a Program, + * Trace can present itself as a Program at a particular point in time. + * + *

+ * Each particular component will be introduced as its used in the script below, but for now some + * core concepts: + * + *

    + *
  • A point in time is called a "tick." These don't necessarily correspond to any real unit of + * time, though they may. The only requirement is that they are numbered in chronological + * order.
  • + *
  • Every annotation has a "lifespan," which is the range of ticks for which the annotation is + * effective. Some annotations may overlap, others may not. In general, if the corresponding concept + * in Program permits address overlap, then Trace permits both address and time overlap. If not, + * then neither is permitted. In essense, Trace defines overlap as the intersection of rectangles, + * where an annotation's X dimension is it's address range, and its Y dimension is its lifespan. + *
  • + *
  • Observations in memory happen at a particular tick and are assumed in effect until another + * observation changes that. To record the "freshness" of observations, the memory manager tags + * regions as KNOWN, UNKNOWN, or ERROR. An observation implicitly marks the affected region as + * KNOWN. The intent is to grey the background for regions where memory is UNKNOWN for the current + * tick.
  • + *
  • Observations of registers behave exactly the same as observations for memory, by leveraging + * Ghidra's "register space." The only difference is that those observations must be recorded with + * respect to a given thread. Each thread is effectively allocated its own copy of the register + * space. Most the the API components require you to obtain a special "register space" for a given + * thread before recording observations of or applying annotations to that thread.
  • + *
+ * + *

+ * After you've run this script, a trace should appear in the UI. Note that there is not yet a way + * to save a trace in the UI. As an exercise, try adding data units to analyze the threads' stacks. + * It may take some getting accustomed to, but the rules for laying down units should be very + * similar to those in a Program. However, the Trace must take the applied units and decide how far + * into the future they are effective. In general, it defaults to "from here on out." However, two + * conditions may cause the trace to choose an ending tick: 1) The underlying bytes change sometime + * in the future, and 2) There is an overlapping code unit sometime in the future. + * + *

+ * The trace chooses the latest tick possible preceding any byte change or existing code unit, so + * that the unit's underlying bytes remain constant for its lifespan, and the unit does not overlap + * any existing unit. This rule causes some odd behavior for null-terminated strings. I intend to + * adjust this rule slightly for static data types wrt/ byte changes. For those, the placed unit + * should be truncated as described above, however, another data unit of the same type can be placed + * at the change. The same rule is then applied iteratively into the future until an overlapping + * unit is encountered, or there are no remaining byte changes. + */ +public class PopulateTraceLocal extends GhidraScript { + + /** + * The Memory APIs all use Java NIO ByteBuffer. While it has it can sometimes be annoying, it + * provides most of the conveniences you'd need for packing arbitrary data into a memory buffer. + * I'll allocate one here large enough to write a couple values at a time. + */ + private ByteBuffer buf = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN); + + private Language lang; + private CompilerSpec cspec; + private Trace trace; + private TraceMemoryManager memory; + private TraceModuleManager modules; + private TraceThreadManager threads; + private TraceTimeManager timeManager; + + private AddressSpace defaultSpace; + + private DebuggerTraceManagerService manager; + + private HostDataModelAccess access; + private DebugClient client; + private DebugControl control; + private HDMAUtil util; + + private Set eventSnaps = new HashSet(); + + /** + * Create an address in the processor's (x86_64) default space. + * + * @param offset the byte offset + * @return the address + */ + protected Address addr(long offset) { + return defaultSpace.getAddress(offset); + } + + /** + * Create an address range in the processor's default space. + * + * @param min the minimum byte offset + * @param max the maximum (inclusive) byte offset + * @return the range + */ + protected AddressRange rng(long min, long max) { + return new AddressRangeImpl(addr(min), addr(max)); + } + + /** + * Get a register by name + * + * @param name the name + * @return the register + */ + protected Register reg(String name) { + return lang.getRegister(name); + } + + @Override + protected void run() throws Exception { + + access = DbgModel.debugCreate(); + client = access.getClient(); + control = client.getControl(); + util = new HDMAUtil(access); + + File f = askFile("Trace", "Load"); + + cspec = currentProgram.getCompilerSpec(); + lang = currentProgram.getLanguage(); + defaultSpace = lang.getAddressFactory().getDefaultAddressSpace(); + trace = new DBTrace(f.getName(), cspec, this); + + memory = trace.getMemoryManager(); + modules = trace.getModuleManager(); + threads = trace.getThreadManager(); + timeManager = trace.getTimeManager(); + + manager = state.getTool().getService(DebuggerTraceManagerService.class); + + client.openDumpFileWide(f.getAbsolutePath()); + control.waitForEvent(); + + try (UndoableTransaction tid = + UndoableTransaction.start(trace, "Populate Events", true)) { + + List children = + util.getElements(List.of("Debugger", "State", "DebuggerVariables", "curprocess", + "TTD", "Events")); + + Map maxPos = util.getAttributes( + List.of("Debugger", "State", "DebuggerVariables", "curprocess", "TTD", "Lifetime", + "MaxPosition")); + Long max = (Long) maxPos.get("Sequence").getValue(); + + for (ModelObject event : children) { + Map eventMap = event.getKeyValueMap(); + ModelObject pos = eventMap.get("Position"); + ModelObject seq = pos.getKeyValue("Sequence"); + //ModelObject step = pos.getKeyValue("Steps"); + ModelObject type = eventMap.get("Type"); + String display = type.getValueString(); + Long snap = (Long) seq.getValue(); + if (display.contains("ModuleLoaded") || display.contains("ModuleUnloaded")) { + ModelObject module = eventMap.get("Module"); + Map moduleMap = module.getKeyValueMap(); + ModelObject name = moduleMap.get("Name"); + ModelObject address = moduleMap.get("Address"); + ModelObject size = moduleMap.get("Size"); + String moduleId = name.getValueString(); + display += " " + moduleId; + Address base = + currentProgram.getAddressFactory().getAddress(address.getValueString()); + if (display.contains("ModuleLoaded")) { + Long start = (Long) address.getValue(); + Long sz = (Long) size.getValue(); + buf = ByteBuffer.allocate(sz.intValue()).order(ByteOrder.LITTLE_ENDIAN); + AddressRange rng = rng(start, start + sz - 1); + modules.addLoadedModule(moduleId, moduleId, rng, snap); + memory.addRegion(moduleId, Range.atLeast(snap), rng, + TraceMemoryFlag.READ, TraceMemoryFlag.WRITE, TraceMemoryFlag.EXECUTE); + try { + int read = + client.getDataSpaces().readVirtual(start, buf, sz.intValue()); + int written = memory.putBytes(snap, rng.getMinAddress(), buf.flip()); + if (read != written) { + Msg.debug(this, + "read:written=" + read + ":" + written + " at " + start); + } + } + catch (Exception e) { + System.err.println("Unable to read " + moduleId + " at " + snap); + } + } + else { + if (snap >= 0) { + Collection mods = + modules.getModulesByPath(moduleId); + Iterator iterator = mods.iterator(); + while (iterator.hasNext()) { + TraceModule next = iterator.next(); + next.setUnloadedSnap(snap); + } + } + } + } + else if (display.contains("ThreadCreated") || + display.contains("ThreadTerminated")) { + ModelObject thread = eventMap.get("Thread"); + //ModelObject uid = thread.getKeyValue("UniqueId"); + ModelObject id = thread.getKeyValue("Id"); + String threadId = id.getValueString(); + display += " " + threadId; + if (display.contains("ThreadCreated")) { + threads.addThread(threadId, Range.atLeast(snap)); + } + else { + if (snap >= 0) { + Collection thrs = + threads.getThreadsByPath(threadId); + Iterator iterator = thrs.iterator(); + while (iterator.hasNext()) { + TraceThread next = iterator.next(); + next.setDestructionSnap(snap); + } + } + } + } + if (snap < 0) { + snap = ++max; + } + timeManager.getSnapshot(snap, true).setDescription(display); + eventSnaps.add(snap); + } + } + + try (UndoableTransaction tid = + UndoableTransaction.start(trace, "Populate Registers", true)) { + //for (Long tick : tickManager.getAllTicks()) { + for (Long snap : eventSnaps) { + control.execute("!tt " + Long.toHexString(snap) + ":0"); + control.waitForEvent(); + + List modelThreads = + util.getElements( + List.of("Debugger", "State", "DebuggerVariables", "curprocess", "Threads")); + Map modelThreadMap = new HashMap(); + for (ModelObject obj : modelThreads) { + modelThreadMap.put(obj.getSearchKey(), obj); + } + + Collection liveThreads = threads.getLiveThreads(snap); + for (TraceThread thread : liveThreads) { + TraceMemoryRegisterSpace regspace = memory.getMemoryRegisterSpace(thread, true); + ModelObject modelThread = modelThreadMap.get("0x" + thread.getName()); + if (modelThread != null) { + Map registers = + modelThread.getKeyValue("Registers") + .getKeyValue( + "User") + .getKeyValueMap(); + for (String rname : registers.keySet()) { + ModelObject r = registers.get(rname); + Register reg = reg(rname.toUpperCase()); + if (reg != null) { + try { + regspace.setValue(snap, + new RegisterValue(reg, BigInteger.valueOf( + Long.parseUnsignedLong(r.getValueString(), 16)))); + } + catch (Exception e) { + System.err.println(rname + "<--" + r.getValueString()); + } + } + } + } + else { + System.err.println(thread.getName() + " not found!"); + } + + } + } + } + + /* + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Populate Heap", true)) { + ModelObject currentSession = util.getCurrentSession(); + ModelObject data = currentSession.getKeyValue("TTD").getKeyValue("Data"); + ModelMethod heap = data.getMethod("Heap"); + Pointer[] args = new Pointer[0]; + ModelObject ret = heap.call(data, 0, args); + List heapObjects = new ArrayList(); + for (ModelObject heapObj : ret.getElements()) { + Map heapMap = heapObj.getKeyValueMap(); + ModelObject address = heapMap.get("Address"); + ModelObject size = heapMap.get("Size"); + ModelObject timeStart = heapMap.get("TimeStart").getKeyValue("Sequence"); + ModelObject timeEnd = heapMap.get("TimeEnd").getKeyValue("Sequence"); + if (address == null) { + continue; + } + Long start = (Long) address.getValue(); + if (size == null) { + continue; + } + Long sz = (Long) size.getValue(); + if (sz == null) { + continue; + } + AddressRange rng = rng(start, start + sz); + String heapId = "Heap " + address.getValueString(); + Long startTick = (Long) timeStart.getValue(); + Long stopTick = (Long) timeEnd.getValue(); + Range interval = + (stopTick > 0) ? Range.open(startTick, stopTick) : Range.atLeast(startTick); + tickManager.setTick(startTick, heapId + " allocated"); + tickManager.setTick(stopTick, heapId + " freed"); + TraceMemoryRegion heapRegion = + memory.addRegion(heapId, interval, rng, + TraceMemoryFlag.READ, TraceMemoryFlag.WRITE, TraceMemoryFlag.EXECUTE); + heapObjects.add(heapRegion); + } + for (TraceMemoryRegion heapRegion : heapObjects) { + long startTick = heapRegion.getCreatedTick(); + try { + control.execute("!tt " + Long.toHexString(startTick) + ":0"); + control.waitForEvent(); + + AddressRange range = heapRegion.getRange(); + buf = + ByteBuffer.allocate((int) range.getLength()).order(ByteOrder.LITTLE_ENDIAN); + long start = range.getMinAddress().getOffset(); + int read = + client.getDataSpaces().readVirtual(start, buf, + (int) range.getLength()); + int written = memory.putBytes(startTick, range.getMinAddress(), buf.flip()); + if (read != written) { + Msg.debug(this, + "read:written=" + read + ":" + written + " at " + start); + } + } + catch (Exception e) { + System.err.println( + "Unable to read " + heapRegion.getName() + " at " + startTick); + } + } + } + */ + + /** + * Give a program view to Ghidra's program manager + * + * NOTE: Eventually, there will probably be a TraceManager service as well, but to use the + * familiar UI components, we generally take orders from the ProgramManager. + */ + manager.openTrace(trace); + manager.activateTrace(trace); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/ghidra_scripts/PopulateTraceRemote.java b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/ghidra_scripts/PopulateTraceRemote.java new file mode 100644 index 0000000000..26328d6be6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/ghidra_scripts/PopulateTraceRemote.java @@ -0,0 +1,174 @@ + +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.io.File; +import java.util.Set; + +import ghidra.app.script.GhidraScript; +import ghidra.app.services.DebuggerModelService; +import ghidra.app.services.DebuggerTraceManagerService; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.target.*; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.address.*; +import ghidra.program.model.lang.*; +import ghidra.trace.database.DBTrace; +import ghidra.trace.model.Trace; +import ghidra.trace.model.time.TraceTimeManager; +import ghidra.util.database.UndoableTransaction; + +/** + * This script populates a trace database for demonstrations purposes and opens it in the current + * tool. + * + *

+ * Your current tool had better be the "TraceBrowser"! The demonstration serves two purposes. 1) It + * puts interesting data into the TraceBrowser and leaves some annotations as an exercise. 2) It + * demonstrates how a decent portion the Trace API works. + * + *

+ * A Trace is basically a collection of observations of memory and registers over the lifetime of an + * application or computer system. In Ghidra, the Trace object also supports many of the same + * annotations as does Program. In the same way that Program brings knowledge markup to an image of + * bytes, Trace brings knowledge markup to bytes observed over time. + * + *

+ * Effectively, if you take the cross-product of Program with time and add Threads, Breakpoints, + * etc., you get Trace. It's a lot. In order to use all the UI components which take a Program, + * Trace can present itself as a Program at a particular point in time. + * + *

+ * Each particular component will be introduced as its used in the script below, but for now some + * core concepts: + * + *

    + *
  • A point in time is called a "tick." These don't necessarily correspond to any real unit of + * time, though they may. The only requirement is that they are numbered in chronological + * order.
  • + *
  • Every annotation has a "lifespan," which is the range of ticks for which the annotation is + * effective. Some annotations may overlap, others may not. In general, if the corresponding concept + * in Program permits address overlap, then Trace permits both address and time overlap. If not, + * then neither is permitted. In essense, Trace defines overlap as the intersection of rectangles, + * where an annotation's X dimension is it's address range, and its Y dimension is its lifespan. + *
  • + *
  • Observations in memory happen at a particular tick and are assumed in effect until another + * observation changes that. To record the "freshness" of observations, the memory manager tags + * regions as KNOWN, UNKNOWN, or ERROR. An observation implicitly marks the affected region as + * KNOWN. The intent is to grey the background for regions where memory is UNKNOWN for the current + * tick.
  • + *
  • Observations of registers behave exactly the same as observations for memory, by leveraging + * Ghidra's "register space." The only difference is that those observations must be recorded with + * respect to a given thread. Each thread is effectively allocated its own copy of the register + * space. Most the the API components require you to obtain a special "register space" for a given + * thread before recording observations of or applying annotations to that thread.
  • + *
+ * + *

+ * After you've run this script, a trace should appear in the UI. Note that there is not yet a way + * to save a trace in the UI. As an exercise, try adding data units to analyze the threads' stacks. + * It may take some getting accustomed to, but the rules for laying down units should be very + * similar to those in a Program. However, the Trace must take the applied units and decide how far + * into the future they are effective. In general, it defaults to "from here on out." However, two + * conditions may cause the trace to choose an ending tick: 1) The underlying bytes change sometime + * in the future, and 2) There is an overlapping code unit sometime in the future. + * + *

+ * The trace chooses the latest tick possible preceding any byte change or existing code unit, so + * that the unit's underlying bytes remain constant for its lifespan, and the unit does not overlap + * any existing unit. This rule causes some odd behavior for null-terminated strings. I intend to + * adjust this rule slightly for static data types wrt/ byte changes. For those, the placed unit + * should be truncated as described above, however, another data unit of the same type can be placed + * at the change. The same rule is then applied iteratively into the future until an overlapping + * unit is encountered, or there are no remaining byte changes. + */ +public class PopulateTraceRemote extends GhidraScript { + + private Language lang; + private CompilerSpec cspec; + private Trace trace; + private TraceTimeManager timeManager; + + private AddressSpace defaultSpace; + + private DebuggerTraceManagerService manager; + private DebuggerModelService targets; + + /** + * Create an address in the processor's (x86_64) default space. + * + * @param offset the byte offset + * @return the address + */ + protected Address addr(long offset) { + return defaultSpace.getAddress(offset); + } + + /** + * Create an address range in the processor's default space. + * + * @param min the minimum byte offset + * @param max the maximum (inclusive) byte offset + * @return the range + */ + protected AddressRange rng(long min, long max) { + return new AddressRangeImpl(addr(min), addr(max)); + } + + /** + * Get a register by name + * + * @param name the name + * @return the register + */ + protected Register reg(String name) { + return lang.getRegister(name); + } + + @Override + protected void run() throws Exception { + + File f = askFile("Trace", "Load"); + + cspec = currentProgram.getCompilerSpec(); + lang = currentProgram.getLanguage(); + defaultSpace = lang.getAddressFactory().getDefaultAddressSpace(); + trace = new DBTrace(f.getName(), cspec, this); + + PluginTool tool = state.getTool(); + manager = tool.getService(DebuggerTraceManagerService.class); + targets = tool.getService(DebuggerModelService.class); + + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Populate Events", true)) { + timeManager = trace.getTimeManager(); + timeManager.createSnapshot("init"); + } + + manager.openTrace(trace); + manager.activateTrace(trace); + + Set models = targets.getModels(); + DebuggerObjectModel model = (DebuggerObjectModel) models.toArray()[0]; + TargetInterpreter interpreter = + DebugModelConventions.findSuitable(TargetInterpreter.tclass, model.createRef()).get(); + interpreter.execute(".opendump " + f.getAbsolutePath()).get(); + interpreter.execute("g"); + TargetAttacher attacher = + DebugModelConventions.findSuitable(TargetAttacher.tclass, model.createRef()).get(); + // TODO: Is "Available" the correct path? + attacher.attach(model.createRef("Available", "[0]").as(TargetAttachable.tclass)).get(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/Module.manifest b/Ghidra/Debug/Debugger-agent-dbgmodel/Module.manifest new file mode 100644 index 0000000000..8f0afe45a1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/Module.manifest @@ -0,0 +1,2 @@ +MODULE FILE LICENSE: lib/jna-5.4.0.jar Apache License 2.0 +MODULE FILE LICENSE: lib/jna-platform-5.4.0.jar Apache License 2.0 diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle b/Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle new file mode 100644 index 0000000000..df64a13bfb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle @@ -0,0 +1,61 @@ +apply from: "$rootProject.projectDir/gradle/javaProject.gradle" +apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle" +apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" +apply from: "$rootProject.projectDir/gradle/nativeProject.gradle" +apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" + +apply plugin: 'eclipse' +eclipse.project.name = 'Debug Debugger-agent-dbgmodel' + +dependencies { + compile project(":Debugger-agent-dbgeng") + + testCompile project(path: ":Debugger-agent-dbgeng", configuration: 'testArtifacts') +} + +def boolean filterJar(File jarfile) { + if (jarfile.name.contains("gradle-api")) { + return false + } else if (jarfile.name.contains("groovy-all")) { + return false + } else if (jarfile.name.contains("gradle-installation-beacon")) { + return false + } + return true +} + +jar { + manifest { + attributes['Main-Class'] = 'agent.dbgmodel.gadp.DbgModelGadpServer' + } +} + +task configureNodepJar { + doLast { + configurations.runtime.files.forEach { + if (filterJar(it)) { + nodepJar.from(zipTree(it)) + } + } + } +} + +task nodepJar(type: Jar) { + inputs.file(file(jar.archivePath)) + dependsOn(configureNodepJar) + dependsOn(jar) + + appendix = 'nodep' + manifest { + attributes['Main-Class'] = 'agent.dbgmodel.gadp.DbgModelGadpServer' + } + + from(zipTree(jar.archivePath)) +} + +test { + jvmArgs('-Xrs') // TODO: Is this needed, or left over from trial-and-error + if ("win64".equals(getCurrentPlatformName())) { + dependsOn(":Framework-Debugging:testSpecimenWin64") + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/certification.manifest b/Ghidra/Debug/Debugger-agent-dbgmodel/certification.manifest new file mode 100644 index 0000000000..fc6bcc9c98 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/certification.manifest @@ -0,0 +1,8 @@ +##VERSION: 2.0 +##MODULE IP: Apache License 2.0 +.classpath||NONE||reviewed||END| +.project||NONE||reviewed||END| +Module.manifest||GHIDRA||||END| +build.gradle||GHIDRA||||END| +src/javaprovider/def/javaprovider.def||GHIDRA||||END| +src/javaprovider/rc/javaprovider.rc||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/cpp/javaprovider.cpp b/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/cpp/javaprovider.cpp new file mode 100644 index 0000000000..2bb02ec96b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/cpp/javaprovider.cpp @@ -0,0 +1,224 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define INITGUID + +#include +#include + +#include "resource.h" + +#define CHECK_RESULT(x, y) do { \ + HRESULT hr = (x); \ + if (hr != S_OK) { \ + fprintf(stderr, "HRESULT of %s = %x\n", ##x, hr); \ + return y; \ + } \ +} while (0) + +class EXT_CLASS : public ExtExtension { +public: + virtual HRESULT Initialize(); + virtual void Uninitialize(); + + //virtual void OnSessionAccessible(ULONG64 Argument); + + EXT_COMMAND_METHOD(java_add_cp); + EXT_COMMAND_METHOD(java_set); + EXT_COMMAND_METHOD(java_get); + EXT_COMMAND_METHOD(java_run); + + void run_command(PCSTR name); +}; + +EXT_DECLARE_GLOBALS(); + +JavaVM* jvm = NULL; +JNIEnv* env = NULL; +jclass clsCommands = NULL; + +char JDK_JVM_DLL_PATH[] = "\\jre\\bin\\server\\jvm.dll"; +char JRE_JVM_DLL_PATH[] = "\\bin\\server\\jvm.dll"; + +typedef jint (_cdecl *CreateJavaVMFunc)(JavaVM**, void**, void*); + +HRESULT EXT_CLASS::Initialize() { + HRESULT result = ExtExtension::Initialize(); + if (result != S_OK) { + return result; + } + + char* env_java_home = getenv("JAVA_HOME"); + if (env_java_home == NULL) { + fprintf(stderr, "JAVA_HOME is not set\n"); + fflush(stderr); + return E_FAIL; + } + char* java_home = strdup(env_java_home); + size_t home_len = strlen(java_home); + if (java_home[home_len - 1] == '\\') { + java_home[home_len - 1] = '\0'; + } + size_t full_len = home_len + sizeof(JDK_JVM_DLL_PATH); + char* full_path = new char[full_len]; + HMODULE jvmDll = NULL; + // Try the JRE path first; + strcpy_s(full_path, full_len, java_home); + strcat_s(full_path, full_len, JRE_JVM_DLL_PATH); + fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path); + fflush(stderr); + jvmDll = LoadLibraryA(full_path); + if (jvmDll == NULL) { + // OK, then try the JDK path + strcpy_s(full_path, full_len, java_home); + strcat_s(full_path, full_len, JDK_JVM_DLL_PATH); + fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path); + fflush(stderr); + jvmDll = LoadLibraryA(full_path); + } + free(full_path); + free(java_home); + if (jvmDll == NULL) { + fprintf(stderr, "Could not find the jvm.dll\n"); + fflush(stderr); + return E_FAIL; + } + fprintf(stderr, "Found it!\n"); + fflush(stderr); + + JavaVMOption options[2]; + JavaVMInitArgs vm_args = { 0 }; + vm_args.version = JNI_VERSION_1_8; + vm_args.nOptions = sizeof(options)/sizeof(options[0]); + vm_args.options = options; + options[0].optionString = "-Xrs"; + options[1].optionString = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"; + vm_args.ignoreUnrecognized = false; + CreateJavaVMFunc create_jvm = NULL; + //create_jvm = JNI_CreateJavaVM; + create_jvm = (CreateJavaVMFunc) GetProcAddress(jvmDll, "JNI_CreateJavaVM"); + jint jni_result = create_jvm(&jvm, (void**)&env, &vm_args); + + if (jni_result != JNI_OK) { + jvm = NULL; + fprintf(stderr, "Could not initialize JVM: %d: ", jni_result); + switch (jni_result) { + case JNI_ERR: + fprintf(stderr, "unknown error"); + break; + case JNI_EDETACHED: + fprintf(stderr, "thread detached from the VM"); + break; + case JNI_EVERSION: + fprintf(stderr, "JNI version error"); + break; + case JNI_ENOMEM: + fprintf(stderr, "not enough memory"); + break; + case JNI_EEXIST: + fprintf(stderr, "VM already created"); + break; + case JNI_EINVAL: + fprintf(stderr, "invalid arguments"); + break; + } + fprintf(stderr, "\n"); + fflush(stderr); + return E_FAIL; + } + + HMODULE hJavaProviderModule = GetModuleHandle(TEXT("javaprovider")); + HRSRC resCommandsClassfile = FindResource(hJavaProviderModule, MAKEINTRESOURCE(IDR_CLASSFILE1), TEXT("Classfile")); + HGLOBAL gblCommandsClassfile = LoadResource(hJavaProviderModule, resCommandsClassfile); + LPVOID lpCommandsClassfile = LockResource(gblCommandsClassfile); + DWORD szCommandsClassfile = SizeofResource(hJavaProviderModule, resCommandsClassfile); + + clsCommands = env->DefineClass( + "javaprovider/Commands", NULL, (jbyte*) lpCommandsClassfile, szCommandsClassfile + ); + if (clsCommands == NULL) { + fprintf(stderr, "Could not define Commands class\n"); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return E_FAIL; + } + } + + return S_OK; +} + +void EXT_CLASS::Uninitialize() { + if (jvm != NULL) { + jvm->DestroyJavaVM(); + } + ExtExtension::Uninitialize(); +} + +void EXT_CLASS::run_command(PCSTR name) { + // TODO: Throw an exception during load, then! + if (jvm == NULL) { + Out("javaprovider extension did not load properly.\n"); + return; + } + if (clsCommands == NULL) { + Out("javaprovider extension did not load properly.\n"); + return; + } + + PCSTR args = GetRawArgStr(); + + jmethodID mthCommand = env->GetStaticMethodID(clsCommands, name, "(Ljava/lang/String;)V"); + if (mthCommand == NULL) { + Out("INTERNAL ERROR: No such command: %s\n", name); + return; + } + + jstring argsStr = env->NewStringUTF(args); + if (argsStr == NULL) { + Out("Could not create Java string for arguments.\n"); + return; + } + + env->CallStaticVoidMethod(clsCommands, mthCommand, argsStr); + env->DeleteLocalRef(argsStr); + if (env->ExceptionCheck()) { + Out("Exception during javaprovider command:\n"); + env->ExceptionDescribe(); // TODO: Send this to output callbacks, not console. + env->ExceptionClear(); + } +} + +EXT_COMMAND(java_add_cp, "Add an element to the class path", "{{custom}}") { + run_command("java_add_cp"); +} + +EXT_COMMAND(java_set, "Set a Java system property", "{{custom}}") { + run_command("java_set"); +} + +EXT_COMMAND(java_get, "Get a Java system property", "{{custom}}") { + run_command("java_get"); +} + +EXT_COMMAND(java_run, "Execute the named java class", "{{custom}}") { + run_command("java_run"); +} + +#define JNA extern "C" __declspec(dllexport) + +JNA HRESULT createClient(PDEBUG_CLIENT* client) { + return g_ExtInstance.m_Client->CreateClient(client); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/def/javaprovider.def b/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/def/javaprovider.def new file mode 100644 index 0000000000..197aee0bbd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/def/javaprovider.def @@ -0,0 +1,13 @@ +EXPORTS + +; For ExtCpp + DebugExtensionInitialize + DebugExtensionUninitialize + DebugExtensionNotify + help + +; My Commands + java_add_cp + java_set + java_get + java_run diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/headers/afxres.h b/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/headers/afxres.h new file mode 100644 index 0000000000..853b8950c1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/headers/afxres.h @@ -0,0 +1,16 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/headers/resource.h b/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/headers/resource.h new file mode 100644 index 0000000000..39e5f2e8e5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/headers/resource.h @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by javaprovider.rc +// +#define IDR_CLASSFILE1 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/rc/javaprovider.rc b/Ghidra/Debug/Debugger-agent-dbgmodel/src/javaprovider/rc/javaprovider.rc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/DbgModelInJvmDebuggerModelFactory.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/DbgModelInJvmDebuggerModelFactory.java new file mode 100644 index 0000000000..2c2b22be0c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/DbgModelInJvmDebuggerModelFactory.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel; + +import java.util.concurrent.CompletableFuture; + +import agent.dbgmodel.model.impl.DbgModel2Impl; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.LocalDebuggerModelFactory; +import ghidra.dbg.util.ConfigurableFactory.FactoryDescription; +import ghidra.util.classfinder.ExtensionPointProperties; + +/** + * Note this is in the testing source because it's not meant to be shipped in the release.... That + * may change if it proves stable, though, no? + */ +@FactoryDescription( // + brief = "IN-VM MS dbgmodel local debugger", // + htmlDetails = "Launch a dbgmodel session in this same JVM" // +) +@ExtensionPointProperties(priority = 70) +public class DbgModelInJvmDebuggerModelFactory implements LocalDebuggerModelFactory { + + @Override + public CompletableFuture build() { + DbgModel2Impl model = new DbgModel2Impl(); + return model.startDbgEng(new String[] {}).thenApply(__ -> model); + } + + @Override + public boolean isCompatible() { + return System.getProperty("os.name").toLowerCase().contains("windows"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/COMUtilsExtra.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/COMUtilsExtra.java new file mode 100644 index 0000000000..30cb27a1c6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/COMUtilsExtra.java @@ -0,0 +1,72 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel; + +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMException; +import com.sun.jna.platform.win32.COM.COMUtils; + +/** + * Utilities for interacting with Microsoft COM objects beyond those provided by {@link COMUtils}. + * + * See the MSDN for details on the meanings of the return codes for the function or method of + * interest. + */ +public interface COMUtilsExtra { + + public static HRESULT E_UNEXPECTED = new HRESULT(0x8000FFFF); + public static HRESULT E_BOUNDS = new HRESULT(0x8000000B); + public static HRESULT E_NOTIMPLEMENTED = new HRESULT(0x80004001); + public static HRESULT E_NOINTERFACE = new HRESULT(0x80004002); + public static HRESULT E_COM_EXC = new HRESULT(0x80004003); + public static HRESULT E_FAIL = new HRESULT(0x80004005); + public static HRESULT E_CANTCALLOUT_INASYNCCALL = new HRESULT(0x80010004); + public static HRESULT E_INTERNALEXCEPTION = new HRESULT(0x80040205); + public static HRESULT E_ACCESS_DENIED = new HRESULT(0x80070005); + public static HRESULT E_CANNOT_READ = new HRESULT(0x8007001E); + public static HRESULT E_INVALID_PARAM = new HRESULT(0x80070057); + public static HRESULT E_SCOPE_NOT_FOUND = new HRESULT(0x8007013E); + + /** + * Check if the given exception represents an {@code E_NOINTERFACE} result + * + * @param e the exception + * @return true if {@code E_NOINTERFACE} + */ + static boolean isE_NOINTERFACE(COMException e) { + return e.getMessage().contains("HRESULT: 80004002"); + } + + /** + * Check if the given exception represents an {@code E_UNEXPECTED} result + * + * @param e the exception + * @return true if {@code E_UNEXPECTED} + */ + static boolean isE_UNEXPECTED(COMException e) { + return e.getMessage().contains("HRESULT: 8000ffff"); + } + + /** + * Check if the given exception represents an {@code E_INTERNALEXCEPTION} result + * + * @param e the exception + * @return true if {@code E_INTERNALEXCEPTION} + */ + static boolean isE_INTERNALEXCEPTION(COMException e) { + return e.getMessage().contains("HRESULT: 80040205"); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/DbgModel.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/DbgModel.java new file mode 100644 index 0000000000..bbe98e01bf --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/DbgModel.java @@ -0,0 +1,196 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel; + +import java.lang.ref.Cleaner; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Kernel32Util; +import com.sun.jna.platform.win32.WinDef.DWORD; +import com.sun.jna.platform.win32.WinNT.HANDLE; +import com.sun.jna.platform.win32.COM.IUnknown; + +import agent.dbgeng.impl.dbgeng.client.DebugClientImpl1; +import agent.dbgeng.impl.dbgeng.client.DebugClientInternal; +import agent.dbgeng.jna.dbgeng.DbgEngNative; +import agent.dbgeng.jna.dbgeng.client.IDebugClient; +import agent.dbgmodel.dbgmodel.bridge.HostDataModelAccess; +import agent.dbgmodel.impl.dbgmodel.bridge.HostDataModelAccessInternal; +import ghidra.comm.util.BitmaskSet; + +/** + * A wrapper for Microsoft's {@code dbgeng.dll} that presents a Java-friendly interface. + * + * This is the "root interface" from which all other interfaces to {@code dbgeng.dll} are generated. + * Not every method listed in the documentation, nor every method present in the header, is + * implemented. Only those that were necessary to implement the SCTL adapter. However, the class and + * interface hierarchy was designed so that adding the remaining methods should be fairly + * straightforward. This wrapper attempts to obtain the most capable COM interfaces for the debug + * client that it knows. Again, newer interfaces should be fairly straightforward to add. + * + * Methods that are "obviously" wrappers for a COM method are left undocumented, unless there is + * some nuance to how it has been wrapped. In many cases, a parameter which is an integer in the COM + * method may be presented as an {@code enum} or {@link BitmaskSet} by the wrapper. Consult the MSDN + * for the meaning of the various values and bit flags. + * + * Each wrapper interface is implemented by several COM interface wrappers: one for each known COM + * interface version. The wrapper is optimistic, in that it declares wrapper methods even for COM + * methods that are only available in later versions. The implementations limited to earlier COM + * interfaces should either emulate the operation, or throw an + * {@link UnsupportedOperationException}. Where a newer method is provided by a newer interface, a + * wrapper implementation should prefer the latest. For example, one series of interfaces introduces + * {@code *Wide} variants of existing methods. Since Java also uses a UTF-16-like string encoding + * internally, JNA permits wide strings to be passed by reference. Thus, the wide variant is always + * preferred. + * + * Pay careful attention to the threading requirements imposed by {@code dbgeng.dll} these can be + * found in the MSDN. As a general rule of thumb, if the method is reentrant (i.e., it can be called + * from any thread), it is declared in the {@code *Reentrant} variant of the wrapper interface. + * There are few of these. Unless the documentation explicitly lists the method as reentrant, do not + * declare it there. Many methods appear to execute successfully from the wrong thread, but cause + * latent issues. A practice to prevent accidental use of non-reentrant methods outside of the + * client's owning thread is to ensure that only the owning thread can see the full interface. All + * other threads should only have access to the reentrant interface. + * + * If you implement methods that introduce a new callback class, use the existing callback type + * hierarchies as a model. There are many classes to implement. Furthermore, be sure to keep a + * reference to any active callback instances within the wrapper that uses them. The JNA has no way + * of knowing whether or not the instance is still being used by the external C/C++ library. If you + * do not store a reference, the JVM will think it's garbage and free it, even though COM is still + * using it. Drop the reference only when you are certain nothing external has a reference to it. + */ +public class DbgModel { + private static final Cleaner CLEANER = Cleaner.create(); + + private static class ReleaseCOMObject implements Runnable { + private final IUnknown obj; + + ReleaseCOMObject(IUnknown obj) { + this.obj = obj; + } + + @Override + public void run() { + //Msg.debug(this, "Releasing COM object: " + obj); + try { + obj.Release(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + } + + private static class ReleaseHANDLE implements Runnable { + private final HANDLE handle; + + public ReleaseHANDLE(HANDLE handle) { + this.handle = handle; + } + + @Override + public void run() { + Kernel32Util.closeHandle(handle); + } + } + + public static class OpaqueCleanable { + @SuppressWarnings("unused") // A reference to control GC + private final Object state; + @SuppressWarnings("unused") // A reference to control GC + private final Cleaner.Cleanable cleanable; + + public OpaqueCleanable(Object state, Cleaner.Cleanable cleanable) { + this.state = state; + this.cleanable = cleanable; + } + } + + public static OpaqueCleanable releaseWhenPhantom(Object owner, IUnknown obj) { + ReleaseCOMObject state = new ReleaseCOMObject(obj); + return new OpaqueCleanable(state, CLEANER.register(owner, state)); + } + + public static OpaqueCleanable releaseWhenPhantom(Object owner, HANDLE handle) { + ReleaseHANDLE state = new ReleaseHANDLE(handle); + return new OpaqueCleanable(state, CLEANER.register(owner, state)); + } + + private static HostDataModelAccess wrapClient(DebugClientInternal debugClient) { + DebugClientImpl1 impl = (DebugClientImpl1) debugClient; + IDebugClient jnaClient = impl.getJNAClient(); + HostDataModelAccess hostDataModelAccess = + HostDataModelAccessInternal.tryPreferredInterfaces(jnaClient::QueryInterface); + hostDataModelAccess.setClient(debugClient); + hostDataModelAccess.getDataModel(); + return hostDataModelAccess; + } + + /** + * Connect to a debug session. + * + * See {@code DebugConnect} or {@code DebugConnectWide} on the MSDN. + * + * @param remoteOptions the options, like those given to {@code -remote} + * @return a new client connected as specified + */ + public static HostDataModelAccess debugConnect(String remoteOptions) { + addSearchPaths(); + // so this will not work + WString options = new WString(remoteOptions); + return wrapClient(DebugClientInternal.tryPreferredInterfaces((refiid, + ppClient) -> DbgEngNative.INSTANCE.DebugConnectWide(options, refiid, ppClient))); + } + + /** + * Create a debug client. + * + * Typically, this client is connected to the "local server". See {@code DebugCreate} on the + * MSDN. + * + * @return a new client + */ + + public static HostDataModelAccess debugCreate() { + addSearchPaths(); + return wrapClient( + DebugClientInternal.tryPreferredInterfaces(DbgEngNative.INSTANCE::DebugCreate)); + } + + /** + * Create a debug client with the given options. + * + * See {@code DebugCreateEx} on the MSDN. + * + * @param options the options + * @return a new client + */ + public static HostDataModelAccess debugCreate(int options) { + addSearchPaths(); + DWORD dwOpts = new DWORD(options); + return wrapClient(DebugClientInternal.tryPreferredInterfaces( + (refiid, ppClient) -> DbgEngNative.INSTANCE.DebugCreateEx(refiid, dwOpts, ppClient))); + } + + private static void addSearchPaths() { + /* + NativeLibrary.addSearchPath("dbghelp.dll", "C:\\Software\\windbg\\amd64"); + NativeLibrary.addSearchPath("dbgeng.dll", "C:\\Software\\windbg\\amd64"); + NativeLibrary.addSearchPath("dbgmodel.dll", "C:\\Software\\windbg\\amd64"); + */ + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/UnknownEx.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/UnknownEx.java new file mode 100644 index 0000000000..9576a94ebe --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/UnknownEx.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel; + +import com.sun.jna.Pointer; + +public interface UnknownEx { + + Pointer getPointer(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/bridge/HostDataModelAccess.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/bridge/HostDataModelAccess.java new file mode 100644 index 0000000000..bb83ab26c3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/bridge/HostDataModelAccess.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.bridge; + +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.datamodel.DataModelManager1; +import agent.dbgmodel.dbgmodel.debughost.DebugHost; + +/** + * A wrapper for {@code IHostDataModelAccess} and its newer variants. + */ +public interface HostDataModelAccess extends UnknownEx { + + void getDataModel(); + + DebugClient getClient(); + + void setClient(DebugClient debugClient); + + DataModelManager1 getManager(); + + DebugHost getHost(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/ComparableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/ComparableConcept.java new file mode 100644 index 0000000000..70f327b099 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/ComparableConcept.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.concept; + +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IComparableConcept} and its newer variants. + */ +public interface ComparableConcept extends Concept { + + int compareObjects(ModelObject contextObject, ModelObject otherObject); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/Concept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/Concept.java new file mode 100644 index 0000000000..cf818df3aa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/Concept.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.concept; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.main.KeyStore; + +public interface Concept extends UnknownEx { + + KeyStore getMetadata(); + + void setMetadata(KeyStore mdata); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DataModelConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DataModelConcept.java new file mode 100644 index 0000000000..794829f7e6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DataModelConcept.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.concept; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostSymbolEnumerator; +import agent.dbgmodel.dbgmodel.debughost.DebugHostTypeSignature; +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IDataModelConcept} and its newer variants. + */ +public interface DataModelConcept extends Concept { + + void initializeObject(ModelObject modelObject, DebugHostTypeSignature matchingTypeSignature, + DebugHostSymbolEnumerator wildcardMatches); + + String getName(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DynamicConceptProviderConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DynamicConceptProviderConcept.java new file mode 100644 index 0000000000..01ad73888f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DynamicConceptProviderConcept.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.concept; + +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IDynamicConceptProviderConcept} and its newer variants. + */ +public interface DynamicConceptProviderConcept extends Concept { + + boolean getConcept(ModelObject contextObject, REFIID conceptId); + + void setConcept(ModelObject contextObject, REFIID conceptId, UnknownEx conceptInterface, + KeyStore conceptMetadata); + + void notifyParent(ModelObject parentModel); + + void notifyParentChange(ModelObject parentModel); + + void notifyDestruct(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DynamicKeyProviderConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DynamicKeyProviderConcept.java new file mode 100644 index 0000000000..3a4f5cb632 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/DynamicKeyProviderConcept.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.concept; + +import com.sun.jna.WString; + +import agent.dbgmodel.dbgmodel.main.*; + +/** + * A wrapper for {@code IDynamicKeyProviderConcept} and its newer variants. + */ +public interface DynamicKeyProviderConcept extends Concept { + + boolean getKey(ModelObject contextObject, WString key); + + void setKey(ModelObject contextObject, WString key, ModelObject keyValue, KeyStore metadata); + + KeyEnumerator EnumerateKeys(ModelObject contextObject); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/EquatableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/EquatableConcept.java new file mode 100644 index 0000000000..5a19819c83 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/EquatableConcept.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.concept; + +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IEquatableConcept} and its newer variants. + */ +public interface EquatableConcept extends Concept { + + boolean areObjectsEqual(ModelObject contextObject, ModelObject otherObject); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/IndexableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/IndexableConcept.java new file mode 100644 index 0000000000..7a9e905940 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/IndexableConcept.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.concept; + +import com.sun.jna.Pointer; + +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IIndexableConcept} and its newer variants. + */ +public interface IndexableConcept extends Concept { + + long getDimensionality(ModelObject contextObject); + + ModelObject getAt(ModelObject contextObject, long indexerCount, Pointer[] indexers); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/IterableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/IterableConcept.java new file mode 100644 index 0000000000..0027c35977 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/IterableConcept.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.concept; + +import agent.dbgmodel.dbgmodel.main.ModelIterator; +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IIterableConcept} and its newer variants. + */ +public interface IterableConcept extends Concept { + + long getDefaultIndexDimensionality(ModelObject contextObject); + + ModelIterator getIterator(ModelObject contextObject); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/PreferredRuntimeTypeConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/PreferredRuntimeTypeConcept.java new file mode 100644 index 0000000000..1df35f1c94 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/PreferredRuntimeTypeConcept.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.concept; + +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IPreferredRuntimeTypeConcept} and its newer variants. + */ +public interface PreferredRuntimeTypeConcept extends Concept { + + ModelObject CastToPreferredRuntimeType(ModelObject contextObject); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/StringDisplayableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/StringDisplayableConcept.java new file mode 100644 index 0000000000..d7f958ca7f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/concept/StringDisplayableConcept.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.concept; + +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IStringDisplayableConcept} and its newer variants. + */ +public interface StringDisplayableConcept extends Concept { + + String toDisplayString(ModelObject contextObject, KeyStore metadata); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/DataModelManager1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/DataModelManager1.java new file mode 100644 index 0000000000..c7a942ee9b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/DataModelManager1.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Variant.VARIANT.ByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.concept.DataModelConcept; +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptManager; +import agent.dbgmodel.dbgmodel.debughost.*; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ModelObjectKind; + +/** + * A wrapper for {@code IDataModelManager1} and its newer variants. + */ +public interface DataModelManager1 extends UnknownEx { + + void close(); + + ModelObject createNoValue(); + + ModelObject createErrorObject(HRESULT hrError, WString pwszMessage); + + ModelObject createTypedObject(DebugHostContext context, LOCATION objectLocation, + DebugHostType1 objectType); + + ModelObject createTypedObjectReference(DebugHostContext context, LOCATION objectLocation, + DebugHostType1 objectType); + + ModelObject createSyntheticObject(DebugHostContext context); + + ModelObject createDataModelObject(DataModelConcept dataModel); + + ModelObject createIntrinsicObject(ModelObjectKind objectKind, ByReference intrinsicData); + + ModelObject createTypedIntrinsicObject(ByReference intrinsicData, DebugHostType1 type); + + ModelObject getModelForTypeSignature(DebugHostTypeSignature typeSignature); + + ModelObject getModelForType(DebugHostType1 type); + + void registerModelForTypeSignature(DebugHostTypeSignature typeSignature, + ModelObject dataModel); + + void unregisterModelForTypeSignature(ModelObject dataModel, + DebugHostTypeSignature typeSignature); + + void registerExtensionForTypeSignature(DebugHostTypeSignature typeSignature, + ModelObject dataModel); + + void unregisterExtensionForTypeSignature(ModelObject dataModel, + DebugHostTypeSignature typeSignature); + + KeyStore createMetadataStore(KeyStore parentStore); + + ModelObject getRootNamespace(); + + void registerNamedModel(WString modelName, ModelObject modelObject); + + void unregisterNamedModel(WString modelName); + + ModelObject acquireNamedModel(WString modelName); + + DataModelScriptManager asScriptManager(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/DataModelManager2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/DataModelManager2.java new file mode 100644 index 0000000000..8042a8360b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/DataModelManager2.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Variant.VARIANT.ByReference; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostContext; +import agent.dbgmodel.dbgmodel.debughost.DebugHostType1; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IDataModelManager2} and its newer variants. + */ +public interface DataModelManager2 extends DataModelManager1 { + + ModelObject acquireSubNamespace(WString modelName, WString subNamespaceModelName, + WString accessName, KeyStore metadata); + + ModelObject createTypedIntrinsicObjectEx(DebugHostContext context, ByReference intrinsicData, + DebugHostType1 type); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelNameBinder.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelNameBinder.java new file mode 100644 index 0000000000..d5f9f7d39f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelNameBinder.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script; + +import com.sun.jna.WString; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.main.KeyEnumerator; +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IDataModelNameBinder} and its newer variants. + */ +public interface DataModelNameBinder extends UnknownEx { + + void bindValue(ModelObject contextObject, WString name); + + void bindReference(ModelObject contextObject, WString name); + + KeyEnumerator enumerateValues(ModelObject contextObject); + + KeyEnumerator enumerateReferences(ModelObject contextObject); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScript.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScript.java new file mode 100644 index 0000000000..391b0f1305 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScript.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScript} and its newer variants. + */ +public interface DataModelScript extends UnknownEx { + + String getName(); + + void rename(WString scriptName); + + void populate(Pointer contentStream); + + void execute(DataModelScriptClient client); + + void unlink(); + + boolean isInvocable(); + + void invokeMain(DataModelScriptClient client); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptClient.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptClient.java new file mode 100644 index 0000000000..1abbb87bc9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptClient.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptClient} and its newer variants. + */ +public interface DataModelScriptClient extends UnknownEx { + + void reportError(int errorClass, HRESULT hrFail, WString message, int line, int position); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptHostContext.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptHostContext.java new file mode 100644 index 0000000000..991b868940 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptHostContext.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IDataModelScriptHostContext} and its newer variants. + */ +public interface DataModelScriptHostContext extends UnknownEx { + + void notifyScriptChange(DataModelScript script, int changeKind); + + ModelObject getNamespaceObject(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptManager.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptManager.java new file mode 100644 index 0000000000..540d5cd911 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptManager.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptManager} and its newer variants. + */ +public interface DataModelScriptManager extends UnknownEx { + + DataModelNameBinder getDefaultNameBinder(); + + void registerScriptProvider(DataModelScriptProvider provider); + + void unregisterScriptProvider(DataModelScriptProvider provider); + + DataModelScriptProvider findProviderForScriptType(String scriptType); + + DataModelScriptProvider findProviderForScriptExtension(String scriptExtension); + + DataModelScriptProviderEnumerator enumeratorScriptProviders(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptProvider.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptProvider.java new file mode 100644 index 0000000000..115a69f3f9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptProvider.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptProvider} and its newer variants. + */ +public interface DataModelScriptProvider extends UnknownEx { + + String getName(); + + String getExtension(); + + DataModelScriptTemplate createScript(); + + DataModelScriptTemplate getDefaultTemplateContent(); + + DataModelScriptTemplateEnumerator enumerateTemplates(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptProviderEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptProviderEnumerator.java new file mode 100644 index 0000000000..b215f7e31a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptProviderEnumerator.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptProviderEnumerator} and its newer variants. + */ +public interface DataModelScriptProviderEnumerator extends UnknownEx { + + void reset(); + + DataModelScriptProvider getNext(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptTemplate.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptTemplate.java new file mode 100644 index 0000000000..47a8ae7dcd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptTemplate.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptTemplate} and its newer variants. + */ +public interface DataModelScriptTemplate extends UnknownEx { + + String getName(); + + String getDescription(); + + UnknownEx getContent(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptTemplateEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptTemplateEnumerator.java new file mode 100644 index 0000000000..8ae7867de2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/DataModelScriptTemplateEnumerator.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptTemplateEnumerator} and its newer variants. + */ +public interface DataModelScriptTemplateEnumerator extends UnknownEx { + + void reset(); + + DataModelScriptTemplate getNext(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebug1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebug1.java new file mode 100644 index 0000000000..bef2908499 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebug1.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script.debug; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptDebug1} and its newer variants. + */ +public interface DataModelScriptDebug1 extends UnknownEx { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebug2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebug2.java new file mode 100644 index 0000000000..6232ce058b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebug2.java @@ -0,0 +1,22 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script.debug; + +/** + * A wrapper for {@code IDataModelScriptDebug2} and its newer variants. + */ +public interface DataModelScriptDebug2 extends DataModelScriptDebug1 { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpoint.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpoint.java new file mode 100644 index 0000000000..640bb07f10 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpoint.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script.debug; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptDebugBreakpoint} and its newer variants. + */ +public interface DataModelScriptDebugBreakpoint extends UnknownEx { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumerator.java new file mode 100644 index 0000000000..4b5d191643 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumerator.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script.debug; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptDebugBreakpointEnumerator} and its newer variants. + */ +public interface DataModelScriptDebugBreakpointEnumerator extends UnknownEx { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugClient.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugClient.java new file mode 100644 index 0000000000..45cc4fa09f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugClient.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script.debug; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptDebugClient} and its newer variants. + */ +public interface DataModelScriptDebugClient extends UnknownEx { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugStack.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugStack.java new file mode 100644 index 0000000000..63dbaf18b3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugStack.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script.debug; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptDebugStack} and its newer variants. + */ +public interface DataModelScriptDebugStack extends UnknownEx { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrame.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrame.java new file mode 100644 index 0000000000..7ef75a02e8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrame.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script.debug; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptDebugStackFrame} and its newer variants. + */ +public interface DataModelScriptDebugStackFrame extends UnknownEx { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumerator.java new file mode 100644 index 0000000000..d7996519f7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumerator.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.datamodel.script.debug; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDataModelScriptDebugVariableSetEnumerator} and its newer variants. + */ +public interface DataModelScriptDebugVariableSetEnumerator extends UnknownEx { +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHost.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHost.java new file mode 100644 index 0000000000..86065c4046 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHost.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.main.KeyStore; + +/** + * A wrapper for {@code IDebugHost} and its newer variants. + */ +public interface DebugHost extends UnknownEx { + + UnknownEx getHostDefinedInterface(); + + DebugHostContext getCurrentContext(); + + KeyStore getDefaultMetadata(); + + DebugHostSymbols asSymbols(); + + DebugHostMemory1 asMemory(); + + DebugHostScriptHost asScriptHost(); + + DebugHostEvaluator2 asEvaluator(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostBase.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostBase.java new file mode 100644 index 0000000000..2ce27e1dac --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostBase.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import com.sun.jna.WString; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.SymbolKind; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostBaseClass; + +/** + * A wrapper for {@code IDebugHostBase} and its newer variants. + */ +public interface DebugHostBase extends UnknownEx { + + DebugHostContext getContext(); + + DebugHostSymbolEnumerator enumerateChildren(SymbolKind symbolModule, WString Name); + + SymbolKind getSymbolKind(); + + String getName(); + + DebugHostType1 getType(); + + DebugHostModule1 getContainingModule(); + + long getOffset(); + + IDebugHostBaseClass getJnaData(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostBaseClass.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostBaseClass.java new file mode 100644 index 0000000000..47eda0aa26 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostBaseClass.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +/** + * A wrapper for {@code IDebugHostBaseClass} and its newer variants. + */ +public interface DebugHostBaseClass extends DebugHostBase { + + long getOffset(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostConstant.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostConstant.java new file mode 100644 index 0000000000..66907f99bd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostConstant.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Variant.VARIANT; + +/** + * A wrapper for {@code IDebugHostConstant} and its newer variants. + */ +public interface DebugHostConstant extends DebugHostBase { + + VARIANT getValue(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostContext.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostContext.java new file mode 100644 index 0000000000..e6057ad30c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostContext.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDebugHostContext} and its newer variants. + */ +public interface DebugHostContext extends UnknownEx { + + boolean isEqualTo(DebugHostContext context); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostData.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostData.java new file mode 100644 index 0000000000..f89dd7775b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostData.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Variant.VARIANT; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +/** + * A wrapper for {@code IDebugHostData} and its newer variants. + */ +public interface DebugHostData extends DebugHostBase { + + int getLocationKind(); + + LOCATION getLocation(); + + VARIANT getValue(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostErrorSink.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostErrorSink.java new file mode 100644 index 0000000000..920100f821 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostErrorSink.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDebugHostErrorSink} and its newer variants. + */ +public interface DebugHostErrorSink extends UnknownEx { + + void reportError(int errorClass, HRESULT hrError, WString message); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostEvaluator1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostEvaluator1.java new file mode 100644 index 0000000000..5c2de9d02d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostEvaluator1.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import com.sun.jna.WString; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IDebugHostEvaluator1} and its newer variants. + */ +public interface DebugHostEvaluator1 extends UnknownEx { + + ModelObject evaluateExpression(DebugHostContext context, WString expression, + ModelObject bindingContext); + + ModelObject evaluateExtendedExpression(DebugHostContext context, WString expression, + ModelObject bindingContext); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostEvaluator2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostEvaluator2.java new file mode 100644 index 0000000000..e6121bde0f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostEvaluator2.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IDebugHostEvaluator2} and its newer variants. + */ +public interface DebugHostEvaluator2 extends DebugHostEvaluator1 { + + ModelObject assignTo(ModelObject assignmentReference, ModelObject assignmentValue); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostExtensability.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostExtensability.java new file mode 100644 index 0000000000..20189375f5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostExtensability.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import com.sun.jna.WString; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.main.ModelObject; + +/** + * A wrapper for {@code IDebugHostExtensability} and its newer variants. + */ +public interface DebugHostExtensability extends UnknownEx { + + void createFunctionAlias(WString aliasName, ModelObject functionObject); + + void destroyFunctionAlias(WString aliasName); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostField.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostField.java new file mode 100644 index 0000000000..c88eeb5ad5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostField.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Variant.VARIANT; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +/** + * A wrapper for {@code IDebugHostField} and its newer variants. + */ +public interface DebugHostField extends DebugHostBase { + + int getLocationKind(); + + long getOffset(); + + LOCATION getLocation(); + + VARIANT getValue(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostMemory1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostMemory1.java new file mode 100644 index 0000000000..7cb3fd7eaf --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostMemory1.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import java.nio.ByteBuffer; + +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +/** + * A wrapper for {@code IDebugHostMemory1} and its newer variants. + */ +public interface DebugHostMemory1 extends UnknownEx { + + long readBytes(DebugHostContext context, LOCATION location, ByteBuffer buffer, long bufferSize); + + long writeBytes(DebugHostContext context, LOCATION location, ByteBuffer buffer, + long bufferSize); + + ULONGLONGByReference readPointers(DebugHostContext context, LOCATION location, long count); + + ULONGLONGByReference writePointers(DebugHostContext context, LOCATION location, long count); + + String GetDisplayStringForLocation(DebugHostContext context, LOCATION location, + boolean verbose); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostMemory2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostMemory2.java new file mode 100644 index 0000000000..a9075567e1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostMemory2.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +/** + * A wrapper for {@code IDebugHostMemory2} and its newer variants. + */ +public interface DebugHostMemory2 extends DebugHostMemory1 { + + LOCATION linearizeLocation(DebugHostContext context, LOCATION location); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModule1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModule1.java new file mode 100644 index 0000000000..8999140d06 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModule1.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +/** + * A wrapper for {@code IDebugHostModule1} and its newer variants. + */ +public interface DebugHostModule1 extends DebugHostBase { + + String getImageName(boolean allowPath); + + LOCATION getBaseLocation(); + + void getVersion(); + + DebugHostType1 findTypeByName(String typeName); + + DebugHostSymbol1 findSymbolByRVA(long rva); + + DebugHostSymbol1 findSymbolByName(String symbolName); + + DebugHostSymbol1 asSymbol(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModule2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModule2.java new file mode 100644 index 0000000000..fb770ea907 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModule2.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +/** + * A wrapper for {@code IDebugHostModule} and its newer variants. + */ +public interface DebugHostModule2 extends DebugHostModule1 { + + DebugHostSymbol1 findContainingSymbolByRVA(long rva); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModuleSignature.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModuleSignature.java new file mode 100644 index 0000000000..a4700ef87c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostModuleSignature.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDebugHostModuleSignature} and its newer variants. + */ +public interface DebugHostModuleSignature extends UnknownEx { + + boolean IsMatch(DebugHostModule1 module); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostPublic.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostPublic.java new file mode 100644 index 0000000000..0962dd47be --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostPublic.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LocationKind; + +/** + * A wrapper for {@code IDebugHostPublic} and its newer variants. + */ +public interface DebugHostPublic extends UnknownEx { + + LocationKind getLocationKind(); + + LOCATION getLocation(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostScriptHost.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostScriptHost.java new file mode 100644 index 0000000000..61449ff1ef --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostScriptHost.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScript; + +/** + * A wrapper for {@code IDebugHostScriptHost} and its newer variants. + */ +public interface DebugHostScriptHost extends UnknownEx { + + DebugHostContext createContext(DataModelScript script); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostStatus.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostStatus.java new file mode 100644 index 0000000000..6d36ee5dbe --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostStatus.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDebugHostStatus} and its newer variants. + */ +public interface DebugHostStatus extends UnknownEx { + + boolean PollUserInterrupt(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbol1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbol1.java new file mode 100644 index 0000000000..dcc1c86298 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbol1.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +/** + * A wrapper for {@code IDebugHostSymbol1} and its newer variants. + */ +public interface DebugHostSymbol1 extends DebugHostBase { + + int compareAgainst(DebugHostSymbol1 comparisonSymbol, long comparisonFlags); + + DebugHostBaseClass asBaseClass(); + + DebugHostConstant asConstant(); + + DebugHostData asData(); + + DebugHostField asField(); + + DebugHostModule1 asModule(); + + DebugHostPublic asPublic(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbol2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbol2.java new file mode 100644 index 0000000000..f3edbc28b8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbol2.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import com.sun.jna.Structure.ByReference; +import com.sun.jna.WString; + +/** + * A wrapper for {@code IDebugHostSymbol2} and its newer variants. + */ +public interface DebugHostSymbol2 extends DebugHostSymbol1 { + + DebugHostSymbolEnumerator enumerateChildrenEx(long kind, WString name, ByReference searchInfo); + + int getLanguage(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbolEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbolEnumerator.java new file mode 100644 index 0000000000..5fd7d1f2e7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbolEnumerator.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IDebugHostSymbolEnumerator} and its newer variants. + */ +public interface DebugHostSymbolEnumerator extends UnknownEx { + + void reset(); + + DebugHostSymbol1 getNext(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbols.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbols.java new file mode 100644 index 0000000000..78e3f54a69 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostSymbols.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import com.sun.jna.WString; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +/** + * A wrapper for {@code IDebugHostSymbols} and its newer variants. + */ +public interface DebugHostSymbols extends UnknownEx { + + DebugHostModuleSignature createModuleSignature(WString pwszModuleName, WString pwszMinVersion, + WString pwszMaxVersion); + + DebugHostTypeSignature createTypeSignature(WString signatureSpecification, + DebugHostModule1 module); + + DebugHostTypeSignature createTypeSignatureForModuleRange(WString signatureSpecification, + WString pwszModuleName, WString pwszMinVersion, WString pwszMaxVersion); + + DebugHostSymbolEnumerator enumerateModules(DebugHostContext context); + + DebugHostModule1 findModuleByName(DebugHostContext context, String string); + + DebugHostModule1 findModuleByLocation(DebugHostContext context, LOCATION moduleLocation); + + DebugHostType1 getMostDerivedObject(DebugHostContext context, LOCATION location, + DebugHostType1 objectTypeLOCATION); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostType1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostType1.java new file mode 100644 index 0000000000..0b8217e9f1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostType1.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.*; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ARRAY_DIMENSION.ByReference; + +/** + * A wrapper for {@code IDebugHostType1} and its newer variants. + */ +public interface DebugHostType1 extends DebugHostBase { + + TypeKind getTypeKind(); + + long getSize(); + + DebugHostType1 getBaseType(); + + int getHashCode(); + + IntrinsicKind getIntrinsicType(); + + void getBitField(); + + PointerKind getPointerKind(); + + DebugHostType1 getMemberType(); + + DebugHostType1 createPointerTo(int kind); + + long getArrayDimensionality(); + + ARRAY_DIMENSION getArrayDimensions(long dimensions); + + DebugHostType1 createArrayOf(long dimensions, ByReference pDimensions); + + int getFunctionCallingConvention(); + + DebugHostType1 getFunctionReturnType(); + + DebugHostType1 getFunctionParameterTypeAt(int i); + + boolean isGeneric(); + + long getGenericArgumentCount(); + + DebugHostSymbol1 getGenericArgumentAt(int i); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostType2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostType2.java new file mode 100644 index 0000000000..630ab23991 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostType2.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +/** + * A wrapper for {@code IDebugHostType2} and its newer variants. + */ +public interface DebugHostType2 extends DebugHostType1 { + + boolean isTypedef(); + + DebugHostType2 getTypedefBaseType(); + + DebugHostType2 getTypedefFinalBaseType(); + + int getFunctionVarArgsKind(); + + DebugHostType2 getFunctionInstancePointerType(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostTypeSignature.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostTypeSignature.java new file mode 100644 index 0000000000..51475942e0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/debughost/DebugHostTypeSignature.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.debughost; + +import com.sun.jna.Pointer; + +/** + * A wrapper for {@code IDebugHostTypeSignature} and its newer variants. + */ +public interface DebugHostTypeSignature { + + Pointer getPointer(); + + int getHashCode(); + + boolean isMatch(DebugHostType1 type); + + int compareAgainst(DebugHostTypeSignature typeSignature); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/err/DbgModelException.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/err/DbgModelException.java new file mode 100644 index 0000000000..26ecf263e0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/err/DbgModelException.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.err; + +/** + * The base exception for checked {@code dbgmodel.dll} wrapper-related errors. + */ +public class DbgModelException extends Exception { + public DbgModelException() { + super(); + } + + public DbgModelException(String message) { + super(message); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/err/DbgModelRuntimeException.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/err/DbgModelRuntimeException.java new file mode 100644 index 0000000000..5ea342d289 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/err/DbgModelRuntimeException.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.err; + +/** + * The base exception for unchecked {@code dbgmodel.dll} wrapper-related errors. + */ +public class DbgModelRuntimeException extends RuntimeException { + public DbgModelRuntimeException() { + super(); + } + + public DbgModelRuntimeException(String message) { + super(message); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/KeyEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/KeyEnumerator.java new file mode 100644 index 0000000000..931689d476 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/KeyEnumerator.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.main; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IKeyEnumerator} and its newer variants. + */ +public interface KeyEnumerator extends UnknownEx { + + void reset(); + + String getNext(); + + ModelObject getValue(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/KeyStore.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/KeyStore.java new file mode 100644 index 0000000000..e3523edf14 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/KeyStore.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.main; + +import com.sun.jna.WString; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IKeyStore} and its newer variants. + */ +public interface KeyStore extends UnknownEx { + + ModelObject getKey(WString key); + + void setKey(WString key, ModelObject object, KeyStore metadata); + + ModelObject getKeyValue(WString key); + + void setKeyValue(WString key, ModelObject object); + + void clearKeys(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelIterator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelIterator.java new file mode 100644 index 0000000000..62d990bd64 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelIterator.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.main; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IModelIterator} and its newer variants. + */ +public interface ModelIterator extends UnknownEx { + + void reset(); + + ModelObject getNext(long dimensions); + + ModelObject getIndexers(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelKeyReference1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelKeyReference1.java new file mode 100644 index 0000000000..357d97f614 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelKeyReference1.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.main; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IModelKeyReference1} and its newer variants. + */ +public interface ModelKeyReference1 extends UnknownEx { + + String getKeyName(); + + ModelObject getOriginalObject(); + + ModelObject getContextObject(); + + ModelObject getKey(); + + ModelObject getKeyValue(); + + void setKey(ModelObject object, KeyStore metadata); + + void setKeyValue(ModelObject object, KeyStore metadata); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelKeyReference2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelKeyReference2.java new file mode 100644 index 0000000000..22d732f21f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelKeyReference2.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.main; + +/** + * A wrapper for {@code IModelKeyReference2} and its newer variants. + */ +public interface ModelKeyReference2 extends ModelKeyReference1 { + + void overrideContextObject(ModelObject newContextObject); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelMethod.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelMethod.java new file mode 100644 index 0000000000..d92fe03417 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelMethod.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.main; + +import com.sun.jna.Pointer; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IModelMethod} and its newer variants. + */ +public interface ModelMethod extends UnknownEx { + + ModelObject call(ModelObject contextObject, long argCount, Pointer[] ppArguments); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelObject.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelObject.java new file mode 100644 index 0000000000..1eb7ab48ba --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelObject.java @@ -0,0 +1,139 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.main; + +import java.util.List; +import java.util.Map; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WTypes.VARTYPE; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.datamodel.DataModelManager1; +import agent.dbgmodel.dbgmodel.debughost.DebugHostContext; +import agent.dbgmodel.dbgmodel.debughost.DebugHostType1; +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.*; +import agent.dbgmodel.jna.dbgmodel.main.IModelObject; + +/** + * A wrapper for {@code IModelObject} and its newer variants. + */ +public interface ModelObject extends UnknownEx { + + DebugHostContext getContext(); + + ModelObjectKind getKind(); + + Object getIntrinsicValue(); + + VARIANT getIntrinsicValueAs(VARTYPE vt); + + ModelObject getKeyValue(String key); + + void setKeyValue(WString key, ModelObject object); + + KeyEnumerator enumerateKeyValues(); + + ModelObject getRawValue(int kind, WString name, int searchFlags); + + RawEnumerator enumerateRawValues(int kind, int searchFlags); + + ModelObject dereference(); + + ModelObject tryCastToRuntimeType(); + + UnknownEx getConcept(REFIID conceptId); + + LOCATION getLocation(); + + DebugHostType1 getTypeInfo(); + + DebugHostType1 getTargetInfo(); + + long getNumberOfParentModels(); + + ModelObject getParentModel(int i); + + void addParentModel(ModelObject model, ModelObject contextObject, boolean override); + + void removeParentModel(ModelObject model); + + ModelObject getKey(String key); + + ModelObject getKeyReference(String key); + + void setKey(WString key, ModelObject object, KeyStore conceptMetadata); + + void clearKeys(); + + KeyEnumerator enumerateKeys(); + + KeyEnumerator enumerateKeyReferences(); + + void setConcept(REFIID conceptId, ModelObject conceptInterface, ModelObject conceptMetadata); + + void clearConcepts(); + + ModelObject getRawReference(int kind, WString name, int searchFlags); + + RawEnumerator enumerateRawReferences(int kind, int searchFlags); + + void setContextForDataModel(ModelObject dataModelObject, IUnknownEx context); + + UnknownEx getContextForDataModel(ModelObject dataModelObject); + + boolean compare(ModelObject contextObject, ModelObject other); + + /*******/ + + KeyStore getMetadata(); + + void setMetadata(KeyStore metadata); + + void setContextObject(ModelObject context); + + ModelObject getIndexer(); + + void setIndexer(ModelObject indexer); + + List getElements(); + + ModelObject getChild(DataModelManager1 manager, VARIANT v); + + Map getKeyValueMap(); + + Map getRawValueMap(); + + Object getValue(); + + String getValueString(); + + IModelObject getJnaData(); + + void switchTo(DataModelManager1 manager, VARIANT v); + + ModelMethod getMethod(String name); + + String getSearchKey(); + + void setSearchKey(String key); + + TypeKind getTypeKind(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelPropertyAccessor.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelPropertyAccessor.java new file mode 100644 index 0000000000..e8e8f7cd49 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/ModelPropertyAccessor.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.main; + +import agent.dbgmodel.dbgmodel.UnknownEx; + +/** + * A wrapper for {@code IModelPropertyAccessor} and its newer variants. + */ +public interface ModelPropertyAccessor extends UnknownEx { + + ModelObject getValue(String key, ModelObject contextObject); + + void setValue(String key, ModelObject contextObject, ModelObject value); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/RawEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/RawEnumerator.java new file mode 100644 index 0000000000..ced1836f88 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/dbgmodel/main/RawEnumerator.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel.main; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ModelObjectKind; + +/** + * A wrapper for {@code IRawEnumerator} and its newer variants. + */ +public interface RawEnumerator extends UnknownEx { + + void reset(); + + String getNext(); + + ModelObjectKind getKind(); + + ModelObject getValue(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelGadpServer.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelGadpServer.java new file mode 100644 index 0000000000..cd18fa578b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelGadpServer.java @@ -0,0 +1,121 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.gadp; + +import java.io.IOException; +import java.net.SocketAddress; +import java.util.concurrent.ExecutionException; + +import agent.dbgeng.gadp.DbgEngGadpServer; +import agent.dbgmodel.gadp.impl.DbgModelGadpServerImpl; +import ghidra.dbg.agent.AgentWindow; +import ghidra.util.Msg; + +/** + * The interface for the SCTL-{@code dbgeng.dll} server + * + * This is just an interface to specify the truly public methods. This is also a convenient place to + * put all the command-line parsing logic. + * + * This server implements the SCTL commands necessary to have a smooth debugging experience in + * Ghidra. It implements almost every command that has use on a binary without debugging + * information. It operates as a standalone debugging server based on {@code dbgeng.dll}, which can + * accept other {@code dbgeng.dll}-based clients as well as SCTL clients. + * + * Without limitation, the caveats are listed here: + * + * 1) The {@code Tnames} request in not implemented. The only namespaces available are those given + * in the {@code Rstat} response. + * + * 2) For binaries without a debugging database (pdb file), the symbol commands only search the + * exported symbols. + * + * 3) The type commands are not implemented. Ghidra can read most PDB files directly. + * + * 4) While SCTL presents thread-specific control, {@code dbgeng.dll} does not. Continue ("g" in + * {@code dbgeng.dll}) affects all debugged targets, except those with higher suspect counts and + * those that are frozen. The API makes it impossible to perfectly track which threads are actually + * executed by "g". The server thus assumes that all threads run when any thread runs, and it will + * synthesize the commands to reflect that in the connected clients. + * + * 5) The {@code Ttrace} command is not supported. The user can configure filters in the host + * debugger; however, some events will always be trapped by the SCTL server. Future versions may + * adjust this. + * + * 6) Snapshots are not supported. {@code dbgeng.dll} as no equivalent. + * + * 7) System calls are no yet reported. Windows programs do not use {@code fork} and {@code exec}. + * Instead, calls to {@code CreateProcess} cause the server to synthesize {@code Tattach} commands. + * + * 8) The {@code Tunwind1} command is not supported. Ghidra should unwind instead. + */ +public interface DbgModelGadpServer extends DbgEngGadpServer { + + /** + * The entry point for the SCTL-DBGENG server in stand-alone mode + * + * Run it to see help. + * + * @param args the command-line arguments + * @throws IOException if an I/O error occurs + * @throws ExecutionException + * @throws InterruptedException + */ + public static void main(String[] args) throws Exception { + new DbgModelRunner().run(args); + } + + /** + * Create a new instance of the server + * + * @param addr the address to bind the SCTL server to + * @param busId the client ID the server should use on the bus for synthesized commands + * @param dbgSrvTransport the transport specification for the {@code dbgeng.dll} server + * @return the server instance + * @throws IOException + */ + public static DbgModelGadpServer newInstance(SocketAddress addr) throws IOException { + return new DbgModelGadpServerImpl(addr); + } + + /** + * Runs the server from the command line or dbgeng javaprovider + */ + public class DbgModelRunner extends DbgEngRunner { + + public DbgModelRunner() { + } + + @Override + public void run(String args[]) + throws IOException, InterruptedException, ExecutionException { + parseArguments(args); + + try (DbgModelGadpServer server = newInstance(bindTo)) { + server.startDbgEng(dbgengArgs.toArray(new String[] {})).exceptionally(e -> { + Msg.error(this, "Error starting dbgeng/GADP", e); + System.exit(-1); + return null; + }); + new AgentWindow("dbgmodel.dll Agent for Ghidra", server.getLocalAddress()); + while (server.isRunning()) { + // TODO: Put consoleLoop back? + Thread.sleep(1000); + } + } + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelLocalDebuggerModelFactory.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelLocalDebuggerModelFactory.java new file mode 100644 index 0000000000..a8eadae2f6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelLocalDebuggerModelFactory.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.gadp; + +import agent.dbgeng.gadp.DbgEngLocalDebuggerModelFactory; +import ghidra.dbg.util.ConfigurableFactory.FactoryDescription; +import ghidra.util.classfinder.ExtensionPointProperties; + +@FactoryDescription( // + brief = "MS dbgmodel.dll (WinDbg 2) local agent via GADP/TCP", // + htmlDetails = "Launch a new agent using the Microsoft Debug Model (best for WinDbg 2)." // +) +@ExtensionPointProperties(priority = 90) +public class DbgModelLocalDebuggerModelFactory extends DbgEngLocalDebuggerModelFactory { + + @Override + protected String getThreadName() { + return "Local dbgmodel.dll Agent stdout"; + } + + @Override + protected Class getServerClass() { + return DbgModelGadpServer.class; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/DbgModelClientThreadExecutor.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/DbgModelClientThreadExecutor.java new file mode 100644 index 0000000000..7e061a83be --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/DbgModelClientThreadExecutor.java @@ -0,0 +1,78 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.gadp.impl; + +import java.util.function.Supplier; + +import agent.dbgeng.gadp.impl.AbstractClientThreadExecutor; +import agent.dbgeng.manager.DbgManager; +import agent.dbgmodel.dbgmodel.bridge.HostDataModelAccess; + +/** + * A single-threaded executor which creates and exclusively accesses the {@code dbgeng.dll} client. + * + * The executor also has a priority mechanism, so that callbacks may register follow-on handlers + * which take precedence over other tasks in the queue (which could trigger additional callbacks). + * This is required since certain operation are not allowed during normal callback processing. For + * example, changing the current process is typically not allowed, but it is necessary to retrieve a + * thread's context. + */ +public class DbgModelClientThreadExecutor extends AbstractClientThreadExecutor { + + private final Supplier makeAccess; + private WrappedDbgModel dbgmodel; + private DbgManager manager; + + /** + * Instantiate a new executor, providing a means of creating the client + * + * @param makeClient the callback to create the client + */ + public DbgModelClientThreadExecutor(Supplier makeClient) { + this.makeAccess = makeClient; + thread.setDaemon(true); + thread.start(); + } + + /** + * Obtain a reference to the client, only if the calling thread is this executor's thread. + * + * @return the client + */ + public WrappedDbgModel getAccess() { + if (thread != Thread.currentThread()) { + throw new AssertionError("Cannot get client outside owning thread"); + } + return dbgmodel; + } + + @Override + protected void init() { + dbgmodel = new WrappedDbgModel(makeAccess.get()); + client = dbgmodel.getClient(); + } + + @Override + public DbgManager getManager() { + return manager; + } + + @Override + public void setManager(DbgManager manager) { + this.manager = manager; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/DbgModelGadpServerImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/DbgModelGadpServerImpl.java new file mode 100644 index 0000000000..1dd1eb2fd9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/DbgModelGadpServerImpl.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.gadp.impl; + +import java.io.IOException; +import java.net.SocketAddress; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.AbstractDbgModel; +import agent.dbgmodel.gadp.DbgModelGadpServer; +import agent.dbgmodel.model.impl.DbgModel2Impl; +import ghidra.dbg.gadp.server.AbstractGadpServer; + +public class DbgModelGadpServerImpl implements DbgModelGadpServer { + public class GadpSide extends AbstractGadpServer { + public GadpSide(AbstractDbgModel model, SocketAddress addr) throws IOException { + super(model, addr); + } + } + + protected final AbstractDbgModel model; + protected final GadpSide server; + + public DbgModelGadpServerImpl(SocketAddress addr) throws IOException { + super(); + this.model = new DbgModel2Impl(); + this.server = new GadpSide(model, addr); + } + + @Override + public CompletableFuture startDbgEng(String[] args) { + return model.startDbgEng(args).thenCompose(__ -> server.launchAsyncService()); + } + + @Override + public SocketAddress getLocalAddress() { + return server.getLocalAddress(); + } + + @Override + public boolean isRunning() { + return model.isRunning(); + } + + @Override + public void terminate() throws IOException { + model.terminate(); + server.terminate(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/WrappedDbgModel.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/WrappedDbgModel.java new file mode 100644 index 0000000000..34fd0f3bad --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/WrappedDbgModel.java @@ -0,0 +1,969 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.gadp.impl; + +import java.nio.ByteBuffer; +import java.util.*; + +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.COM.COMException; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugRunningProcess.Description; +import agent.dbgeng.dbgeng.DebugRunningProcess.Description.ProcessDescriptionFlags; +import agent.dbgeng.dbgeng.DebugValue.*; +import agent.dbgmodel.dbgmodel.bridge.HostDataModelAccess; +import agent.dbgmodel.dbgmodel.debughost.*; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.DebugRunningProcessImpl; +import agent.dbgmodel.impl.dbgmodel.bridge.HDMAUtil; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import ghidra.comm.util.BitmaskSet; + +public class WrappedDbgModel + implements DebugClient, DebugSystemObjects, DebugRegisters, DebugDataSpaces, DebugSymbols { + + private HDMAUtil util; + private DebugClient client; + private HashMap map = new HashMap(); + private boolean USE_CLIENT = false; + + //private boolean USE_CLIENT = true; + + public WrappedDbgModel(HostDataModelAccess access) { + util = new HDMAUtil(access); + client = access.getClient(); + System.err.println(">>>>>>>>>>>> USING WRAPPED DBGMODEL <<<<<<<<<<<<<<"); + } + + public DebugClient getClient() { + return this; + } + + // Used to get ExitTime/Status for threads + @Override + public DebugAdvanced getAdvanced() { + return client.getAdvanced(); + } + + // Used for control, I/O, breakpoints + @Override + public DebugControl getControl() { + return client.getControl(); + } + + // Used to enumerate/read-write regions + @Override + public DebugDataSpaces getDataSpaces() { + return this; + } + + // Used to enumerate registers + @Override + public DebugRegisters getRegisters() { + return this; + } + + // Used to enumerate threads/processes + @Override + public DebugSystemObjects getSystemObjects() { + return this; + } + + // Used to enumerate symbols + @Override + public DebugSymbols getSymbols() { + return this; + } + + public Map getAttributes(List path) { + return getUtil().getAttributes(path); + } + + public List getElements(List path) { + return getUtil().getElements(path); + } + + public ModelObject getMethod(List path) { + return getUtil().getMethod(path); + } + + @Override + public void attachProcess(DebugServerId si, int processId, + BitmaskSet attachFlags) { + client.attachProcess(si, processId, attachFlags); + } + + @Override + public void createProcess(DebugServerId si, String commandLine, + BitmaskSet createFlags) { + client.createProcess(si, commandLine, createFlags); + } + + @Override + public void createProcessAndAttach(DebugServerId si, String commandLine, + BitmaskSet createFlags, int processId, + BitmaskSet attachFlags) { + client.createProcessAndAttach(si, commandLine, createFlags, processId, attachFlags); + } + + @Override + public void abandonCurrentProcess() { + client.abandonCurrentProcess(); + } + + @Override + public void startServer(String options) { + client.startServer(options); + } + + @Override + public void waitForProcessServerEnd(int timeout) { + client.waitForProcessServerEnd(timeout); + } + + @Override + public void terminateCurrentProcess() { + client.terminateCurrentProcess(); + } + + @Override + public void detachCurrentProcess() { + client.detachCurrentProcess(); + } + + @Override + public void connectSession(int flags) { + client.connectSession(flags); + } + + @Override + public void endSession(DebugEndSessionFlags flags) { + client.endSession(flags); + } + + public HDMAUtil getUtil() { + return util; + } + + @Override + public DebugClient createClient() { + return client; + } + + @Override + public void endSessionReentrant() { + client.endSessionReentrant(); + } + + @Override + public DebugServerId getLocalServer() { + return client.getLocalServer(); + } + + @Override + public void attachKernel(long flags, String options) { + client.attachKernel(flags, options); + } + + @Override + public void startProcessServer(String options) { + client.startProcessServer(options); + } + + @Override + public DebugServerId connectProcessServer(String options) { + return client.connectProcessServer(options); + } + + @Override + public boolean dispatchCallbacks(int timeout) { + return client.dispatchCallbacks(timeout); + } + + @Override + public void flushCallbacks() { + client.flushCallbacks(); + } + + @Override + public void exitDispatch(DebugClient client) { + client.exitDispatch(client); + } + + @Override + public void setInputCallbacks(DebugInputCallbacks cb) { + client.setInputCallbacks(cb); + } + + @Override + public void setOutputCallbacks(DebugOutputCallbacks cb) { + client.setOutputCallbacks(cb); + } + + @Override + public void setEventCallbacks(DebugEventCallbacks cb) { + client.setEventCallbacks(cb); + } + + @Override + public Description getProcessDescription(DebugServerId si, int systemId, + BitmaskSet flags) { + return client.getProcessDescription(si, systemId, flags); + } + + @Override + public void openDumpFileWide(String fileName) { + client.openDumpFileWide(fileName); + } + + // DATA SPACES INTERFACE + + @Override + public int readVirtual(long offset, ByteBuffer buf, int remaining) { + DebugHostContext currentContext = getUtil().getCurrentContext(); + DebugHost host = getUtil().getHost(); + ULONGLONG ulOffset = new ULONGLONG(offset); + LOCATION base = new LOCATION(ulOffset); + DebugHostMemory1 memory = host.asMemory(); + return (int) memory.readBytes(currentContext, base, buf, remaining); + } + + @Override + public int writeVirtual(long offset, ByteBuffer buf, int remaining) { + // UNTESTED + DebugHostContext currentContext = getUtil().getCurrentContext(); + DebugHost host = getUtil().getHost(); + ULONGLONG ulOffset = new ULONGLONG(offset); + LOCATION base = new LOCATION(ulOffset); + DebugHostMemory1 memory = host.asMemory(); + return (int) memory.writeBytes(currentContext, base, buf, remaining); + } + + @Override + public int readVirtualUncached(long offset, ByteBuffer into, int len) { + return readVirtual(offset, into, len); + } + + @Override + public int writeVirtualUncached(long offset, ByteBuffer from, int len) { + return readVirtual(offset, from, len); + } + + // TODO what follows is untested + + @Override + public int readPhysical(long offset, ByteBuffer buf, int remaining) { + return client.getDataSpaces().readPhysical(offset, buf, remaining); + } + + @Override + public int writePhysical(long offset, ByteBuffer buf, int remaining) { + return client.getDataSpaces().writePhysical(offset, buf, remaining); + } + + @Override + public int readControl(int processor, long offset, ByteBuffer buf, int remaining) { + return client.getDataSpaces().readControl(processor, offset, buf, remaining); + } + + @Override + public int writeControl(int processor, long offset, ByteBuffer buf, int remaining) { + return client.getDataSpaces().writeControl(processor, offset, buf, remaining); + } + + @Override + public int readBusData(int busDataType, int busNumber, int slotNumber, long offset, + ByteBuffer buf, int remaining) { + return client.getDataSpaces() + .readBusData(busDataType, busNumber, slotNumber, offset, buf, remaining); + } + + @Override + public int writeBusData(int busDataType, int busNumber, int slotNumber, long offset, + ByteBuffer buf, int remaining) { + return client.getDataSpaces() + .writeBusData(busDataType, busNumber, slotNumber, offset, buf, remaining); + } + + @Override + public int readIo(int interfaceType, int busNumber, int addressSpace, long offset, + ByteBuffer buf, int remaining) { + return client.getDataSpaces() + .readIo(interfaceType, busNumber, addressSpace, offset, buf, remaining); + } + + @Override + public int writeIo(int interfaceType, int busNumber, int addressSpace, long offset, + ByteBuffer buf, int remaining) { + return client.getDataSpaces() + .writeIo(interfaceType, busNumber, addressSpace, offset, buf, remaining); + } + + @Override + public long readMsr(int msr) { + return client.getDataSpaces().readMsr(msr); + } + + @Override + public void writeMsr(int msr, long value) { + client.getDataSpaces().writeMsr(msr, value); + } + + @Override + public int readDebuggerData(int offset, ByteBuffer buf, int remaining) { + DebugHostContext currentContext = getUtil().getCurrentContext(); + DebugHost host = getUtil().getHost(); + ULONGLONG ulOffset = new ULONGLONG(offset); + LOCATION base = new LOCATION(ulOffset); + DebugHostMemory1 memory = host.asMemory(); + return (int) memory.readBytes(currentContext, base, buf, remaining); + } + + @Override + public DebugMemoryBasicInformation queryVirtual(long offset) { + return client.getDataSpaces().queryVirtual(offset); + } + + // REGISTERS INTERFACE + + public DebugRegisterDescription getRegisterDescription(int i) { + return client.getRegisters().getDescription(i); + /* + if (USE_CLIENT) { + System.err.println("getRegisterDescription"); + return client.getRegisters().getDescription(i); + } + */ + /* + Map registerMap = getRegisterMap(); + Object[] array = registerMap.keySet().toArray(); + if (i > array.length) { + DebugRegisterDescription description = client.getRegisters().getDescription(i); + System.err.println(description.name + " not found"); + return null; + } + String regname = (String) array[i]; + ModelObject register = registerMap.get(regname); + DebugRegisterDescription drp = new DebugRegisterDescription(regname, i, getDVType(register), + new BitmaskSet<>(DebugRegisterFlags.class, 0), 0, 0, 0, 0); + return drp; + */ + } + + public Set getAllRegisterDescriptions() { + return client.getRegisters().getAllDescriptions(); + /* + if (USE_CLIENT) { + System.err.println("getAllRegisterDescriptions"); + return client.getRegisters().getAllDescriptions(); + } + */ + /* + Set set = new HashSet(); + Map registerMap = getRegisterMap(); + Object[] array = registerMap.keySet().toArray(); + for (int i = 0; i < array.length; i++) { + String regname = (String) array[i]; + if (regname == null) { + continue; + } + ModelObject register = registerMap.get(regname); + DebugRegisterDescription drp = new DebugRegisterDescription(regname, i, + getDVType(register), new BitmaskSet<>(DebugRegisterFlags.class, 0), 0, 0, 0, 0); + set.add(drp); + } + return set; + */ + } + + /* + public void setRegisterValue(Entry ent) { + System.err.println("setRegisterValue"); + Map registerMap = getRegisterMap(); + ModelObject register = registerMap.get(ent.getKey()); + DebugValue value = decodeBytes(register, ent.getValue()); + client.getRegisters().setValueByName(ent.getKey(), value); + } + */ + + @Override + public int getNumberRegisters() { + return client.getRegisters().getNumberRegisters(); + //return getRegisterMap().size(); + } + + @Override + public DebugRegisterDescription getDescription(int registerNumber) { + return getRegisterDescription(registerNumber); + } + + @Override + public int getIndexByName(String name) { + return client.getRegisters().getIndexByName(name); + /* + if (USE_CLIENT) { + System.err.println("getAllRegisterDescriptions"); + return client.getRegisters().getIndexByName(name); + } + */ + /* + TreeMap registerMap = (TreeMap) getRegisterMap(); + Object[] array = registerMap.keySet().toArray(); + for (int i = 0; i < array.length; i++) { + String key = (String) array[i]; + if (key.equals(name)) { + return i; + } + } + return -1; + */ + } + + @Override + public DebugValue getValueByName(String name) { + return client.getRegisters().getValueByName(name); + //TreeMap registerMap = (TreeMap) getRegisterMap(); + //ModelObject obj = registerMap.get(name); + //return getDebugValue(obj); + } + + @Override + public DebugValue getValue(int index) { + return client.getRegisters().getValue(index); + /* + if (USE_CLIENT) { + System.err.println("getAllRegisterDescriptions"); + return client.getRegisters().getValue(index); + } + */ + /* + TreeMap registerMap = (TreeMap) getRegisterMap(); + Object[] array = registerMap.values().toArray(); + if (index >= 0 && index < array.length) { + ModelObject obj = (ModelObject) array[index]; + return getDebugValue(obj); + } + return null; + */ + } + + @Override + public Map getValues(DebugRegisterSource source, + Collection indices) { + return client.getRegisters().getValues(source, indices); + /* + if (USE_CLIENT) { + System.err.println("getAllRegisterDescriptions"); + return client.getRegisters().getValues(source, indices); + } + */ + /* + TreeMap registerMap = (TreeMap) getRegisterMap(); + Object[] array = registerMap.keySet().toArray(); + Map registers = new TreeMap(); + for (Integer index : indices) { + String key = (String) array[index]; + ModelObject obj = registerMap.get(key); + registers.put(index, getDebugValue(obj)); + } + return registers; + */ + } + + @Override + public void setValueByName(String name, DebugValue value) { + setValue(getIndexByName(name), value); + } + + @Override + public void setValue(int index, DebugValue value) { + client.getRegisters().setValue(index, value); + } + + @Override + public void setValues(DebugRegisterSource source, Map values) { + client.getRegisters().setValues(source, values); + } + + // SYSTEM OBJECT INTERFACE + + public List getThreadIds() { + List ids = null; + /* + if (USE_CLIENT) { + System.err.println("getThreadIds"); + ids = client.getSystemObjects().getThreads(); + return ids; + } + */ + ModelObject currentSession = getUtil().getCurrentSession(); + ModelObject currentProcess = getUtil().getCurrentProcess(); + String pid = getUtil().getCtlId(currentProcess); + List runningThreads = getUtil().getRunningThreads(currentSession, pid); + ids = new ArrayList(); + for (ModelObject t : runningThreads) { + String tid = getUtil().getCtlId(t); + ids.add(tid2dti(tid)); + } + return ids; + } + + public List getProcessIds() { + /* + if (USE_CLIENT) { + System.err.println("getProcessIds"); + return client.getSystemObjects().getProcesses(); + } + */ + // TODO: What follow is problematic. SO.getProcesses uses SO.GetNumberProcesses + // and GetProcessIdsByIndex, which return the processes being debugged, not the + // system process list. Using the system process list and calling getProcessIdBySystemID + // triggers unsupported interface COM exceptions + List ids = new ArrayList(); + List procs = getRunningProcesses(sid2dsi()); + for (DebugRunningProcess p : procs) { + try { + ids.add(pid2dpi("0x" + Integer.toHexString(p.getSystemId()))); + } + catch (COMException e) { + System.err.println(p.getSystemId()); + } + } + return ids; + } + + @Override + public void setCurrentSystemId(DebugSessionId dpi) { + client.getSystemObjects().setCurrentSystemId(dpi); + /* + if (USE_CLIENT) { + System.err.println("setCurrentProcess"); + client.getSystemObjects().setCurrentProcessId(dpi); + return; + } + *//* DOESN'T WORK + String pid = obj2id(dpi); + ModelObject currentProcess = util.getCurrentProcess(); + util.setCurrentProcess(currentProcess, pid); + */ + } + + @Override + public void setCurrentProcessId(DebugProcessId dpi) { + client.getSystemObjects().setCurrentProcessId(dpi); + /* + if (USE_CLIENT) { + System.err.println("setCurrentProcess"); + client.getSystemObjects().setCurrentProcessId(dpi); + return; + } + *//* DOESN'T WORK + String pid = obj2id(dpi); + ModelObject currentProcess = util.getCurrentProcess(); + util.setCurrentProcess(currentProcess, pid); + */ + } + + @Override + public void setCurrentThreadId(DebugThreadId dti) { + client.getSystemObjects().setCurrentThreadId(dti); + /* + if (USE_CLIENT) { + System.err.println("setCurrentThread"); + client.getSystemObjects().setCurrentThreadId(dti); + return; + } + *//* DOESN'T WORK + String tid = obj2id(dti); + ModelObject currentThread = util.getCurrentThread(); + util.setCurrentThread(currentThread, tid); + */ + } + + @Override + public DebugSessionId getCurrentSystemId() { + /* + if (USE_CLIENT) { + System.err.println("getCurrentSystemId"); + return client.getSystemObjects().getCurrentSystemId(); + } + */ + return client.getSystemObjects().getCurrentSystemId(); + } + + @Override + public DebugProcessId getCurrentProcessId() { + /* + if (USE_CLIENT) { + System.err.println("getCurrentProcessId"); + return client.getSystemObjects().getCurrentProcessId(); + } + */ + ModelObject currentProcess = getUtil().getCurrentProcess(); + DebugProcessId dpi = client.getSystemObjects().getCurrentProcessId(); + if (currentProcess != null) { + String id = getUtil().getCtlId(currentProcess); + addObj(dpi, id); + } + return dpi; + } + + @Override + public DebugThreadId getCurrentThreadId() { + /* + if (USE_CLIENT) { + System.err.println("getCurrentThreadId"); + return client.getSystemObjects().getCurrentThreadId(); + } + */ + ModelObject currentThread = getUtil().getCurrentThread(); + DebugThreadId dti = client.getSystemObjects().getCurrentThreadId(); + if (currentThread != null) { + String id = getUtil().getCtlId(currentThread); + addObj(dti, id); + } + return dti; + } + + @Override + public DebugProcessId getProcessIdByHandle(long handle) { + // "Handle" key exists for processes, but not threads currently + return client.getSystemObjects().getProcessIdByHandle(handle); + } + + @Override + public DebugThreadId getThreadIdByHandle(long handle) { + // "Handle" key exists for processes, but not threads currently + return client.getSystemObjects().getThreadIdByHandle(handle); + } + + @Override + public DebugSessionId getEventSystem() { + /* + if (USE_CLIENT) { + System.err.println("getEventProcess"); + return client.getSystemObjects().getEventProcess(); + } + */ + return client.getSystemObjects().getEventSystem(); + //return getCurrentProcessId(); + } + + @Override + public DebugProcessId getEventProcess() { + /* + if (USE_CLIENT) { + System.err.println("getEventProcess"); + return client.getSystemObjects().getEventProcess(); + } + */ + return client.getSystemObjects().getEventProcess(); + //return getCurrentProcessId(); + } + + @Override + public DebugThreadId getEventThread() { + /* + if (USE_CLIENT) { + System.err.println("getEventThread"); + return client.getSystemObjects().getEventThread(); + } + */ + return client.getSystemObjects().getEventThread(); + //return getCurrentThreadId(); + } + + @Override + public List getRunningProcesses(DebugServerId si) { + /* + if (USE_CLIENT) { + System.err.println("getRunningProcesses"); + return client.getRunningProcesses(si); + } + */ + List processes = getUtil().getRunningProcesses(obj2id(si)); + List result = new ArrayList<>(processes.size()); + for (ModelObject child : processes) { + String pid = getUtil().getCtlId(child); + result.add(new DebugRunningProcessImpl(pid, child, si)); + } + return result; + } + + @Override + public int getCurrentThreadSystemId() { // used by impl + return client.getSystemObjects().getCurrentThreadSystemId(); + //return getCurrentThreadId().id; + } + + @Override + public int getCurrentProcessSystemId() { // used by impl + return client.getSystemObjects().getCurrentProcessSystemId(); + //return getCurrentProcessId().id; + } + + @Override + public int getNumberThreads() { // used by dso + return client.getSystemObjects().getNumberThreads(); + } + + @Override + public List getThreads(int start, int count) { // used by dso + return client.getSystemObjects().getThreads(start, count); + } + + @Override + public int getNumberProcesses() { // used by dso + return client.getSystemObjects().getNumberProcesses(); + } + + @Override + public List getProcesses(int start, int count) { // used by dso + return client.getSystemObjects().getProcesses(start, count); + } + + @Override + public int getNumberSystems() { // used by dso + return client.getSystemObjects().getNumberSystems(); + } + + @Override + public List getSystems(int start, int count) { // used by dso + return client.getSystemObjects().getSystems(start, count); + } + + @Override + public DebugThreadId getThreadIdBySystemId(int systemId) { // used by this + return client.getSystemObjects().getThreadIdBySystemId(systemId); + } + + @Override + public DebugProcessId getProcessIdBySystemId(int systemId) { // used by this + return client.getSystemObjects().getProcessIdBySystemId(systemId); + } + + @Override + public int getTotalNumberThreads() { // unused + return client.getSystemObjects().getTotalNumberThreads(); + } + + // SYMBOLS INTERFACE + + public List getDebugHostModules() { + DebugHostSymbols symbols = getUtil().getHost().asSymbols(); + DebugHostSymbolEnumerator enumerator = + symbols.enumerateModules(getUtil().getCurrentContext()); + List list = new ArrayList(); + DebugHostSymbol1 next; + while ((next = enumerator.getNext()) != null) { + list.add(next.asModule()); + } + return list; + } + + public List getModuleList() { + return getUtil().getModuleList(); + } + + @Override + public int getNumberLoadedModules() { + return getModuleList().size(); + } + + @Override + public int getNumberUnloadedModules() { + return client.getSymbols().getNumberUnloadedModules(); + } + + @Override + public DebugModule getModuleByIndex(int index) { + List modules = getModuleList(); + return modules.get(index); + } + + @Override + public DebugModule getModuleByModuleName(String name, int startIndex) { + List modules = getModuleList(); + for (DebugModule module : modules) { + String moduleName = module.getName(null); + if (moduleName.equals(name)) { + return module; + } + } + System.err.println(name + " not found"); + return null; + } + + @Override + public DebugModule getModuleByOffset(long offset, int startIndex) { + List modules = getModuleList(); + long min = Long.MAX_VALUE; + DebugModule ret = null; + for (DebugModule module : modules) { + long baseAddress = module.getBase(); + if (offset >= baseAddress && baseAddress < min) { + min = baseAddress; + ret = module; + } + } + return ret; + } + + @Override + public Iterable iterateSymbolMatches(String pattern) { + return client.getSymbols().iterateSymbolMatches(pattern); + } + + @Override + public List getSymbolIdsByName(String pattern) { + return client.getSymbols().getSymbolIdsByName(pattern); + } + + @Override + public DebugSymbolEntry getSymbolEntry(DebugSymbolId id) { + return client.getSymbols().getSymbolEntry(id); + } + + @Override + public String getSymbolPath() { + return client.getSymbols().getSymbolPath(); + } + + @Override + public void setSymbolPath(String path) { + client.getSymbols().setSymbolPath(path); + } + + @Override + public int getSymbolOptions() { + return client.getSymbols().getSymbolOptions(); + } + + @Override + public void setSymbolOptions(int options) { + client.getSymbols().setSymbolOptions(options); + } + + // UTILITY METHODS + + private DebugServerId sid2dsi() { + DebugServerId dsi = new DebugServerId(0); + addObj(dsi, "0"); + return dsi; + } + + private DebugProcessId pid2dpi(String id) { + if (!id.startsWith("0x")) { + id = "0x" + id; + } + int pid = Integer.decode(id); + if (pid == 0) { + return new DebugProcessId(-1); + } + DebugProcessId dpi = client.getSystemObjects().getProcessIdBySystemId(pid); + addObj(dpi, id); + return dpi; + } + + private DebugThreadId tid2dti(String id) { + if (!id.startsWith("0x")) { + id = "0x" + id; + } + int tid = Integer.decode(id); + DebugThreadId dti = client.getSystemObjects().getThreadIdBySystemId(tid); + addObj(dti, id); + return dti; + } + + private void addObj(Object obj, String id) { + if (obj == null || id == null) { + System.err.println("attempt to add null object"); + } + map.put(obj, id); + } + + private String obj2id(Object obj) { + String id = map.get(obj); + return id; + } + + public DebugValue getDebugValue(ModelObject register) { + DebugValue dv = null; + Object value = register.getValue(); + if (value instanceof Short) { + dv = new DebugInt16Value(((Short) value).shortValue()); + } + else if (value instanceof Integer) { + dv = new DebugInt32Value(((Integer) value).intValue()); + } + else if (value instanceof Long) { + dv = new DebugInt64Value(((Long) value).longValue()); + } + return dv; + } + + public byte[] encodeAsBytes(ModelObject register) { + DebugValue dv = null; + Object value = register.getValue(); + if (value instanceof Short) { + Short lval = (Short) value; + dv = new DebugInt16Value(lval); + } + else if (value instanceof Integer) { + Integer lval = (Integer) value; + dv = new DebugInt32Value(lval); + } + else if (value instanceof Long) { + Long lval = (Long) value; + dv = new DebugInt64Value(lval); + } + else { + return new byte[0]; + } + return dv.encodeAsBytes(); + } + + private DebugValue decodeBytes(ModelObject register, byte[] bytes) { + DebugValue dv = null; + Object value = register.getValue(); + if (value instanceof Short) { + dv = new DebugInt16Value(bytes); + } + else if (value instanceof Integer) { + dv = new DebugInt32Value(bytes); + } + else if (value instanceof Long) { + dv = new DebugInt64Value(bytes); + } + return dv; + } + + private DebugValueType getDVType(ModelObject register) { + Object value = register.getValue(); + if (value instanceof Short) { + return DebugValueType.INT16; + } + else if (value instanceof Integer) { + return DebugValueType.INT32; + } + else if (value instanceof Long) { + return DebugValueType.INT64; + } + return DebugValueType.INVALID; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/DbgModelUtil.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/DbgModelUtil.java new file mode 100644 index 0000000000..006975c9d4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/DbgModelUtil.java @@ -0,0 +1,88 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.function.Function; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.*; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.err.DbgModelRuntimeException; +import ghidra.util.Msg; + +public abstract class DbgModelUtil { + public static final ULONG DEBUG_ANY_ID = new ULONG(-1); + + private DbgModelUtil() { + } + + public static interface InterfaceSupplier { + HRESULT get(REFIID refiid, PointerByReference pClient); + } + + @SuppressWarnings("unchecked") + public static I tryPreferredInterfaces(Class cls, + Map> preferred, InterfaceSupplier supplier) { + PointerByReference ppClient = new PointerByReference(); + for (Map.Entry> ent : preferred.entrySet()) { + try { + COMUtils.checkRC(supplier.get(ent.getKey(), ppClient)); + if (ppClient.getValue() == null) { + continue; + } + Object impl = + ent.getValue().getConstructor(Pointer.class).newInstance(ppClient.getValue()); + Method instanceFor = cls.getMethod("instanceFor", ent.getValue()); + Object instance = instanceFor.invoke(null, impl); + return (I) instance; + } + catch (COMException e) { + Msg.debug(DbgModelUtil.class, e + " (" + ent.getValue() + ")"); + // TODO: Only try next on E_NOINTERFACE? + // Try next + } + catch (Exception e) { + throw new AssertionError("INTERNAL: Unexpected exception", e); + } + } + throw new DbgModelRuntimeException("None of the preferred interfaces are supported"); + } + + public static U lazyWeakCache(Map cache, T unk, + Function forNew) { + synchronized (cache) { + U present = cache.get(unk.getPointer()); + if (present != null) { + unk.Release(); + return present; + } + U absent = forNew.apply(unk); + cache.put(unk.getPointer(), absent); + return absent; + } + } + + public static void dbgline() { + System.out.println(new Exception().getStackTrace()[1]); + System.out.flush(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/DebugRunningProcessImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/DebugRunningProcessImpl.java new file mode 100644 index 0000000000..3dc205750f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/DebugRunningProcessImpl.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel; + +import agent.dbgeng.dbgeng.DebugRunningProcess; +import agent.dbgeng.dbgeng.DebugServerId; +import agent.dbgmodel.dbgmodel.main.ModelObject; + +public class DebugRunningProcessImpl + implements DebugRunningProcess, Comparable { + + public DebugRunningProcessImpl(String id, ModelObject object, DebugServerId server) { + this.object = object; + this.server = server; + this.systemId = Integer.decode(id); + } + + protected final ModelObject object; + protected final DebugServerId server; + protected final int systemId; + + @Override + public int getSystemId() { + return systemId; + } + + @Override + public Description getFullDescription(Description.ProcessDescriptionFlags... flags) { + return new Description(systemId, object.getSearchKey(), object.toString()); + } + + @Override + public String getExecutableName(Description.ProcessDescriptionFlags... flags) { + return getFullDescription(flags).getExecutableName(); + } + + @Override + public String getDescription(Description.ProcessDescriptionFlags... flags) { + return getFullDescription(flags).getDescription(); + } + + @Override + public int compareTo(DebugRunningProcessImpl that) { + int result; + + result = Integer.compare(this.systemId, that.systemId); + if (result != 0) { + return result; + } + + return 0; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/UnknownExImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/UnknownExImpl.java new file mode 100644 index 0000000000..5aa87e9273 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/UnknownExImpl.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.COM.IUnknown; + +import agent.dbgmodel.dbgmodel.main.KeyStore; + +public class UnknownExImpl implements UnknownExInternal { + + private final IUnknown jnaData; + private KeyStore metadata; + + public UnknownExImpl(IUnknown jnaData) { + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return null; + } + + public void setMetadata(KeyStore metadata) { + this.metadata = metadata; + } + + public KeyStore getMetadata() { + return metadata; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/UnknownExInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/UnknownExInternal.java new file mode 100644 index 0000000000..e4bd6d441a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/UnknownExInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.WrapIUnknownEx; + +import com.sun.jna.platform.win32.COM.IUnknown; + +import ghidra.util.datastruct.WeakValueHashMap; + +public interface UnknownExInternal extends UnknownEx { + Map CACHE = new WeakValueHashMap<>(); + + static UnknownExInternal instanceFor(WrapIUnknownEx data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, UnknownExImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IUnknown.IID_IUNKNOWN), WrapIUnknownEx.class) // + .build(); + + static UnknownExInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(UnknownExInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HDMAUtil.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HDMAUtil.java new file mode 100644 index 0000000000..227542966f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HDMAUtil.java @@ -0,0 +1,268 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.bridge; + +import java.util.*; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Variant.VARIANT; + +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgeng.dbgeng.DebugModule; +import agent.dbgmodel.dbgmodel.bridge.HostDataModelAccess; +import agent.dbgmodel.dbgmodel.datamodel.DataModelManager1; +import agent.dbgmodel.dbgmodel.debughost.*; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ModelObjectKind; +import ghidra.dbg.util.PathUtils; + +public class HDMAUtil { + + private DataModelManager1 manager; + private DebugHost host; + private DebugClient client; + + public HDMAUtil(HostDataModelAccess access) { + manager = access.getManager(); + host = access.getHost(); + client = access.getClient(); + } + + public DataModelManager1 getManager() { + return manager; + } + + public DebugHost getHost() { + return host; + } + + public ModelObject getRootNamespace() { + return getManager().getRootNamespace(); + } + + public DebugHostContext getCurrentContext() { + return getHost().getCurrentContext(); + } + + public ModelObject getSessionOf(DebugHostContext obj) { + return getRootNamespace().getKeyValue("Debugger").getKeyValue("Sessions");//[obj] + } + + public ModelObject getProcessOf(DebugHostContext obj) { + return getSessionOf(obj).getKeyValue("Processes");//[obj] + } + + public ModelObject getThreadOf(DebugHostContext obj) { + return getProcessOf(obj).getKeyValue("Threads");//[obj] + } + + public VARIANT string2variant(String id) { + Integer decode = id == null ? 0 : Integer.decode(id); + return new VARIANT(decode); + } + + public Map getAttributes(List path) { + ModelObject target = getTerminalModelObject(path); + if (target == null) { + System.err.println("(A) Null target for path=" + path); + return new HashMap(); + } + ModelObjectKind kind = target.getKind(); + if (kind.equals(ModelObjectKind.OBJECT_ERROR)) { + HashMap map = new HashMap(); + map.put("ERROR", target); + return map; + } + if (kind.equals(ModelObjectKind.OBJECT_INTRINSIC) || + kind.equals(ModelObjectKind.OBJECT_TARGET_OBJECT) || + kind.equals(ModelObjectKind.OBJECT_TARGET_OBJECT_REFERENCE)) { + return target.getRawValueMap(); + } + return target.getKeyValueMap(); + } + + public List getElements(List path) { + ModelObject target = getTerminalModelObject(path); + if (target == null) { + System.err.println("(C) Null target for path=" + path); + return new ArrayList(); + } + ModelObjectKind kind = target.getKind(); + if (kind.equals(ModelObjectKind.OBJECT_ERROR)) { + List list = new ArrayList(); + list.add(target); + return list; + } + return target.getElements(); + } + + public ModelObject getMethod(List path) { + DebugHostEvaluator2 eval = host.asEvaluator(); + DebugHostContext context = host.getCurrentContext(); + List npath = PathUtils.parent(path); + int last = path.size() - 1; + String cmd = path.get(last); + ModelObject parentModel = getTerminalModelObject(npath); + return eval.evaluateExtendedExpression(context, new WString(cmd), parentModel); + } + + public ModelObject getTerminalModelObject(List path) { + //System.err.println(path); + ModelObject target = getRootNamespace(); + for (String str : path) { + //System.err.println(":" + str); + String indexStr = null; + if (str.endsWith(")")) { + target = evaluatePredicate(target, str); + if (target.getKind().equals(ModelObjectKind.OBJECT_ERROR)) { + return target; + } + } + if (str.endsWith("]")) { + indexStr = str.substring(str.indexOf("[") + 1, str.indexOf("]")); + str = str.substring(0, str.indexOf("[")); + } + Map keyMap = target.getKeyValueMap(); + if (keyMap.containsKey(str)) { + target = keyMap.get(str); + } + else { + Map rawMap = target.getRawValueMap(); + if (rawMap.containsKey(str)) { + target = rawMap.get(str); + } + } + if (indexStr != null) { + List children = target.getElements(); + for (ModelObject child : children) { + if (indexStr.equals(child.getSearchKey())) { + target = child; + } + } + } + } + return target; + } + + private ModelObject evaluatePredicate(ModelObject target, String call) { + DebugHostEvaluator2 eval = host.asEvaluator(); + DebugHostContext context = host.getCurrentContext(); + return eval.evaluateExtendedExpression(context, new WString(call), target); + } + + public ModelObject getSession(String id) { + ModelObject sessions = getRootNamespace().getKeyValue("Debugger").getKeyValue("Sessions"); + return sessions.getChild(manager, string2variant(id)); + } + + public ModelObject getProcess(ModelObject session, String id) { + ModelObject processes = session.getKeyValue("Processes"); + return processes.getChild(manager, string2variant(id)); + } + + public ModelObject getThread(ModelObject process, String id) { + ModelObject threads = process.getKeyValue("Threads"); + return threads.getChild(manager, string2variant(id)); + } + + public ModelObject getSettings() { + return getRootNamespace().getKeyValue("Debugger").getKeyValue("Settings"); + } + + public ModelObject getVariables() { + return getRootNamespace().getKeyValue("Debugger") + .getKeyValue("State") + .getKeyValue("DebuggerVariables"); + } + + public ModelObject getCurrentSession() { + return getVariables().getKeyValue("cursession"); + } + + public ModelObject getCurrentProcess() { + return getVariables().getKeyValue("curprocess"); + } + + public ModelObject getCurrentThread() { + return getVariables().getKeyValue("curthread"); + } + + public ModelObject getCurrentStack() { + return getCurrentThread().getKeyValue("Stack"); + } + + public ModelObject getCurrentFrame() { + return getVariables().getKeyValue("curframe"); + } + + public List getCurrentModuleList() { + ModelObject process = getCurrentProcess(); + ModelObject modules = process.getKeyValue("Modules"); + return modules.getElements(); + } + + public List getModuleList() { + DebugHostSymbols symbols = host.asSymbols(); + DebugHostSymbolEnumerator enumerator = symbols.enumerateModules(getCurrentContext()); + List modules = new ArrayList(); + DebugHostSymbol1 next; + int index = 0; + while ((next = enumerator.getNext()) != null) { + DebugHostModule1 module = next.asModule(); + String name = module.getName(); + //BSTR imageName = module.getImageName(true); + //LOCATION base = module.getBaseLocation(); + //long baseAddress = base.Offset.longValue(); + DebugModule debugModule = client.getSymbols().getModuleByModuleName(name, index++); + //DebugModule debugModule = new DbgModelModule(name, baseAddress, index++); + modules.add(debugModule); + } + return modules; + } + + public List getRunningProcesses(String id) { + ModelObject session = getSession(id); + ModelObject processes = session.getKeyValue("Processes"); + return processes.getElements(); + } + + public List getRunningThreads(ModelObject session, String id) { + ModelObject process = getProcess(session, id); + ModelObject threads = process.getKeyValue("Threads"); + return threads.getElements(); + } + + public ModelObject getProcessDescription(String sid, int systemId) { + ModelObject session = getSession(sid); + return getProcess(session, Integer.toHexString(systemId)); + } + + public void setCurrentProcess(ModelObject context, String id) { + VARIANT v = new VARIANT(id); + context.switchTo(manager, v); + } + + public void setCurrentThread(ModelObject context, String id) { + VARIANT v = new VARIANT(id); + context.switchTo(manager, v); + } + + public String getCtlId(ModelObject object) { + ModelObject value = object.getKeyValue("Id"); + return value == null ? "" : value.getValueString(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HostDataModelAccessImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HostDataModelAccessImpl.java new file mode 100644 index 0000000000..bc914b0221 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HostDataModelAccessImpl.java @@ -0,0 +1,105 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.bridge; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.dbgeng.DebugClient; +import agent.dbgeng.impl.dbgeng.client.DebugClientImpl1; +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.datamodel.DataModelManager1; +import agent.dbgmodel.dbgmodel.debughost.DebugHost; +import agent.dbgmodel.impl.dbgmodel.datamodel.DataModelManagerInternal; +import agent.dbgmodel.impl.dbgmodel.debughost.DebugHostInternal; +import agent.dbgmodel.jna.dbgmodel.bridge.IHostDataModelAccess; +import agent.dbgmodel.jna.dbgmodel.datamodel.WrapIDataModelManager1; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHost; + +public class HostDataModelAccessImpl implements HostDataModelAccessInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IHostDataModelAccess jnaData; + + private DataModelManager1 manager; + private DebugHost host; + private DebugClient debugClient; + + public HostDataModelAccessImpl(IHostDataModelAccess jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + public HostDataModelAccessImpl(DebugClient debugClient) { + DebugClientImpl1 impl = (DebugClientImpl1) debugClient; + this.jnaData = (IHostDataModelAccess) impl.getJNAClient(); + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + public IHostDataModelAccess getJNAData() { + return jnaData; + } + + @Override + public void getDataModel() { + PointerByReference ppManager = new PointerByReference(); + PointerByReference ppHost = new PointerByReference(); + COMUtils.checkRC(jnaData.GetDataModel(ppManager, ppHost)); + + WrapIDataModelManager1 wrap0 = new WrapIDataModelManager1(ppManager.getValue()); + try { + manager = DataModelManagerInternal.tryPreferredInterfaces(wrap0::QueryInterface); + } + finally { + wrap0.Release(); + } + WrapIDebugHost wrap1 = new WrapIDebugHost(ppHost.getValue()); + try { + host = DebugHostInternal.tryPreferredInterfaces(wrap1::QueryInterface); + } + finally { + wrap1.Release(); + } + } + + @Override + public DataModelManager1 getManager() { + return manager; + } + + @Override + public DebugHost getHost() { + return host; + } + + @Override + public DebugClient getClient() { + return debugClient; + } + + @Override + public void setClient(DebugClient debugClient) { + this.debugClient = debugClient; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HostDataModelAccessInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HostDataModelAccessInternal.java new file mode 100644 index 0000000000..17fb32e86e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/bridge/HostDataModelAccessInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.bridge; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.bridge.HostDataModelAccess; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.bridge.IHostDataModelAccess; +import agent.dbgmodel.jna.dbgmodel.bridge.WrapIHostDataModelAccess; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface HostDataModelAccessInternal extends HostDataModelAccess { + Map CACHE = new WeakValueHashMap<>(); + + static HostDataModelAccessInternal instanceFor(WrapIHostDataModelAccess data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, HostDataModelAccessImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IHostDataModelAccess.IID_IHOST_DATA_MODEL_ACCESS), + WrapIHostDataModelAccess.class) // + .build(); + + static HostDataModelAccessInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(HostDataModelAccessInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/ComparableConceptImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/ComparableConceptImpl.java new file mode 100644 index 0000000000..476cf6e279 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/ComparableConceptImpl.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.concept.IComparableConcept; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class ComparableConceptImpl implements ComparableConceptInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IComparableConcept jnaData; + private KeyStore metadata; + + public ComparableConceptImpl(IComparableConcept jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public int compareObjects(ModelObject contextObject, ModelObject otherObject) { + Pointer pContextObject = contextObject.getPointer(); + Pointer pOtherObject = otherObject.getPointer(); + ULONGByReference pulComparisonResult = new ULONGByReference(); + COMUtils.checkRC(jnaData.CompareObjects(pContextObject, pOtherObject, pulComparisonResult)); + return pulComparisonResult.getValue().intValue(); + } + + @Override + public KeyStore getMetadata() { + return metadata; + } + + @Override + public void setMetadata(KeyStore metdata) { + this.metadata = metdata; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/ComparableConceptInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/ComparableConceptInternal.java new file mode 100644 index 0000000000..6261c0dfa1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/ComparableConceptInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.concept.ComparableConcept; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.concept.IComparableConcept; +import agent.dbgmodel.jna.dbgmodel.concept.WrapIComparableConcept; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface ComparableConceptInternal extends ComparableConcept { + Map CACHE = new WeakValueHashMap<>(); + + static ComparableConceptInternal instanceFor(WrapIComparableConcept data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, ComparableConceptImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IComparableConcept.IID_ICOMPARABLE_CONCEPT), + WrapIComparableConcept.class) // + .build(); + + static ComparableConceptInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(ComparableConceptInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DataModelConceptImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DataModelConceptImpl.java new file mode 100644 index 0000000000..385dc2f539 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DataModelConceptImpl.java @@ -0,0 +1,79 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.debughost.DebugHostSymbolEnumerator; +import agent.dbgmodel.dbgmodel.debughost.DebugHostTypeSignature; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.concept.IDataModelConcept; + +public class DataModelConceptImpl implements DataModelConceptInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelConcept jnaData; + private KeyStore metadata; + + public DataModelConceptImpl(IDataModelConcept jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void initializeObject(ModelObject modelObject, + DebugHostTypeSignature matchingTypeSignature, + DebugHostSymbolEnumerator wildcardMatches) { + Pointer pModelObject = modelObject.getPointer(); + Pointer pMatchingTypeSignature = matchingTypeSignature.getPointer(); + Pointer pWildcardMatches = wildcardMatches.getPointer(); + COMUtils.checkRC( + jnaData.InitializeObject(pModelObject, pMatchingTypeSignature, pWildcardMatches)); + } + + @Override + public String getName() { + BSTRByReference bref = new BSTRByReference(); + COMUtils.checkRC(jnaData.GetName(bref)); + BSTR bstr = bref.getValue(); + String modelName = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return modelName; + } + + @Override + public KeyStore getMetadata() { + return metadata; + } + + @Override + public void setMetadata(KeyStore metdata) { + this.metadata = metdata; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DataModelConceptInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DataModelConceptInternal.java new file mode 100644 index 0000000000..f4efa1f390 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DataModelConceptInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.concept.DataModelConcept; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.concept.IDataModelConcept; +import agent.dbgmodel.jna.dbgmodel.concept.WrapIDataModelConcept; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelConceptInternal extends DataModelConcept { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelConceptInternal instanceFor(WrapIDataModelConcept data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelConceptImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDataModelConcept.IID_IDATA_MODEL_CONCEPT), + WrapIDataModelConcept.class) // + .build(); + + static DataModelConceptInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelConceptInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicConceptProviderConceptImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicConceptProviderConceptImpl.java new file mode 100644 index 0000000000..d8b1e3174b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicConceptProviderConceptImpl.java @@ -0,0 +1,128 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.UnknownExInternal; +import agent.dbgmodel.impl.dbgmodel.main.KeyStoreInternal; +import agent.dbgmodel.jna.dbgmodel.WrapIUnknownEx; +import agent.dbgmodel.jna.dbgmodel.concept.IDynamicConceptProviderConcept; +import agent.dbgmodel.jna.dbgmodel.main.WrapIKeyStore; + +public class DynamicConceptProviderConceptImpl implements DynamicConceptProviderConceptInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDynamicConceptProviderConcept jnaData; + + // TODO: creates IUnknown + //private UnknownInternal conceptInterface; + private UnknownEx conceptInterface; + private KeyStore conceptMetadata; + private KeyStore metadata; + + public DynamicConceptProviderConceptImpl(IDynamicConceptProviderConcept jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public boolean getConcept(ModelObject contextObject, REFIID conceptId) { + Pointer pContextObject = contextObject.getPointer(); + PointerByReference ppConceptInterface = new PointerByReference(); + PointerByReference ppConceptMetadata = new PointerByReference(); + BOOLByReference pHasConcept = new BOOLByReference(); + COMUtils.checkRC(jnaData.GetConcept(pContextObject, conceptId, ppConceptInterface, + ppConceptMetadata, pHasConcept)); + + WrapIUnknownEx wrap0 = new WrapIUnknownEx(ppConceptInterface.getValue()); + try { + conceptInterface = UnknownExInternal.tryPreferredInterfaces(wrap0::QueryInterface); + } + finally { + wrap0.Release(); + } + WrapIKeyStore wrap1 = new WrapIKeyStore(ppConceptInterface.getValue()); + try { + conceptMetadata = KeyStoreInternal.tryPreferredInterfaces(wrap1::QueryInterface); + } + finally { + wrap1.Release(); + } + + return pHasConcept.getValue().booleanValue(); + } + + @Override + public void setConcept(ModelObject contextObject, REFIID conceptId, UnknownEx conceptInterface, + KeyStore conceptMetadata) { + Pointer pContextObject = contextObject.getPointer(); + Pointer pConceptInterface = conceptInterface.getPointer(); + Pointer pConceptMetadata = conceptMetadata.getPointer(); + COMUtils.checkRC( + jnaData.SetConcept(pContextObject, conceptId, pConceptInterface, pConceptMetadata)); + } + + @Override + public void notifyParent(ModelObject parentModel) { + Pointer pParentModel = parentModel.getPointer(); + COMUtils.checkRC(jnaData.NotifyParent(pParentModel)); + } + + @Override + public void notifyParentChange(ModelObject parentModel) { + Pointer pParentModel = parentModel.getPointer(); + COMUtils.checkRC(jnaData.NotifyParent(pParentModel)); + } + + @Override + public void notifyDestruct() { + COMUtils.checkRC(jnaData.NotifyDestruct()); + } + + public UnknownEx getConceptInterface() { + return conceptInterface; + } + + public KeyStore getConceptMetadata() { + return conceptMetadata; + } + + @Override + public KeyStore getMetadata() { + return metadata; + } + + @Override + public void setMetadata(KeyStore metdata) { + this.metadata = metdata; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicConceptProviderConceptInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicConceptProviderConceptInternal.java new file mode 100644 index 0000000000..8bf277a335 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicConceptProviderConceptInternal.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.concept.DynamicConceptProviderConcept; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.concept.IDynamicConceptProviderConcept; +import agent.dbgmodel.jna.dbgmodel.concept.WrapIDynamicConceptProviderConcept; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DynamicConceptProviderConceptInternal extends DynamicConceptProviderConcept { + Map CACHE = new WeakValueHashMap<>(); + + static DynamicConceptProviderConceptInternal instanceFor( + WrapIDynamicConceptProviderConcept data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DynamicConceptProviderConceptImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put( + new REFIID( + IDynamicConceptProviderConcept.IID_IDYNAMIC_CONCEPT_PROVIDER_CONCEPT), + WrapIDynamicConceptProviderConcept.class) // + .build(); + + static DynamicConceptProviderConceptInternal tryPreferredInterfaces( + InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DynamicConceptProviderConceptInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicKeyProviderConceptImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicKeyProviderConceptImpl.java new file mode 100644 index 0000000000..bbda3c9dbd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicKeyProviderConceptImpl.java @@ -0,0 +1,101 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.*; +import agent.dbgmodel.impl.dbgmodel.main.KeyEnumeratorInternal; +import agent.dbgmodel.impl.dbgmodel.main.ModelObjectImpl; +import agent.dbgmodel.jna.dbgmodel.concept.IDynamicKeyProviderConcept; +import agent.dbgmodel.jna.dbgmodel.main.WrapIKeyEnumerator; + +public class DynamicKeyProviderConceptImpl implements DynamicKeyProviderConceptInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDynamicKeyProviderConcept jnaData; + private ModelObject keyValue; + private KeyStore metadata; + + public DynamicKeyProviderConceptImpl(IDynamicKeyProviderConcept jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public boolean getKey(ModelObject contextObject, WString key) { + Pointer pContextObject = contextObject.getPointer(); + PointerByReference ppKeyValue = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + BOOLByReference pHasKey = new BOOLByReference(); + COMUtils.checkRC(jnaData.GetKey(pContextObject, key, ppKeyValue, ppMetadata, pHasKey)); + + keyValue = ModelObjectImpl.getObjectWithMetadata(ppKeyValue, ppMetadata); + + return pHasKey.getValue().booleanValue(); + } + + @Override + public void setKey(ModelObject contextObject, WString key, ModelObject keyValue, + KeyStore conceptMetadata) { + Pointer pContextObject = contextObject.getPointer(); + Pointer pKeyValue = keyValue.getPointer(); + Pointer pMetadata = conceptMetadata.getPointer(); + COMUtils.checkRC( + jnaData.SetKey(pContextObject, key, pKeyValue, pMetadata)); + } + + @Override + public KeyEnumerator EnumerateKeys(ModelObject contextObject) { + Pointer pContextObject = contextObject.getPointer(); + PointerByReference ppEnumerator = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateKeys(pContextObject, ppEnumerator)); + + WrapIKeyEnumerator wrap = new WrapIKeyEnumerator(ppEnumerator.getValue()); + try { + return KeyEnumeratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + public ModelObject getKeyValue() { + return keyValue; + } + + @Override + public KeyStore getMetadata() { + return metadata; + } + + @Override + public void setMetadata(KeyStore metdata) { + this.metadata = metdata; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicKeyProviderConceptInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicKeyProviderConceptInternal.java new file mode 100644 index 0000000000..8b56ea5f08 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/DynamicKeyProviderConceptInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.concept.DynamicKeyProviderConcept; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.concept.IDynamicKeyProviderConcept; +import agent.dbgmodel.jna.dbgmodel.concept.WrapIDynamicKeyProviderConcept; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DynamicKeyProviderConceptInternal extends DynamicKeyProviderConcept { + Map CACHE = new WeakValueHashMap<>(); + + static DynamicKeyProviderConceptInternal instanceFor(WrapIDynamicKeyProviderConcept data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DynamicKeyProviderConceptImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDynamicKeyProviderConcept.IID_IDYNAMIC_KEY_PROVIDER_CONCEPT), + WrapIDynamicKeyProviderConcept.class) // + .build(); + + static DynamicKeyProviderConceptInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DynamicKeyProviderConceptInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/EquatableConceptImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/EquatableConceptImpl.java new file mode 100644 index 0000000000..4e4681b442 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/EquatableConceptImpl.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.concept.IEquatableConcept; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class EquatableConceptImpl implements EquatableConceptInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IEquatableConcept jnaData; + private KeyStore metadata; + + public EquatableConceptImpl(IEquatableConcept jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public boolean areObjectsEqual(ModelObject contextObject, ModelObject otherObject) { + Pointer pContextObject = contextObject.getPointer(); + Pointer pOtherObject = otherObject.getPointer(); + BOOLByReference pIsEqual = new BOOLByReference(); + COMUtils.checkRC(jnaData.AreObjectsEqual(pContextObject, pOtherObject, pIsEqual)); + return pIsEqual.getValue().booleanValue(); + } + + @Override + public KeyStore getMetadata() { + return metadata; + } + + @Override + public void setMetadata(KeyStore metdata) { + this.metadata = metdata; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/EquatableConceptInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/EquatableConceptInternal.java new file mode 100644 index 0000000000..1e06199cab --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/EquatableConceptInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.concept.EquatableConcept; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.concept.IEquatableConcept; +import agent.dbgmodel.jna.dbgmodel.concept.WrapIEquatableConcept; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface EquatableConceptInternal extends EquatableConcept { + Map CACHE = new WeakValueHashMap<>(); + + static EquatableConceptInternal instanceFor(WrapIEquatableConcept data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, EquatableConceptImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IEquatableConcept.IID_IEQUATABLE_CONCEPT), + WrapIEquatableConcept.class) // + .build(); + + static EquatableConceptInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(EquatableConceptInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IndexableConceptImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IndexableConceptImpl.java new file mode 100644 index 0000000000..0b281bf6d8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IndexableConceptImpl.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.main.ModelObjectImpl; +import agent.dbgmodel.jna.dbgmodel.concept.IIndexableConcept; + +public class IndexableConceptImpl implements IndexableConceptInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IIndexableConcept jnaData; + + private ModelObject indexers; + private KeyStore metadata; + + public IndexableConceptImpl(IIndexableConcept jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public long getDimensionality(ModelObject contextObject) { + Pointer pContextObject = contextObject.getPointer(); + ULONGLONGByReference pDimensionality = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.GetDimensionality(pContextObject, pDimensionality)); + return pDimensionality.getValue().longValue(); + } + + @Override + public ModelObject getAt(ModelObject contextObject, long indexerCount, + Pointer[] ppIndexers) { + Pointer pContextObject = contextObject.getPointer(); + ULONGLONG ulIndexerCount = new ULONGLONG(indexerCount); + PointerByReference ppObject = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + COMUtils.checkRC( + jnaData.GetAt(pContextObject, ulIndexerCount, ppIndexers, + ppObject, ppMetadata)); + + return ModelObjectImpl.getObjectWithMetadata(ppObject, ppMetadata); + } + + public ModelObject getIndexers() { + return indexers; + } + + @Override + public KeyStore getMetadata() { + return metadata; + } + + @Override + public void setMetadata(KeyStore metdata) { + this.metadata = metdata; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IndexableConceptInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IndexableConceptInternal.java new file mode 100644 index 0000000000..717fd31dff --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IndexableConceptInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.concept.IndexableConcept; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.concept.IIndexableConcept; +import agent.dbgmodel.jna.dbgmodel.concept.WrapIIndexableConcept; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface IndexableConceptInternal extends IndexableConcept { + Map CACHE = new WeakValueHashMap<>(); + + static IndexableConceptInternal instanceFor(WrapIIndexableConcept data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, IndexableConceptImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IIndexableConcept.IID_IINDEXABLE_CONCEPT), + WrapIIndexableConcept.class) // + .build(); + + static IndexableConceptInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(IndexableConceptInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IterableConceptImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IterableConceptImpl.java new file mode 100644 index 0000000000..25f84a6854 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IterableConceptImpl.java @@ -0,0 +1,94 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.COMUtilsExtra; +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.*; +import agent.dbgmodel.impl.dbgmodel.main.ModelIteratorInternal; +import agent.dbgmodel.jna.dbgmodel.concept.IIterableConcept; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelIterator; + +public class IterableConceptImpl implements IterableConceptInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IIterableConcept jnaData; + + private ModelIterator iterator; + private KeyStore metadata; + + public IterableConceptImpl(IIterableConcept jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public long getDefaultIndexDimensionality(ModelObject contextObject) { + Pointer pContextObject = contextObject.getPointer(); + ULONGLONGByReference pDimensionality = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.GetDefaultIndexDimensionality(pContextObject, pDimensionality)); + return pDimensionality.getValue().longValue(); + } + + @Override + public ModelIterator getIterator(ModelObject contextObject) { + Pointer pContextObject = contextObject.getPointer(); + PointerByReference ppIndexers = new PointerByReference(); + HRESULT hr = jnaData.GetIterator(pContextObject, ppIndexers); + if (hr.equals(COMUtilsExtra.E_FAIL)) { + return null; + } + if (hr.equals(COMUtilsExtra.E_COM_EXC)) { + return null; + } + COMUtils.checkRC(hr); + + WrapIModelIterator wrap = new WrapIModelIterator(ppIndexers.getValue()); + try { + return ModelIteratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + public ModelIterator getIterator() { + return iterator; + } + + @Override + public KeyStore getMetadata() { + return metadata; + } + + @Override + public void setMetadata(KeyStore metdata) { + this.metadata = metdata; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IterableConceptInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IterableConceptInternal.java new file mode 100644 index 0000000000..db98f44937 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/IterableConceptInternal.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.concept.IterableConcept; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.concept.IIterableConcept; +import agent.dbgmodel.jna.dbgmodel.concept.WrapIIterableConcept; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface IterableConceptInternal extends IterableConcept { + Map CACHE = new WeakValueHashMap<>(); + + static IterableConceptInternal instanceFor(WrapIIterableConcept data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, IterableConceptImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IIterableConcept.IID_IITERABLE_CONCEPT), WrapIIterableConcept.class) // + .build(); + + static IterableConceptInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(IterableConceptInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/PreferredRuntimeTypeConceptImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/PreferredRuntimeTypeConceptImpl.java new file mode 100644 index 0000000000..e1886257f0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/PreferredRuntimeTypeConceptImpl.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.main.ModelObjectInternal; +import agent.dbgmodel.jna.dbgmodel.concept.IPreferredRuntimeTypeConcept; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelObject; + +public class PreferredRuntimeTypeConceptImpl implements PreferredRuntimeTypeConceptInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IPreferredRuntimeTypeConcept jnaData; + private KeyStore metadata; + + public PreferredRuntimeTypeConceptImpl(IPreferredRuntimeTypeConcept jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public ModelObject CastToPreferredRuntimeType(ModelObject contextObject) { + Pointer pContextObject = contextObject.getPointer(); + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC(jnaData.CastToPreferredRuntimeType(pContextObject, ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public KeyStore getMetadata() { + return metadata; + } + + @Override + public void setMetadata(KeyStore metdata) { + this.metadata = metdata; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/PreferredRuntimeTypeConceptInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/PreferredRuntimeTypeConceptInternal.java new file mode 100644 index 0000000000..28a8c985d8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/PreferredRuntimeTypeConceptInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.concept.PreferredRuntimeTypeConcept; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.concept.IPreferredRuntimeTypeConcept; +import agent.dbgmodel.jna.dbgmodel.concept.WrapIPreferredRuntimeTypeConcept; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface PreferredRuntimeTypeConceptInternal extends PreferredRuntimeTypeConcept { + Map CACHE = new WeakValueHashMap<>(); + + static PreferredRuntimeTypeConceptInternal instanceFor(WrapIPreferredRuntimeTypeConcept data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, PreferredRuntimeTypeConceptImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IPreferredRuntimeTypeConcept.IID_IPREFERRED_RUNTIME_TYPE_CONCEPT), + WrapIPreferredRuntimeTypeConcept.class) // + .build(); + + static PreferredRuntimeTypeConceptInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(PreferredRuntimeTypeConceptInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/StringDisplayableConceptImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/StringDisplayableConceptImpl.java new file mode 100644 index 0000000000..411988674a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/StringDisplayableConceptImpl.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.concept.IStringDisplayableConcept; + +public class StringDisplayableConceptImpl implements StringDisplayableConceptInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IStringDisplayableConcept jnaData; + private KeyStore metadata; + + public StringDisplayableConceptImpl(IStringDisplayableConcept jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public String toDisplayString(ModelObject contextObject, KeyStore mdata) { + Pointer pContextObject = contextObject.getPointer(); + Pointer pMetadata = (mdata == null) ? null : mdata.getPointer(); + BSTRByReference bref = new BSTRByReference(); + COMUtils.checkRC(jnaData.ToDisplayString(pContextObject, pMetadata, bref)); + BSTR bstr = bref.getValue(); + String displayString = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return displayString; + } + + @Override + public KeyStore getMetadata() { + return metadata; + } + + @Override + public void setMetadata(KeyStore metdata) { + this.metadata = metdata; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/StringDisplayableConceptInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/StringDisplayableConceptInternal.java new file mode 100644 index 0000000000..96693b443d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/concept/StringDisplayableConceptInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.concept; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.concept.StringDisplayableConcept; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.concept.IStringDisplayableConcept; +import agent.dbgmodel.jna.dbgmodel.concept.WrapIStringDisplayableConcept; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface StringDisplayableConceptInternal extends StringDisplayableConcept { + Map CACHE = new WeakValueHashMap<>(); + + static StringDisplayableConceptInternal instanceFor(WrapIStringDisplayableConcept data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, StringDisplayableConceptImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IStringDisplayableConcept.IID_ISTRING_DISPLAYABLE_CONCEPT), + WrapIStringDisplayableConcept.class) // + .build(); + + static StringDisplayableConceptInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(StringDisplayableConceptInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerImpl1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerImpl1.java new file mode 100644 index 0000000000..b60b854bd5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerImpl1.java @@ -0,0 +1,338 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.concept.DataModelConcept; +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptManager; +import agent.dbgmodel.dbgmodel.debughost.*; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.datamodel.script.DataModelScriptManagerInternal; +import agent.dbgmodel.impl.dbgmodel.debughost.DebugHostSymbolEnumeratorInternal; +import agent.dbgmodel.impl.dbgmodel.debughost.DebugHostTypeSignatureInternal; +import agent.dbgmodel.impl.dbgmodel.main.KeyStoreInternal; +import agent.dbgmodel.impl.dbgmodel.main.ModelObjectInternal; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ModelObjectKind; +import agent.dbgmodel.jna.dbgmodel.datamodel.IDataModelManager1; +import agent.dbgmodel.jna.dbgmodel.main.WrapIKeyStore; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelObject; + +public class DataModelManagerImpl1 implements DataModelManagerInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelManager1 jnaData; + private DebugHostTypeSignatureInternal typeSignature; + private DebugHostSymbolEnumeratorInternal wildcardMatches; + + public DataModelManagerImpl1(IDataModelManager1 jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void close() { + COMUtils.checkRC(jnaData.Close()); + } + + @Override + public ModelObject createNoValue() { + PointerByReference ppContextObject = new PointerByReference(); + COMUtils.checkRC(jnaData.CreateNoValue(ppContextObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppContextObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject createErrorObject(HRESULT hrError, WString pwszMessage) { + PointerByReference ppContextObject = new PointerByReference(); + COMUtils.checkRC(jnaData.CreateErrorObject(hrError, pwszMessage, ppContextObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppContextObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject createTypedObject(DebugHostContext context, LOCATION objectLocation, + DebugHostType1 objectType) { + Pointer pContext = context.getPointer(); + Pointer pObjectType = objectType.getPointer(); + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC( + jnaData.CreateTypedObject(pContext, objectLocation, pObjectType, ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject createTypedObjectReference(DebugHostContext context, LOCATION objectLocation, + DebugHostType1 objectType) { + Pointer pContext = context.getPointer(); + Pointer pObjectType = context.getPointer(); + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC( + jnaData.CreateTypedObjectReference(pContext, objectLocation, pObjectType, ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject createSyntheticObject(DebugHostContext context) { + Pointer pContext = context.getPointer(); + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC( + jnaData.CreateSyntheticObject(pContext, ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject createDataModelObject(DataModelConcept dataModel) { + Pointer pDataModel = dataModel.getPointer(); + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC(jnaData.CreateDataModelObject(pDataModel, ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject createIntrinsicObject(ModelObjectKind objectKind, + VARIANT.ByReference intrinsicData) { + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC(jnaData.CreateIntrinsicObject(objectKind, intrinsicData, ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject createTypedIntrinsicObject(VARIANT.ByReference intrinsicData, + DebugHostType1 type) { + Pointer pType = type.getPointer(); + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC(jnaData.CreateTypedIntrinsicObject(intrinsicData, pType, ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject getModelForTypeSignature(DebugHostTypeSignature typeSignature) { + Pointer pTypeSignature = typeSignature.getPointer(); + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC(jnaData.GetModelForTypeSignature(pTypeSignature, ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject getModelForType(DebugHostType1 type) { + Pointer pType = type.getPointer(); + PointerByReference ppObject = new PointerByReference(); + PointerByReference ppTypeSignature = new PointerByReference(); + PointerByReference ppWildcardMatches = new PointerByReference(); + COMUtils.checkRC( + jnaData.GetModelForType(pType, ppObject, ppTypeSignature, ppWildcardMatches)); + + WrapIModelObject wrap0 = new WrapIModelObject(ppTypeSignature.getValue()); + try { + typeSignature = + DebugHostTypeSignatureInternal.tryPreferredInterfaces(wrap0::QueryInterface); + } + finally { + wrap0.Release(); + } + WrapIModelObject wrap1 = new WrapIModelObject(ppWildcardMatches.getValue()); + try { + wildcardMatches = + DebugHostSymbolEnumeratorInternal.tryPreferredInterfaces(wrap1::QueryInterface); + } + finally { + wrap1.Release(); + } + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public void registerModelForTypeSignature(DebugHostTypeSignature typeSignature, + ModelObject dataModel) { + Pointer pTypeSignature = typeSignature.getPointer(); + Pointer pDataModel = dataModel.getPointer(); + COMUtils.checkRC(jnaData.RegisterModelForTypeSignature(pTypeSignature, pDataModel)); + } + + @Override + public void unregisterModelForTypeSignature(ModelObject dataModel, + DebugHostTypeSignature typeSignature) { + Pointer pDataModel = dataModel.getPointer(); + Pointer pTypeSignature = typeSignature.getPointer(); + COMUtils.checkRC(jnaData.UnregisterModelForTypeSignature(pDataModel, pTypeSignature)); + } + + @Override + public void registerExtensionForTypeSignature(DebugHostTypeSignature typeSignature, + ModelObject dataModel) { + Pointer pTypeSignature = typeSignature.getPointer(); + Pointer pDataModel = dataModel.getPointer(); + COMUtils.checkRC(jnaData.RegisterExtensionForTypeSignature(pTypeSignature, pDataModel)); + } + + @Override + public void unregisterExtensionForTypeSignature(ModelObject dataModel, + DebugHostTypeSignature typeSignature) { + Pointer pDataModel = dataModel.getPointer(); + Pointer pTypeSignature = typeSignature.getPointer(); + COMUtils.checkRC(jnaData.UnregisterExtensionForTypeSignature(pDataModel, pTypeSignature)); + } + + @Override + public KeyStore createMetadataStore(KeyStore parentStore) { + Pointer pParentStore = parentStore.getPointer(); + PointerByReference ppMetadataStore = new PointerByReference(); + COMUtils.checkRC(jnaData.CreateMetadataStore(pParentStore, ppMetadataStore)); + + WrapIKeyStore wrap = new WrapIKeyStore(ppMetadataStore.getValue()); + try { + return KeyStoreInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject getRootNamespace() { + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC(jnaData.GetRootNamespace(ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public void registerNamedModel(WString modelName, ModelObject modelObject) { + Pointer pModelObject = modelObject.getPointer(); + COMUtils.checkRC(jnaData.RegisterNamedModel(modelName, pModelObject)); + } + + @Override + public void unregisterNamedModel(WString modelName) { + COMUtils.checkRC(jnaData.UnregisterNamedModel(modelName)); + } + + @Override + public ModelObject acquireNamedModel(WString modelName) { + PointerByReference ppModelObject = new PointerByReference(); + COMUtils.checkRC(jnaData.AcquireNamedModel(modelName, ppModelObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppModelObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + public DebugHostTypeSignatureInternal getTypeSignature() { + return typeSignature; + } + + public DebugHostSymbolEnumeratorInternal getWildcardMatches() { + return wildcardMatches; + } + + @Override + public DataModelScriptManager asScriptManager() { + return DataModelScriptManagerInternal.tryPreferredInterfaces(jnaData::QueryInterface); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerImpl2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerImpl2.java new file mode 100644 index 0000000000..cdf5e044cc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerImpl2.java @@ -0,0 +1,82 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.datamodel.DataModelManager2; +import agent.dbgmodel.dbgmodel.debughost.DebugHostContext; +import agent.dbgmodel.dbgmodel.debughost.DebugHostType1; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.main.ModelObjectInternal; +import agent.dbgmodel.jna.dbgmodel.datamodel.IDataModelManager2; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelObject; + +public class DataModelManagerImpl2 extends DataModelManagerImpl1 implements DataModelManager2 { + @SuppressWarnings("unused") + private final IDataModelManager2 jnaData; + + public DataModelManagerImpl2(IDataModelManager2 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public ModelObject acquireSubNamespace(WString modelName, WString subNamespaceModelName, + WString accessName, KeyStore metadata) { + Pointer pMetadata = metadata.getPointer(); + PointerByReference ppNamespaceModelObject = new PointerByReference(); + COMUtils.checkRC(jnaData.AcquireSubNamespace(modelName, subNamespaceModelName, accessName, + pMetadata, ppNamespaceModelObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppNamespaceModelObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject createTypedIntrinsicObjectEx(DebugHostContext context, + VARIANT.ByReference intrinsicData, DebugHostType1 type) { + Pointer pContext = context.getPointer(); + Pointer pType = type.getPointer(); + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC( + jnaData.CreateTypedIntrinsicObjectEx(pContext, intrinsicData, pType, ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerInternal.java new file mode 100644 index 0000000000..936363774c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/DataModelManagerInternal.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.DataModelManager1; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelManagerInternal extends DataModelManager1 { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelManagerInternal instanceFor(WrapIDataModelManager1 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelManagerImpl1::new); + } + + static DataModelManagerInternal instanceFor(WrapIDataModelManager2 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelManagerImpl2::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDataModelManager2.IID_IDATA_MODEL_MANAGER2), + WrapIDataModelManager2.class) // + .put(new REFIID(IDataModelManager1.IID_IDATA_MODEL_MANAGER), + WrapIDataModelManager1.class) // + .build(); + + static DataModelManagerInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelManagerInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelNameBinderImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelNameBinderImpl.java new file mode 100644 index 0000000000..74596cfe64 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelNameBinderImpl.java @@ -0,0 +1,136 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.*; +import agent.dbgmodel.impl.dbgmodel.main.*; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelNameBinder; +import agent.dbgmodel.jna.dbgmodel.main.*; + +public class DataModelNameBinderImpl implements DataModelNameBinderInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelNameBinder jnaData; + + private ModelObject value; + private KeyStore metadata; + private ModelObject reference; + + public DataModelNameBinderImpl(IDataModelNameBinder jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void bindValue(ModelObject contextObject, WString name) { + Pointer pContextObject = contextObject.getPointer(); + PointerByReference ppValue = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + COMUtils.checkRC(jnaData.BindValue(pContextObject, name, ppValue, ppMetadata)); + + WrapIModelObject wrap0 = new WrapIModelObject(ppMetadata.getValue()); + try { + value = ModelObjectInternal.tryPreferredInterfaces(wrap0::QueryInterface); + } + finally { + wrap0.Release(); + } + WrapIKeyStore wrap1 = new WrapIKeyStore(ppMetadata.getValue()); + try { + metadata = KeyStoreInternal.tryPreferredInterfaces(wrap1::QueryInterface); + } + finally { + wrap1.Release(); + } + } + + @Override + public void bindReference(ModelObject contextObject, WString name) { + Pointer pContextObject = contextObject.getPointer(); + PointerByReference ppReference = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + COMUtils.checkRC(jnaData.BindReference(pContextObject, name, ppReference, ppMetadata)); + + WrapIModelObject wrap0 = new WrapIModelObject(ppMetadata.getValue()); + try { + reference = ModelObjectInternal.tryPreferredInterfaces(wrap0::QueryInterface); + } + finally { + wrap0.Release(); + } + WrapIKeyStore wrap1 = new WrapIKeyStore(ppMetadata.getValue()); + try { + metadata = KeyStoreInternal.tryPreferredInterfaces(wrap1::QueryInterface); + } + finally { + wrap1.Release(); + } + } + + @Override + public KeyEnumerator enumerateValues(ModelObject contextObject) { + Pointer pContextObject = contextObject.getPointer(); + PointerByReference ppEnumerator = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateValues(pContextObject, ppEnumerator)); + + WrapIKeyEnumerator wrap = new WrapIKeyEnumerator(ppEnumerator.getValue()); + try { + return KeyEnumeratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public KeyEnumerator enumerateReferences(ModelObject contextObject) { + Pointer pContextObject = contextObject.getPointer(); + PointerByReference ppEnumerator = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateReferences(pContextObject, ppEnumerator)); + + WrapIKeyEnumerator wrap = new WrapIKeyEnumerator(ppEnumerator.getValue()); + try { + return KeyEnumeratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + public ModelObject getValue() { + return value; + } + + public KeyStore getMetadata() { + return metadata; + } + + public ModelObject getReference() { + return reference; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelNameBinderInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelNameBinderInternal.java new file mode 100644 index 0000000000..be65feeff6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelNameBinderInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelNameBinder; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelNameBinder; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.WrapIDataModelNameBinder; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelNameBinderInternal extends DataModelNameBinder { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelNameBinderInternal instanceFor(WrapIDataModelNameBinder data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelNameBinderImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDataModelNameBinder.IID_IDATA_MODEL_NAME_BINDER), + WrapIDataModelNameBinder.class) // + .build(); + + static DataModelNameBinderInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelNameBinderInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptClientImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptClientImpl.java new file mode 100644 index 0000000000..afd21d2f22 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptClientImpl.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptClient; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DataModelScriptClientImpl implements DataModelScriptClientInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptClient jnaData; + + public DataModelScriptClientImpl(IDataModelScriptClient jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void reportError(int errorClass, HRESULT hrFail, WString message, + int line, int position) { + ULONG ulErrorClass = new ULONG(errorClass); + ULONG ulLine = new ULONG(line); + ULONG ulPosition = new ULONG(position); + COMUtils.checkRC( + jnaData.ReportError(ulErrorClass, hrFail, message, ulLine, ulPosition)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptClientInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptClientInternal.java new file mode 100644 index 0000000000..eddef9d7d0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptClientInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptClient; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptClient; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.WrapIDataModelScriptClient; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptClientInternal extends DataModelScriptClient { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptClientInternal instanceFor(WrapIDataModelScriptClient data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptClientImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDataModelScriptClient.IID_IDATA_MODEL_SCRIPT_CLIENT), + WrapIDataModelScriptClient.class) // + .build(); + + static DataModelScriptClientInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptClientInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptHostContextImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptHostContextImpl.java new file mode 100644 index 0000000000..fa01622053 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptHostContextImpl.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScript; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.main.ModelObjectInternal; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptHostContext; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelObject; + +public class DataModelScriptHostContextImpl implements DataModelScriptHostContextInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptHostContext jnaData; + + public DataModelScriptHostContextImpl(IDataModelScriptHostContext jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void notifyScriptChange(DataModelScript script, int changeKind) { + Pointer pScript = script.getPointer(); + ULONG ulChangeKind = new ULONG(changeKind); + COMUtils.checkRC(jnaData.NotifyScriptChange(pScript, ulChangeKind)); + } + + @Override + public ModelObject getNamespaceObject() { + PointerByReference ppNamespaceObject = new PointerByReference(); + COMUtils.checkRC(jnaData.GetNamespaceObject(ppNamespaceObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppNamespaceObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptHostContextInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptHostContextInternal.java new file mode 100644 index 0000000000..36452168ab --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptHostContextInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptHostContext; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptHostContext; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.WrapIDataModelScriptHostContext; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptHostContextInternal extends DataModelScriptHostContext { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptHostContextInternal instanceFor(WrapIDataModelScriptHostContext data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptHostContextImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDataModelScriptHostContext.IID_IDATA_MODEL_SCRIPT_HOST_CONTEXT), + WrapIDataModelScriptHostContext.class) // + .build(); + + static DataModelScriptHostContextInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptHostContextInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptImpl.java new file mode 100644 index 0000000000..c3b5b2e7e0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptImpl.java @@ -0,0 +1,90 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptClient; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScript; + +public class DataModelScriptImpl implements DataModelScriptInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScript jnaData; + + public DataModelScriptImpl(IDataModelScript jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public String getName() { + BSTRByReference bref = new BSTRByReference(); + COMUtils.checkRC(jnaData.GetName(bref)); + BSTR bstr = bref.getValue(); + String scriptName = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return scriptName; + } + + @Override + public void rename(WString scriptName) { + COMUtils.checkRC(jnaData.Rename(scriptName)); + } + + @Override + public void populate(Pointer contentStream) { + COMUtils.checkRC(jnaData.Populate(contentStream)); + } + + @Override + public void execute(DataModelScriptClient client) { + Pointer pClient = client.getPointer(); + COMUtils.checkRC(jnaData.Execute(pClient)); + } + + @Override + public void unlink() { + COMUtils.checkRC(jnaData.Unlink()); + } + + @Override + public boolean isInvocable() { + BOOLByReference bIsInvocable = new BOOLByReference(); + COMUtils.checkRC(jnaData.IsInvocable(bIsInvocable)); + return bIsInvocable.getValue().booleanValue(); + } + + @Override + public void invokeMain(DataModelScriptClient client) { + Pointer pClient = client.getPointer(); + COMUtils.checkRC(jnaData.InvokeMain(pClient)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptInternal.java new file mode 100644 index 0000000000..9251ccb54a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScript; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScript; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.WrapIDataModelScript; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptInternal extends DataModelScript { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptInternal instanceFor(WrapIDataModelScript data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDataModelScript.IID_IDATA_MODEL_SCRIPT), + WrapIDataModelScript.class) // + .build(); + + static DataModelScriptInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptManagerImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptManagerImpl.java new file mode 100644 index 0000000000..f9ba112bb9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptManagerImpl.java @@ -0,0 +1,114 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.datamodel.script.*; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.*; + +public class DataModelScriptManagerImpl implements DataModelScriptManagerInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptManager jnaData; + + public DataModelScriptManagerImpl(IDataModelScriptManager jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public DataModelNameBinder getDefaultNameBinder() { + PointerByReference ppNameBinder = new PointerByReference(); + COMUtils.checkRC(jnaData.GetDefaultNameBinder(ppNameBinder)); + + WrapIDataModelNameBinder wrap = new WrapIDataModelNameBinder(ppNameBinder.getValue()); + try { + return DataModelNameBinderInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public void registerScriptProvider(DataModelScriptProvider provider) { + Pointer pProvider = provider.getPointer(); + COMUtils.checkRC(jnaData.RegisterScriptProvider(pProvider)); + } + + @Override + public void unregisterScriptProvider(DataModelScriptProvider provider) { + Pointer pProvider = provider.getPointer(); + COMUtils.checkRC(jnaData.UnregisterScriptProvider(pProvider)); + } + + @Override + public DataModelScriptProvider findProviderForScriptType(String scriptType) { + WString wScriptType = new WString(scriptType); + PointerByReference ppProvider = new PointerByReference(); + COMUtils.checkRC(jnaData.FindProviderForScriptType(wScriptType, ppProvider)); + + WrapIDataModelScriptProvider wrap = new WrapIDataModelScriptProvider(ppProvider.getValue()); + try { + return DataModelScriptProviderInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DataModelScriptProvider findProviderForScriptExtension(String scriptExtension) { + WString wScriptExtension = new WString(scriptExtension); + PointerByReference ppProvider = new PointerByReference(); + COMUtils.checkRC(jnaData.FindProviderForScriptType(wScriptExtension, ppProvider)); + + WrapIDataModelScriptProvider wrap = new WrapIDataModelScriptProvider(ppProvider.getValue()); + try { + return DataModelScriptProviderInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DataModelScriptProviderEnumerator enumeratorScriptProviders() { + PointerByReference ppEnumerator = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateScriptProviders(ppEnumerator)); + + WrapIDataModelScriptProviderEnumerator wrap = + new WrapIDataModelScriptProviderEnumerator(ppEnumerator.getValue()); + try { + return DataModelScriptProviderEnumeratorInternal.tryPreferredInterfaces( + wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptManagerInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptManagerInternal.java new file mode 100644 index 0000000000..b662a32586 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptManagerInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptManager; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptManager; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.WrapIDataModelScriptManager; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptManagerInternal extends DataModelScriptManager { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptManagerInternal instanceFor(WrapIDataModelScriptManager data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptManagerImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDataModelScriptManager.IID_IDATA_MODEL_SCRIPT_MANAGER), + WrapIDataModelScriptManager.class) // + .build(); + + static DataModelScriptManagerInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptManagerInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderEnumeratorImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderEnumeratorImpl.java new file mode 100644 index 0000000000..2c8e1b2563 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderEnumeratorImpl.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.COMUtilsExtra; +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptProvider; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptProviderEnumerator; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.WrapIDataModelScriptProvider; + +public class DataModelScriptProviderEnumeratorImpl + implements DataModelScriptProviderEnumeratorInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptProviderEnumerator jnaData; + + public DataModelScriptProviderEnumeratorImpl(IDataModelScriptProviderEnumerator jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void reset() { + COMUtils.checkRC(jnaData.Reset()); + } + + @Override + public DataModelScriptProvider getNext() { + PointerByReference ppProvider = new PointerByReference(); + HRESULT hr = jnaData.GetNext(ppProvider); + if (hr.equals(COMUtilsExtra.E_BOUNDS)) { + return null; + } + COMUtils.checkRC(hr); + + WrapIDataModelScriptProvider wrap = new WrapIDataModelScriptProvider(ppProvider.getValue()); + try { + return DataModelScriptProviderInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderEnumeratorInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderEnumeratorInternal.java new file mode 100644 index 0000000000..e2fd91fcd0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderEnumeratorInternal.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptProviderEnumerator; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptProviderEnumerator; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.WrapIDataModelScriptProviderEnumerator; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptProviderEnumeratorInternal + extends DataModelScriptProviderEnumerator { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptProviderEnumeratorInternal instanceFor( + WrapIDataModelScriptProviderEnumerator data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptProviderEnumeratorImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put( + new REFIID( + IDataModelScriptProviderEnumerator.IID_IDATA_MODEL_SCRIPT_PROVIDER_ENUMERATOR), + WrapIDataModelScriptProviderEnumerator.class) // + .build(); + + static DataModelScriptProviderEnumeratorInternal tryPreferredInterfaces( + InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptProviderEnumeratorInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderImpl.java new file mode 100644 index 0000000000..1aa063116e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderImpl.java @@ -0,0 +1,111 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptTemplate; +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptTemplateEnumerator; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.*; + +public class DataModelScriptProviderImpl implements DataModelScriptProviderInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptProvider jnaData; + + public DataModelScriptProviderImpl(IDataModelScriptProvider jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public String getName() { + BSTRByReference bref = new BSTRByReference(); + COMUtils.checkRC(jnaData.GetName(bref)); + BSTR bstr = bref.getValue(); + String name = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return name; + } + + @Override + public String getExtension() { + BSTRByReference bref = new BSTRByReference(); + COMUtils.checkRC(jnaData.GetExtension(bref)); + BSTR bstr = bref.getValue(); + String extension = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return extension; + } + + @Override + public DataModelScriptTemplate createScript() { + PointerByReference ppScript = new PointerByReference(); + COMUtils.checkRC(jnaData.CreateScript(ppScript)); + + WrapIDataModelScriptTemplate wrap = + new WrapIDataModelScriptTemplate(ppScript.getValue()); + try { + return DataModelScriptTemplateInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DataModelScriptTemplate getDefaultTemplateContent() { + PointerByReference ppTemplateContent = new PointerByReference(); + COMUtils.checkRC(jnaData.GetDefaultTemplateContent(ppTemplateContent)); + + WrapIDataModelScriptTemplate wrap = + new WrapIDataModelScriptTemplate(ppTemplateContent.getValue()); + try { + return DataModelScriptTemplateInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DataModelScriptTemplateEnumerator enumerateTemplates() { + PointerByReference ppTemplateContent = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateTemplates(ppTemplateContent)); + + WrapIDataModelScriptTemplateEnumerator wrap = + new WrapIDataModelScriptTemplateEnumerator(ppTemplateContent.getValue()); + try { + return DataModelScriptTemplateEnumeratorInternal.tryPreferredInterfaces( + wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderInternal.java new file mode 100644 index 0000000000..638d387696 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptProviderInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptProvider; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptProvider; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.WrapIDataModelScriptProvider; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptProviderInternal extends DataModelScriptProvider { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptProviderInternal instanceFor(WrapIDataModelScriptProvider data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptProviderImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDataModelScriptProvider.IID_IDATA_MODEL_SCRIPT_PROVIDER), + WrapIDataModelScriptProvider.class) // + .build(); + + static DataModelScriptProviderInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptProviderInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateEnumeratorImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateEnumeratorImpl.java new file mode 100644 index 0000000000..4930c86a14 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateEnumeratorImpl.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.COMUtilsExtra; +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptTemplate; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptTemplateEnumerator; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.WrapIDataModelScriptTemplate; + +public class DataModelScriptTemplateEnumeratorImpl + implements DataModelScriptTemplateEnumeratorInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptTemplateEnumerator jnaData; + + public DataModelScriptTemplateEnumeratorImpl(IDataModelScriptTemplateEnumerator jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void reset() { + COMUtils.checkRC(jnaData.Reset()); + } + + @Override + public DataModelScriptTemplate getNext() { + PointerByReference ppTemplateContent = new PointerByReference(); + HRESULT hr = jnaData.GetNext(ppTemplateContent); + if (hr.equals(COMUtilsExtra.E_BOUNDS)) { + return null; + } + COMUtils.checkRC(hr); + + WrapIDataModelScriptTemplate wrap = + new WrapIDataModelScriptTemplate(ppTemplateContent.getValue()); + try { + return DataModelScriptTemplateInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateEnumeratorInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateEnumeratorInternal.java new file mode 100644 index 0000000000..594abc9f5e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateEnumeratorInternal.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptTemplateEnumerator; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptTemplateEnumerator; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.WrapIDataModelScriptTemplateEnumerator; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptTemplateEnumeratorInternal + extends DataModelScriptTemplateEnumerator { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptTemplateEnumeratorInternal instanceFor( + WrapIDataModelScriptTemplateEnumerator data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptTemplateEnumeratorImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put( + new REFIID( + IDataModelScriptTemplateEnumerator.IID_IDATA_MODEL_SCRIPT_TEMPLATE_ENUMERATOR), + WrapIDataModelScriptTemplateEnumerator.class) // + .build(); + + static DataModelScriptTemplateEnumeratorInternal tryPreferredInterfaces( + InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptTemplateEnumeratorInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateImpl.java new file mode 100644 index 0000000000..9179c023e3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateImpl.java @@ -0,0 +1,80 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.impl.dbgmodel.UnknownExInternal; +import agent.dbgmodel.jna.dbgmodel.WrapIUnknownEx; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptTemplate; + +public class DataModelScriptTemplateImpl implements DataModelScriptTemplateInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptTemplate jnaData; + + public DataModelScriptTemplateImpl(IDataModelScriptTemplate jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public String getName() { + BSTRByReference bref = new BSTRByReference(); + COMUtils.checkRC(jnaData.GetName(bref)); + BSTR bstr = bref.getValue(); + String templateName = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return templateName; + } + + @Override + public String getDescription() { + BSTRByReference bref = new BSTRByReference(); + COMUtils.checkRC(jnaData.GetName(bref)); + BSTR bstr = bref.getValue(); + String templateDescription = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return templateDescription; + } + + @Override + public UnknownEx getContent() { + PointerByReference ppContentStream = new PointerByReference(); + COMUtils.checkRC(jnaData.GetContent(ppContentStream)); + + WrapIUnknownEx wrap = new WrapIUnknownEx(ppContentStream.getValue()); + try { + return UnknownExInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateInternal.java new file mode 100644 index 0000000000..438fea7466 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/DataModelScriptTemplateInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScriptTemplate; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.IDataModelScriptTemplate; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.WrapIDataModelScriptTemplate; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptTemplateInternal extends DataModelScriptTemplate { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptTemplateInternal instanceFor(WrapIDataModelScriptTemplate data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptTemplateImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDataModelScriptTemplate.IID_IDATA_MODEL_SCRIPT_TEMPLATE), + WrapIDataModelScriptTemplate.class) // + .build(); + + static DataModelScriptTemplateInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptTemplateInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumeratorImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumeratorImpl.java new file mode 100644 index 0000000000..9d41f4c46d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumeratorImpl.java @@ -0,0 +1,103 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugBreakpointEnumerator; + +public class DataModelScriptDebugBreakpointEnumeratorImpl + implements DataModelScriptDebugBreakpointEnumeratorInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptDebugBreakpointEnumerator jnaData; + + public DataModelScriptDebugBreakpointEnumeratorImpl( + IDataModelScriptDebugBreakpointEnumerator jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + /* + @Override + public DebugMemoryBasicInformation queryVirtual(long offset) { + throw new UnsupportedOperationException("Not implemented in this interface"); + } + + @Override + public int readVirtual(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtual(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int readVirtualUncached(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtualUncached(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeVirtual(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtual(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int writeVirtualUncached(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtualUncached(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + */ +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumeratorInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumeratorInternal.java new file mode 100644 index 0000000000..55ffbf4e9b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointEnumeratorInternal.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.debug.DataModelScriptDebugBreakpointEnumerator; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugBreakpointEnumerator; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.WrapIDataModelScriptDebugBreakpointEnumerator; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptDebugBreakpointEnumeratorInternal + extends DataModelScriptDebugBreakpointEnumerator { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptDebugBreakpointEnumeratorInternal instanceFor( + WrapIDataModelScriptDebugBreakpointEnumerator data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, + DataModelScriptDebugBreakpointEnumeratorImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put( + new REFIID( + IDataModelScriptDebugBreakpointEnumerator.IID_IDATA_MODEL_SCRIPT_DEBUG_BREAKPOINT_ENUMERATOR), + WrapIDataModelScriptDebugBreakpointEnumerator.class) // + .build(); + + static DataModelScriptDebugBreakpointEnumeratorInternal tryPreferredInterfaces( + InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces( + DataModelScriptDebugBreakpointEnumeratorInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointImpl.java new file mode 100644 index 0000000000..caf1710e02 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointImpl.java @@ -0,0 +1,101 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugBreakpoint; + +public class DataModelScriptDebugBreakpointImpl implements DataModelScriptDebugBreakpointInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptDebugBreakpoint jnaData; + + public DataModelScriptDebugBreakpointImpl(IDataModelScriptDebugBreakpoint jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + /* + @Override + public DebugMemoryBasicInformation queryVirtual(long offset) { + throw new UnsupportedOperationException("Not implemented in this interface"); + } + + @Override + public int readVirtual(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtual(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int readVirtualUncached(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtualUncached(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeVirtual(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtual(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int writeVirtualUncached(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtualUncached(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + */ +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointInternal.java new file mode 100644 index 0000000000..d48adc2ff7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugBreakpointInternal.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.debug.DataModelScriptDebugBreakpoint; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugBreakpoint; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.WrapIDataModelScriptDebugBreakpoint; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptDebugBreakpointInternal extends DataModelScriptDebugBreakpoint { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptDebugBreakpointInternal instanceFor( + WrapIDataModelScriptDebugBreakpoint data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptDebugBreakpointImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put( + new REFIID( + IDataModelScriptDebugBreakpoint.IID_IDATA_MODEL_SCRIPT_DEBUG_BREAKPOINT), + WrapIDataModelScriptDebugBreakpoint.class) // + .build(); + + static DataModelScriptDebugBreakpointInternal tryPreferredInterfaces( + InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptDebugBreakpointInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugClientImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugClientImpl.java new file mode 100644 index 0000000000..cd5c467166 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugClientImpl.java @@ -0,0 +1,101 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugClient; + +public class DataModelScriptDebugClientImpl implements DataModelScriptDebugClientInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptDebugClient jnaData; + + public DataModelScriptDebugClientImpl(IDataModelScriptDebugClient jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + /* + @Override + public DebugMemoryBasicInformation queryVirtual(long offset) { + throw new UnsupportedOperationException("Not implemented in this interface"); + } + + @Override + public int readVirtual(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtual(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int readVirtualUncached(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtualUncached(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeVirtual(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtual(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int writeVirtualUncached(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtualUncached(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + */ +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugClientInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugClientInternal.java new file mode 100644 index 0000000000..a1073c7d57 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugClientInternal.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.debug.DataModelScriptDebugClient; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugClient; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.WrapIDataModelScriptDebugClient; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptDebugClientInternal extends DataModelScriptDebugClient { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptDebugClientInternal instanceFor( + WrapIDataModelScriptDebugClient data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptDebugClientImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put( + new REFIID(IDataModelScriptDebugClient.IID_IDATA_MODEL_SCRIPT_DEBUG_CLIENT), + WrapIDataModelScriptDebugClient.class) // + .build(); + + static DataModelScriptDebugClientInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptDebugClientInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugImpl.java new file mode 100644 index 0000000000..c022a70e88 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugImpl.java @@ -0,0 +1,101 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebug; + +public class DataModelScriptDebugImpl implements DataModelScriptDebugInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptDebug jnaData; + + public DataModelScriptDebugImpl(IDataModelScriptDebug jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + /* + @Override + public DebugMemoryBasicInformation queryVirtual(long offset) { + throw new UnsupportedOperationException("Not implemented in this interface"); + } + + @Override + public int readVirtual(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtual(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int readVirtualUncached(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtualUncached(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeVirtual(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtual(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int writeVirtualUncached(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtualUncached(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + */ +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugImpl2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugImpl2.java new file mode 100644 index 0000000000..0ce3956ea3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugImpl2.java @@ -0,0 +1,98 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; + +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebug2; + +public class DataModelScriptDebugImpl2 extends DataModelScriptDebugImpl { + @SuppressWarnings("unused") + private final IDataModelScriptDebug2 jnaData; + + public DataModelScriptDebugImpl2(IDataModelScriptDebug2 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + /* + @Override + public DebugMemoryBasicInformation queryVirtual(long offset) { + throw new UnsupportedOperationException("Not implemented in this interface"); + } + + @Override + public int readVirtual(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtual(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int readVirtualUncached(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtualUncached(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeVirtual(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtual(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int writeVirtualUncached(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtualUncached(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + */ +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugInternal.java new file mode 100644 index 0000000000..8a048a9bff --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugInternal.java @@ -0,0 +1,51 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.debug.DataModelScriptDebug1; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptDebugInternal extends DataModelScriptDebug1 { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptDebugInternal instanceFor(WrapIDataModelScriptDebug data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptDebugImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDataModelScriptDebug2.IID_IDATA_MODEL_SCRIPT_DEBUG2), + WrapIDataModelScriptDebug.class) // + .put(new REFIID(IDataModelScriptDebug.IID_IDATA_MODEL_SCRIPT_DEBUG), + WrapIDataModelScriptDebug.class) // + .build(); + + static DataModelScriptDebugInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptDebugInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrameImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrameImpl.java new file mode 100644 index 0000000000..4a6a0a9631 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrameImpl.java @@ -0,0 +1,101 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugStackFrame; + +public class DataModelScriptDebugStackFrameImpl implements DataModelScriptDebugStackFrameInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptDebugStackFrame jnaData; + + public DataModelScriptDebugStackFrameImpl(IDataModelScriptDebugStackFrame jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + /* + @Override + public DebugMemoryBasicInformation queryVirtual(long offset) { + throw new UnsupportedOperationException("Not implemented in this interface"); + } + + @Override + public int readVirtual(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtual(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int readVirtualUncached(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtualUncached(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeVirtual(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtual(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int writeVirtualUncached(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtualUncached(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + */ +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrameInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrameInternal.java new file mode 100644 index 0000000000..2fb72d2cab --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackFrameInternal.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.debug.DataModelScriptDebugStackFrame; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugStackFrame; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.WrapIDataModelScriptDebugStackFrame; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptDebugStackFrameInternal extends DataModelScriptDebugStackFrame { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptDebugStackFrameInternal instanceFor( + WrapIDataModelScriptDebugStackFrame data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptDebugStackFrameImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put( + new REFIID( + IDataModelScriptDebugStackFrame.IID_IDATA_MODEL_SCRIPT_DEBUG_STACK_FRAME), + WrapIDataModelScriptDebugStackFrame.class) // + .build(); + + static DataModelScriptDebugStackFrameInternal tryPreferredInterfaces( + InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptDebugStackFrameInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackImpl.java new file mode 100644 index 0000000000..40406d44b3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackImpl.java @@ -0,0 +1,101 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugStack; + +public class DataModelScriptDebugStackImpl implements DataModelScriptDebugStackInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptDebugStack jnaData; + + public DataModelScriptDebugStackImpl(IDataModelScriptDebugStack jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + /* + @Override + public DebugMemoryBasicInformation queryVirtual(long offset) { + throw new UnsupportedOperationException("Not implemented in this interface"); + } + + @Override + public int readVirtual(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtual(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int readVirtualUncached(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtualUncached(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeVirtual(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtual(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int writeVirtualUncached(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtualUncached(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + */ +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackInternal.java new file mode 100644 index 0000000000..79ee30c230 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugStackInternal.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.debug.DataModelScriptDebugStack; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugStack; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.WrapIDataModelScriptDebugStack; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptDebugStackInternal extends DataModelScriptDebugStack { + Map CACHE = new WeakValueHashMap<>(); + + static DataModelScriptDebugStackInternal instanceFor( + WrapIDataModelScriptDebugStack data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DataModelScriptDebugStackImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put( + new REFIID(IDataModelScriptDebugStack.IID_IDATA_MODEL_SCRIPT_DEBUG_STACK), + WrapIDataModelScriptDebugStack.class) // + .build(); + + static DataModelScriptDebugStackInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DataModelScriptDebugStackInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumeratorImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumeratorImpl.java new file mode 100644 index 0000000000..fce2454a8d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumeratorImpl.java @@ -0,0 +1,103 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugVariableSetEnumerator; + +public class DataModelScriptDebugVariableSetEnumeratorImpl + implements DataModelScriptDebugVariableSetEnumeratorInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDataModelScriptDebugVariableSetEnumerator jnaData; + + public DataModelScriptDebugVariableSetEnumeratorImpl( + IDataModelScriptDebugVariableSetEnumerator jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + /* + @Override + public DebugMemoryBasicInformation queryVirtual(long offset) { + throw new UnsupportedOperationException("Not implemented in this interface"); + } + + @Override + public int readVirtual(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtual(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int readVirtualUncached(long offset, ByteBuffer into, int len) { + if (len > into.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesRead = new ULONGByReference(); + COMUtils.checkRC(jnaData.ReadVirtualUncached(ullOffset, into, ulLen, pulBytesRead)); + int read = pulBytesRead.getValue().intValue(); + into.position(read + into.position()); + return read; + } + + @Override + public int writeVirtual(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtual(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + + @Override + public int writeVirtualUncached(long offset, ByteBuffer from, int len) { + if (len > from.remaining()) { + throw new BufferOverflowException(); + } + ULONGLONG ullOffset = new ULONGLONG(offset); + ULONG ulLen = new ULONG(len); + ULONGByReference pulBytesWritten = new ULONGByReference(); + COMUtils.checkRC(jnaData.WriteVirtualUncached(ullOffset, from, ulLen, pulBytesWritten)); + int written = pulBytesWritten.getValue().intValue(); + from.position(written + from.position()); + return written; + } + */ +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumeratorInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumeratorInternal.java new file mode 100644 index 0000000000..141b57eccd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/datamodel/script/debug/DataModelScriptDebugVariableSetEnumeratorInternal.java @@ -0,0 +1,58 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.datamodel.script.debug; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.datamodel.script.debug.DataModelScriptDebugVariableSetEnumerator; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.IDataModelScriptDebugVariableSetEnumerator; +import agent.dbgmodel.jna.dbgmodel.datamodel.script.debug.WrapIDataModelScriptDebugVariableSetEnumerator; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DataModelScriptDebugVariableSetEnumeratorInternal + extends DataModelScriptDebugVariableSetEnumerator { + Map CACHE = + new WeakValueHashMap<>(); + + static DataModelScriptDebugVariableSetEnumeratorInternal instanceFor( + WrapIDataModelScriptDebugVariableSetEnumerator data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, + DataModelScriptDebugVariableSetEnumeratorImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put( + new REFIID( + IDataModelScriptDebugVariableSetEnumerator.IID_IDATA_MODEL_SCRIPT_DEBUG_VARIABLE_SET_ENUMERATOR), + WrapIDataModelScriptDebugVariableSetEnumerator.class) // + .build(); + + static DataModelScriptDebugVariableSetEnumeratorInternal tryPreferredInterfaces( + InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces( + DataModelScriptDebugVariableSetEnumeratorInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostBaseClassImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostBaseClassImpl.java new file mode 100644 index 0000000000..161e2b8c30 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostBaseClassImpl.java @@ -0,0 +1,138 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.COMUtilsExtra; +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.debughost.*; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.SymbolKind; +import agent.dbgmodel.jna.dbgmodel.debughost.*; + +public abstract class DebugHostBaseClassImpl implements DebugHostBaseClassInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostBaseClass jnaData; + + public DebugHostBaseClassImpl(IDebugHostBaseClass jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public DebugHostContext getContext() { + PointerByReference ppContext = new PointerByReference(); + COMUtils.checkRC(jnaData.GetContext(ppContext)); + + WrapIDebugHostContext wrap = new WrapIDebugHostContext(ppContext.getValue()); + try { + return DebugHostContextInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostSymbolEnumerator enumerateChildren(SymbolKind kind, WString name) { + ULONG ulKind = new ULONG(kind.ordinal()); + PointerByReference ppEnum = new PointerByReference(); + HRESULT hr = jnaData.EnumerateChildren(ulKind, name, ppEnum); + if (hr.equals(COMUtilsExtra.E_FAIL)) { + return null; + } + COMUtils.checkRC(hr); + + WrapIDebugHostSymbolEnumerator wrap = new WrapIDebugHostSymbolEnumerator(ppEnum.getValue()); + try { + return DebugHostSymbolEnumeratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public SymbolKind getSymbolKind() { + ULONGByReference pulKind = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetSymbolKind(pulKind)); + return SymbolKind.values()[pulKind.getValue().intValue()]; + } + + @Override + public String getName() { + BSTRByReference bref = new BSTRByReference(); + COMUtils.checkRC(jnaData.GetName(bref)); + BSTR bstr = bref.getValue(); + String modelName = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return modelName; + } + + @Override + public DebugHostType1 getType() { + PointerByReference ppType = new PointerByReference(); + HRESULT hr = jnaData.GetType(ppType); + if (hr.equals(COMUtilsExtra.E_FAIL)) { + return null; + } + COMUtils.checkRC(hr); + + WrapIDebugHostType1 wrap = new WrapIDebugHostType1(ppType.getValue()); + try { + return DebugHostTypeInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostModule1 getContainingModule() { + PointerByReference ppType = new PointerByReference(); + COMUtils.checkRC(jnaData.GetContainingModule(ppType)); + + WrapIDebugHostModule1 wrap = new WrapIDebugHostModule1(ppType.getValue()); + try { + return DebugHostModuleInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public long getOffset() { + ULONGLONGByReference ppOffset = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.GetOffset(ppOffset)); + return ppOffset.getValue().longValue(); + } + + @Override + public IDebugHostBaseClass getJnaData() { + return jnaData; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostBaseClassInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostBaseClassInternal.java new file mode 100644 index 0000000000..2fb9b491f6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostBaseClassInternal.java @@ -0,0 +1,51 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostBase; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.X_IDebugHostBaseClass; +import agent.dbgmodel.jna.dbgmodel.debughost.X_WrapIDebugHostBaseClass; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostBaseClassInternal extends DebugHostBase { + + Map CACHE = new WeakValueHashMap<>(); + + static X_DebugHostBaseClassInternal instanceFor(X_WrapIDebugHostBaseClass data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, X_DebugHostBaseClassImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(X_IDebugHostBaseClass.IID_IDEBUG_HOST_BASE_CLASS), + X_WrapIDebugHostBaseClass.class) // + .build(); + + static X_DebugHostBaseClassInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(X_DebugHostBaseClassInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostConstantImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostConstantImpl.java new file mode 100644 index 0000000000..fde283bd2d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostConstantImpl.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Variant.VARIANT; + +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostConstant; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugHostConstantImpl extends DebugHostBaseClassImpl + implements DebugHostConstantInternal { + @SuppressWarnings("unused") + private final IDebugHostConstant jnaData; + + public DebugHostConstantImpl(IDebugHostConstant jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public VARIANT getValue() { + VARIANT.ByReference pValue = new VARIANT.ByReference(); + COMUtils.checkRC(jnaData.GetValue(pValue)); + return (VARIANT) pValue.getValue(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostConstantInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostConstantInternal.java new file mode 100644 index 0000000000..cdbbe9b55b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostConstantInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostConstant; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostConstant; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostConstant; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostConstantInternal extends DebugHostConstant { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostConstantInternal instanceFor(WrapIDebugHostConstant data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostConstantImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostConstant.IID_IDEBUG_HOST_CONSTANT), + WrapIDebugHostConstant.class) // + .build(); + + static DebugHostConstantInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostConstantInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostContextImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostContextImpl.java new file mode 100644 index 0000000000..4a6a558fd6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostContextImpl.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.debughost.DebugHostContext; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostContext; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugHostContextImpl implements DebugHostContextInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostContext jnaData; + + public DebugHostContextImpl(IDebugHostContext jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + public IDebugHostContext getJnaData() { + return jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public boolean isEqualTo(DebugHostContext context) { + Pointer pContext = context.getPointer(); + BOOLByReference pIsEqual = new BOOLByReference(); + COMUtils.checkRC(jnaData.IsEqualTo(pContext, pIsEqual)); + return pIsEqual.getValue().booleanValue(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostContextInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostContextInternal.java new file mode 100644 index 0000000000..1589a4e85b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostContextInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostContext; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostContext; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostContext; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostContextInternal extends DebugHostContext { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostContextInternal instanceFor(WrapIDebugHostContext data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostContextImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostContext.IID_IDEBUG_HOST_CONTEXT), + WrapIDebugHostContext.class) // + .build(); + + static DebugHostContextInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostContextInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostDataImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostDataImpl.java new file mode 100644 index 0000000000..f65d453ba8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostDataImpl.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostData; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugHostDataImpl extends DebugHostBaseClassImpl implements DebugHostDataInternal { + @SuppressWarnings("unused") + private final IDebugHostData jnaData; + + public DebugHostDataImpl(IDebugHostData jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public int getLocationKind() { + ULONGByReference pulLocationKind = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetLocationKind(pulLocationKind)); + return pulLocationKind.getValue().intValue(); + } + + @Override + public LOCATION getLocation() { + LOCATION.ByReference pLocation = new LOCATION.ByReference(); + COMUtils.checkRC(jnaData.GetLocation(pLocation)); + return new LOCATION(pLocation); + } + + @Override + public VARIANT getValue() { + VARIANT.ByReference pValue = new VARIANT.ByReference(); + COMUtils.checkRC(jnaData.GetValue(pValue)); + return (VARIANT) pValue.getValue(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostDataInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostDataInternal.java new file mode 100644 index 0000000000..3c0789b453 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostDataInternal.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostData; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostData; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostData; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostDataInternal extends DebugHostData { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostDataInternal instanceFor(WrapIDebugHostData data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostDataImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostData.IID_IDEBUG_HOST_DATA), WrapIDebugHostData.class) // + .build(); + + static DebugHostDataInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostDataInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostErrorSinkImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostErrorSinkImpl.java new file mode 100644 index 0000000000..6ace827123 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostErrorSinkImpl.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostErrorSink; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugHostErrorSinkImpl implements DebugHostErrorSinkInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostErrorSink jnaData; + + public DebugHostErrorSinkImpl(IDebugHostErrorSink jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void reportError(int errorClass, HRESULT hrError, WString message) { + ULONG ulErrorClass = new ULONG(errorClass); + COMUtils.checkRC(jnaData.ReportError(ulErrorClass, hrError, message)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostErrorSinkInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostErrorSinkInternal.java new file mode 100644 index 0000000000..3fd059d7a7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostErrorSinkInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostErrorSink; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostErrorSink; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostErrorSink; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostErrorSinkInternal extends DebugHostErrorSink { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostErrorSinkInternal instanceFor(WrapIDebugHostErrorSink data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostErrorSinkImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostErrorSink.IID_IDEBUG_HOST_ERROR_SINK), + WrapIDebugHostErrorSink.class) // + .build(); + + static DebugHostErrorSinkInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostErrorSinkInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorImpl1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorImpl1.java new file mode 100644 index 0000000000..1a89a93961 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorImpl1.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.debughost.DebugHostContext; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.main.ModelObjectImpl; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostEvaluator1; + +public class DebugHostEvaluatorImpl1 implements DebugHostEvaluatorInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostEvaluator1 jnaData; + + public DebugHostEvaluatorImpl1(IDebugHostEvaluator1 jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public ModelObject evaluateExpression(DebugHostContext context, + WString expression, ModelObject bindingContext) { + Pointer pContext = context.getPointer(); + Pointer pBindingContext = bindingContext.getPointer(); + PointerByReference ppResult = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + jnaData.EvaluateExpression(pContext, expression, pBindingContext, ppResult, ppMetadata); + + return ModelObjectImpl.getObjectWithMetadata(ppResult, ppMetadata); + } + + @Override + public ModelObject evaluateExtendedExpression(DebugHostContext context, WString expression, + ModelObject bindingContext) { + Pointer pContext = context.getPointer(); + Pointer pBindingContext = bindingContext.getPointer(); + PointerByReference ppResult = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + jnaData.EvaluateExtendedExpression(pContext, expression, pBindingContext, ppResult, + ppMetadata); + + return ModelObjectImpl.getObjectWithMetadata(ppResult, ppMetadata); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorImpl2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorImpl2.java new file mode 100644 index 0000000000..1fd2a619c3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorImpl2.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostEvaluator2; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.main.ModelObjectImpl; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostEvaluator2; + +public class DebugHostEvaluatorImpl2 extends DebugHostEvaluatorImpl1 + implements DebugHostEvaluator2 { + @SuppressWarnings("unused") + private final IDebugHostEvaluator2 jnaData; + + public DebugHostEvaluatorImpl2(IDebugHostEvaluator2 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public ModelObject assignTo(ModelObject assignmentReference, ModelObject assignmentValue) { + Pointer pAssignmentReference = assignmentReference.getPointer(); + Pointer pAssignmentValue = assignmentValue.getPointer(); + PointerByReference ppAssignmentResult = new PointerByReference(); + PointerByReference ppAssignmentMetadata = new PointerByReference(); + jnaData.AssignTo(pAssignmentReference, pAssignmentValue, ppAssignmentResult, + ppAssignmentMetadata); + + return ModelObjectImpl.getObjectWithMetadata(ppAssignmentResult, ppAssignmentMetadata); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorInternal.java new file mode 100644 index 0000000000..01bdf5949f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostEvaluatorInternal.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostEvaluator1; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostEvaluatorInternal extends DebugHostEvaluator1 { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostEvaluatorInternal instanceFor(WrapIDebugHostEvaluator1 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostEvaluatorImpl1::new); + } + + static DebugHostEvaluatorInternal instanceFor(WrapIDebugHostEvaluator2 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostEvaluatorImpl2::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostEvaluator2.IID_IDEBUG_HOST_EVALUATOR2), + WrapIDebugHostEvaluator2.class) // + .put(new REFIID(IDebugHostEvaluator1.IID_IDEBUG_HOST_EVALUATOR), + WrapIDebugHostEvaluator1.class) // + .build(); + + static DebugHostEvaluatorInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostEvaluatorInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostExtensabilityImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostExtensabilityImpl.java new file mode 100644 index 0000000000..28cf9d54a4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostExtensabilityImpl.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostExtensability; + +public class DebugHostExtensabilityImpl implements DebugHostExtensabilityInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostExtensability jnaData; + + public DebugHostExtensabilityImpl(IDebugHostExtensability jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void createFunctionAlias(WString aliasName, ModelObject functionObject) { + Pointer pFunctionObject = functionObject.getPointer(); + COMUtils.checkRC(jnaData.CreateFunctionAlias(aliasName, pFunctionObject)); + } + + @Override + public void destroyFunctionAlias(WString aliasName) { + COMUtils.checkRC(jnaData.DestroyFunctionAlias(aliasName)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostExtensabilityInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostExtensabilityInternal.java new file mode 100644 index 0000000000..87ace450ff --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostExtensabilityInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostExtensability; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostExtensability; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostExtensability; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostExtensabilityInternal extends DebugHostExtensability { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostExtensabilityInternal instanceFor(WrapIDebugHostExtensability data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostExtensabilityImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostExtensability.IID_IDEBUG_HOST_EXTENSABILITY), + WrapIDebugHostExtensability.class) // + .build(); + + static DebugHostExtensabilityInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostExtensabilityInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostFieldImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostFieldImpl.java new file mode 100644 index 0000000000..6e2881f26b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostFieldImpl.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostField; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugHostFieldImpl extends DebugHostBaseClassImpl implements DebugHostFieldInternal { + @SuppressWarnings("unused") + private final IDebugHostField jnaData; + + public DebugHostFieldImpl(IDebugHostField jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public int getLocationKind() { + ULONGByReference pulLocationKind = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetLocationKind(pulLocationKind)); + return pulLocationKind.getValue().intValue(); + } + + @Override + public long getOffset() { + ULONGLONGByReference ppOffset = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.GetOffset(ppOffset)); + return ppOffset.getValue().longValue(); + } + + @Override + public LOCATION getLocation() { + LOCATION.ByReference pLocation = new LOCATION.ByReference(); + COMUtils.checkRC(jnaData.GetLocation(pLocation)); + return new LOCATION(pLocation); + } + + @Override + public VARIANT getValue() { + VARIANT.ByReference pValue = new VARIANT.ByReference(); + COMUtils.checkRC(jnaData.GetValue(pValue)); + return (VARIANT) pValue.getValue(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostFieldInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostFieldInternal.java new file mode 100644 index 0000000000..7854f81453 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostFieldInternal.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostField; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostField; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostField; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostFieldInternal extends DebugHostField { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostFieldInternal instanceFor(WrapIDebugHostField data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostFieldImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostField.IID_IDEBUG_HOST_FIELD), WrapIDebugHostField.class) // + .build(); + + static DebugHostFieldInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostFieldInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostImpl.java new file mode 100644 index 0000000000..90055bc933 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostImpl.java @@ -0,0 +1,111 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.UnknownEx; +import agent.dbgmodel.dbgmodel.debughost.*; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.impl.dbgmodel.UnknownExInternal; +import agent.dbgmodel.impl.dbgmodel.main.KeyStoreInternal; +import agent.dbgmodel.jna.dbgmodel.WrapIUnknownEx; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHost; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostContext; +import agent.dbgmodel.jna.dbgmodel.main.WrapIKeyStore; + +public class DebugHostImpl implements DebugHostInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHost jnaData; + + public DebugHostImpl(IDebugHost jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public UnknownEx getHostDefinedInterface() { + PointerByReference ppHostUnk = new PointerByReference(); + COMUtils.checkRC(jnaData.GetHostDefinedInterface(ppHostUnk)); + + WrapIUnknownEx wrap = new WrapIUnknownEx(ppHostUnk.getValue()); + try { + return UnknownExInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostContext getCurrentContext() { + PointerByReference ppContext = new PointerByReference(); + COMUtils.checkRC(jnaData.GetCurrentContext(ppContext)); + + WrapIDebugHostContext wrap = new WrapIDebugHostContext(ppContext.getValue()); + try { + return DebugHostContextInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public KeyStore getDefaultMetadata() { + PointerByReference ppDefaultMetadataStore = new PointerByReference(); + COMUtils.checkRC(jnaData.GetDefaultMetadata(ppDefaultMetadataStore)); + + WrapIKeyStore wrap = new WrapIKeyStore(ppDefaultMetadataStore.getValue()); + try { + return KeyStoreInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostMemory1 asMemory() { + return DebugHostMemoryInternal.tryPreferredInterfaces(jnaData::QueryInterface); + } + + @Override + public DebugHostSymbols asSymbols() { + return DebugHostSymbolsInternal.tryPreferredInterfaces(jnaData::QueryInterface); + } + + @Override + public DebugHostScriptHost asScriptHost() { + return DebugHostScriptHostInternal.tryPreferredInterfaces(jnaData::QueryInterface); + } + + @Override + public DebugHostEvaluator2 asEvaluator() { + return (DebugHostEvaluator2) DebugHostEvaluatorInternal.tryPreferredInterfaces( + jnaData::QueryInterface); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostInternal.java new file mode 100644 index 0000000000..68e30984bc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostInternal.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHost; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHost; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHost; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostInternal extends DebugHost { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostInternal instanceFor(WrapIDebugHost data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHost.IID_IDEBUG_HOST), WrapIDebugHost.class) // + .build(); + + static DebugHostInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryImpl1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryImpl1.java new file mode 100644 index 0000000000..a4abe03f2d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryImpl1.java @@ -0,0 +1,114 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.debughost.DebugHostContext; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostMemory1; + +public class DebugHostMemoryImpl1 implements DebugHostMemoryInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostMemory1 jnaData; + + public DebugHostMemoryImpl1(IDebugHostMemory1 jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public long readBytes(DebugHostContext context, LOCATION location, ByteBuffer buffer, + long bufferSize) { + if (bufferSize > buffer.remaining()) { + throw new BufferOverflowException(); + } + Pointer pContext = context.getPointer(); + ULONGLONG pulBufferSize = new ULONGLONG(bufferSize); + ULONGLONGByReference pulBytesRead = new ULONGLONGByReference(); + COMUtils.checkRC( + jnaData.ReadBytes(pContext, location, buffer, pulBufferSize, pulBytesRead)); + long read = pulBytesRead.getValue().longValue(); + buffer.position((int) (read + buffer.position())); + return read; + } + + @Override + public long writeBytes(DebugHostContext context, LOCATION location, ByteBuffer buffer, + long bufferSize) { + if (bufferSize > buffer.remaining()) { + throw new BufferOverflowException(); + } + Pointer pContext = context.getPointer(); + ULONGLONG pulBufferSize = new ULONGLONG(bufferSize); + ULONGLONGByReference pulBytesWritten = new ULONGLONGByReference(); + COMUtils.checkRC( + jnaData.WriteBytes(pContext, location, buffer, pulBufferSize, pulBytesWritten)); + long written = pulBytesWritten.getValue().longValue(); + buffer.position((int) (written + buffer.position())); + return written; + } + + @Override + public ULONGLONGByReference readPointers(DebugHostContext context, LOCATION location, + long count) { + Pointer pContext = context.getPointer(); + ULONGLONG pCount = new ULONGLONG(count); + ULONGLONGByReference pPointers = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.ReadPointers(pContext, location, pCount, pPointers)); + return pPointers; + } + + @Override + public ULONGLONGByReference writePointers(DebugHostContext context, LOCATION location, + long count) { + Pointer pContext = context.getPointer(); + ULONGLONG pCount = new ULONGLONG(count); + ULONGLONGByReference pPointers = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.WritePointers(pContext, location, pCount, pPointers)); + return pPointers; + } + + @Override + public String GetDisplayStringForLocation(DebugHostContext context, LOCATION location, + boolean verbose) { + Pointer pContext = context.getPointer(); + BOOL bVerbose = new BOOL(verbose); + BSTRByReference bref = new BSTRByReference(); + COMUtils.checkRC( + jnaData.GetDisplayStringForLocation(pContext, location, bVerbose, bref)); + BSTR bstr = bref.getValue(); + String locationName = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return locationName; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryImpl2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryImpl2.java new file mode 100644 index 0000000000..3a4ce5f7d2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryImpl2.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostContext; +import agent.dbgmodel.dbgmodel.debughost.DebugHostMemory2; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostMemory2; + +public class DebugHostMemoryImpl2 extends DebugHostMemoryImpl1 implements DebugHostMemory2 { + private final IDebugHostMemory2 jnaData; + + public DebugHostMemoryImpl2(IDebugHostMemory2 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public LOCATION linearizeLocation(DebugHostContext context, LOCATION location) { + Pointer pContext = context.getPointer(); + LOCATION.ByReference pLinearizedLocation = new LOCATION.ByReference(); + COMUtils.checkRC(jnaData.LinearizeLocation(pContext, location, pLinearizedLocation)); + return new LOCATION(pLinearizedLocation); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryInternal.java new file mode 100644 index 0000000000..89c37a3424 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostMemoryInternal.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostMemory1; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostMemoryInternal extends DebugHostMemory1 { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostMemoryInternal instanceFor(WrapIDebugHostMemory1 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostMemoryImpl1::new); + } + + static DebugHostMemoryInternal instanceFor(WrapIDebugHostMemory2 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostMemoryImpl2::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostMemory2.IID_IDEBUG_HOST_MEMORY2), + WrapIDebugHostMemory2.class) // + .put(new REFIID(IDebugHostMemory1.IID_IDEBUG_HOST_MEMORY), + WrapIDebugHostMemory1.class) // + .build(); + + static DebugHostMemoryInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostMemoryInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleImpl1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleImpl1.java new file mode 100644 index 0000000000..8286ae60a8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleImpl1.java @@ -0,0 +1,149 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.COMUtilsExtra; +import agent.dbgmodel.dbgmodel.debughost.DebugHostSymbol1; +import agent.dbgmodel.dbgmodel.debughost.DebugHostType1; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.debughost.*; + +public class DebugHostModuleImpl1 extends DebugHostBaseClassImpl + implements DebugHostModuleInternal { + @SuppressWarnings("unused") + private final IDebugHostModule1 jnaData; + private long fileVersion; + private long productVersion; + + public DebugHostModuleImpl1(IDebugHostModule1 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public String getImageName(boolean allowPath) { + BOOL bAllowPath = new BOOL(allowPath); + BSTRByReference bref = new BSTRByReference(); + COMUtils.checkRC(jnaData.GetImageName(bAllowPath, bref)); + BSTR bstr = bref.getValue(); + String imageName = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return imageName; + } + + @Override + public LOCATION getBaseLocation() { + LOCATION.ByReference pLocation = new LOCATION.ByReference(); + COMUtils.checkRC(jnaData.GetBaseLocation(pLocation)); + return new LOCATION(pLocation); + } + + @Override + public void getVersion() { + ULONGLONGByReference pulFileVersion = new ULONGLONGByReference(); + ULONGLONGByReference pulProductVersion = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.GetVersion(pulFileVersion, pulProductVersion)); + fileVersion = pulFileVersion.getValue().longValue(); + productVersion = pulProductVersion.getValue().longValue(); + } + + @Override + public DebugHostType1 findTypeByName(String typeName) { + PointerByReference ppType = new PointerByReference(); + HRESULT hr = jnaData.FindTypeByName(new WString(typeName), ppType); + if (hr.equals(COMUtilsExtra.E_FAIL) || hr.equals(COMUtilsExtra.E_BOUNDS)) { + System.out.println(typeName + " NOT FOUND"); + return null; + } + COMUtils.checkRC(hr); + + WrapIDebugHostType1 wrap = new WrapIDebugHostType1(ppType.getValue()); + try { + return DebugHostTypeInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostSymbol1 findSymbolByRVA(long rva) { + ULONGLONG ulRva = new ULONGLONG(rva); + PointerByReference ppSymbol = new PointerByReference(); + HRESULT hr = jnaData.FindSymbolByRVA(ulRva, ppSymbol); + if (hr.equals(COMUtilsExtra.E_FAIL)) { + System.out.println(rva + " NOT FOUND"); + return null; + } + COMUtils.checkRC(hr); + + WrapIDebugHostSymbol1 wrap = new WrapIDebugHostSymbol1(ppSymbol.getValue()); + try { + return DebugHostSymbolInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostSymbol1 findSymbolByName(String symbolName) { + PointerByReference ppSymbol = new PointerByReference(); + HRESULT hr = jnaData.FindSymbolByName(new WString(symbolName), ppSymbol); + if (hr.equals(COMUtilsExtra.E_FAIL)) { + System.out.println(symbolName + " NOT FOUND"); + return null; + } + COMUtils.checkRC(hr); + + WrapIDebugHostSymbol1 wrap = new WrapIDebugHostSymbol1(ppSymbol.getValue()); + try { + return DebugHostSymbolInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + public long getFileVersion() { + return fileVersion; + } + + public long getProductVersion() { + return productVersion; + } + + @Override + public DebugHostSymbol1 asSymbol() { + return DebugHostSymbolInternal.tryPreferredInterfaces(jnaData::QueryInterface); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleImpl2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleImpl2.java new file mode 100644 index 0000000000..09b8d5abc6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleImpl2.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostModule2; +import agent.dbgmodel.dbgmodel.debughost.DebugHostSymbol1; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostModule2; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostSymbol1; + +public class DebugHostModuleImpl2 extends DebugHostModuleImpl1 implements DebugHostModule2 { + private final IDebugHostModule2 jnaData; + + private long offset; + + public DebugHostModuleImpl2(IDebugHostModule2 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public DebugHostSymbol1 findContainingSymbolByRVA(long rva) { + ULONGLONG ulRva = new ULONGLONG(rva); + PointerByReference ppSymbol = new PointerByReference(); + ULONGLONGByReference pulOffset = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.FindContainingSymbolByRVA(ulRva, ppSymbol, pulOffset)); + + offset = pulOffset.getValue().longValue(); + + WrapIDebugHostSymbol1 wrap = new WrapIDebugHostSymbol1(ppSymbol.getValue()); + try { + return DebugHostSymbolInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public long getOffset() { + return offset; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleInternal.java new file mode 100644 index 0000000000..330cb673f2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleInternal.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostModule1; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostModuleInternal extends DebugHostModule1 { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostModuleInternal instanceFor(WrapIDebugHostModule1 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostModuleImpl1::new); + } + + static DebugHostModuleInternal instanceFor(WrapIDebugHostModule2 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostModuleImpl2::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostModule2.IID_IDEBUG_HOST_MODULE2), + WrapIDebugHostModule2.class) // + .put(new REFIID(IDebugHostModule1.IID_IDEBUG_HOST_MODULE), + WrapIDebugHostModule1.class) // + .build(); + + static DebugHostModuleInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostModuleInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleSignatureImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleSignatureImpl.java new file mode 100644 index 0000000000..597b082669 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleSignatureImpl.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.debughost.DebugHostModule1; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostModuleSignature; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugHostModuleSignatureImpl implements DebugHostModuleSignatureInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostModuleSignature jnaData; + + public DebugHostModuleSignatureImpl(IDebugHostModuleSignature jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public boolean IsMatch(DebugHostModule1 module) { + Pointer pModule = module.getPointer(); + BOOLByReference pIsMatch = new BOOLByReference(); + COMUtils.checkRC(jnaData.IsMatch(pModule, pIsMatch)); + return pIsMatch.getValue().booleanValue(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleSignatureInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleSignatureInternal.java new file mode 100644 index 0000000000..b2b8943597 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostModuleSignatureInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostModuleSignature; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostModuleSignature; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostModuleSignature; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostModuleSignatureInternal extends DebugHostModuleSignature { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostModuleSignatureInternal instanceFor(WrapIDebugHostModuleSignature data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostModuleSignatureImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostModuleSignature.IID_IDEBUG_HOST_MODULE_SIGNATURE), + WrapIDebugHostModuleSignature.class) // + .build(); + + static DebugHostModuleSignatureInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostModuleSignatureInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostPublicImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostPublicImpl.java new file mode 100644 index 0000000000..a49ca44223 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostPublicImpl.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LocationKind; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostPublic; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugHostPublicImpl extends DebugHostBaseClassImpl implements DebugHostPublicInternal { + @SuppressWarnings("unused") + private final IDebugHostPublic jnaData; + + public DebugHostPublicImpl(IDebugHostPublic jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public LocationKind getLocationKind() { + ULONGByReference pulLocationKind = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetLocationKind(pulLocationKind)); + return LocationKind.values()[pulLocationKind.getValue().intValue()]; + } + + @Override + public LOCATION getLocation() { + LOCATION.ByReference pLocation = new LOCATION.ByReference(); + COMUtils.checkRC(jnaData.GetLocation(pLocation)); + return new LOCATION(pLocation); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostPublicInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostPublicInternal.java new file mode 100644 index 0000000000..d5ce337069 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostPublicInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostPublic; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostPublic; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostPublic; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostPublicInternal extends DebugHostPublic { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostPublicInternal instanceFor(WrapIDebugHostPublic data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostPublicImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostPublic.IID_IDEBUG_HOST_PUBLIC), + WrapIDebugHostPublic.class) // + .build(); + + static DebugHostPublicInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostPublicInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostScriptHostImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostScriptHostImpl.java new file mode 100644 index 0000000000..03fa75dedb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostScriptHostImpl.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.datamodel.script.DataModelScript; +import agent.dbgmodel.dbgmodel.debughost.DebugHostContext; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostScriptHost; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostContext; + +public class DebugHostScriptHostImpl implements DebugHostScriptHostInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostScriptHost jnaData; + + public DebugHostScriptHostImpl(IDebugHostScriptHost jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public DebugHostContext createContext(DataModelScript script) { + Pointer pScript = script.getPointer(); + PointerByReference ppScriptContext = new PointerByReference(); + COMUtils.checkRC(jnaData.CreateContext(pScript, ppScriptContext)); + + WrapIDebugHostContext wrap = new WrapIDebugHostContext(ppScriptContext.getValue()); + try { + return DebugHostContextInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostScriptHostInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostScriptHostInternal.java new file mode 100644 index 0000000000..983727e949 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostScriptHostInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostScriptHost; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostScriptHost; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostScriptHost; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostScriptHostInternal extends DebugHostScriptHost { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostScriptHostInternal instanceFor(WrapIDebugHostScriptHost data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostScriptHostImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostScriptHost.IID_IDEBUG_HOST_SCRIPT_HOST), + WrapIDebugHostScriptHost.class) // + .build(); + + static DebugHostScriptHostInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostScriptHostInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostStatusImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostStatusImpl.java new file mode 100644 index 0000000000..12e9e01741 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostStatusImpl.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostStatus; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugHostStatusImpl implements DebugHostStatusInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostStatus jnaData; + + public DebugHostStatusImpl(IDebugHostStatus jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public boolean PollUserInterrupt() { + BOOLByReference pInterruptRequested = new BOOLByReference(); + COMUtils.checkRC(jnaData.PollUserInterrupt(pInterruptRequested)); + return pInterruptRequested.getValue().booleanValue(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostStatusInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostStatusInternal.java new file mode 100644 index 0000000000..46c0e21847 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostStatusInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostStatus; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostStatus; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostStatus; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostStatusInternal extends DebugHostStatus { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostStatusInternal instanceFor(WrapIDebugHostStatus data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostStatusImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostStatus.IID_IDEBUG_HOST_STATUS), + WrapIDebugHostStatus.class) // + .build(); + + static DebugHostStatusInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostStatusInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolEnumeratorImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolEnumeratorImpl.java new file mode 100644 index 0000000000..a2de989464 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolEnumeratorImpl.java @@ -0,0 +1,72 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.debughost.DebugHostSymbol1; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostSymbolEnumerator; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostSymbol1; + +public class DebugHostSymbolEnumeratorImpl implements DebugHostSymbolEnumeratorInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostSymbolEnumerator jnaData; + + public DebugHostSymbolEnumeratorImpl(IDebugHostSymbolEnumerator jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void reset() { + COMUtils.checkRC(jnaData.Reset()); + } + + @Override + public DebugHostSymbol1 getNext() { + PointerByReference ppSymbol = new PointerByReference(); + HRESULT hr = jnaData.GetNext(ppSymbol); + if (hr.longValue() != 0) { + return null; + } + /* + if (hr.equals(COMUtilsExtra.E_BOUNDS)) { + return null; + } + COMUtils.checkRC(hr); + */ + + WrapIDebugHostSymbol1 wrap = new WrapIDebugHostSymbol1(ppSymbol.getValue()); + try { + return DebugHostSymbolInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolEnumeratorInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolEnumeratorInternal.java new file mode 100644 index 0000000000..ec0bef0979 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolEnumeratorInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostSymbolEnumerator; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostSymbolEnumerator; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostSymbolEnumerator; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostSymbolEnumeratorInternal extends DebugHostSymbolEnumerator { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostSymbolEnumeratorInternal instanceFor(WrapIDebugHostSymbolEnumerator data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostSymbolEnumeratorImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostSymbolEnumerator.IID_IDEBUG_HOST_SYMBOL_ENUMERATOR), + WrapIDebugHostSymbolEnumerator.class) // + .build(); + + static DebugHostSymbolEnumeratorInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostSymbolEnumeratorInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolImpl1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolImpl1.java new file mode 100644 index 0000000000..d7b7916334 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolImpl1.java @@ -0,0 +1,81 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinDef.ULONG; + +import agent.dbgmodel.dbgmodel.debughost.*; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostSymbol1; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class DebugHostSymbolImpl1 extends DebugHostBaseClassImpl + implements DebugHostSymbolInternal { + + private final IDebugHostSymbol1 jnaData; + + public DebugHostSymbolImpl1(IDebugHostSymbol1 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public int compareAgainst(DebugHostSymbol1 comparisonSymbol, long comparisonFlags) { + Pointer pComparisonSymbol = comparisonSymbol.getPointer(); + ULONG ulComparisonFlags = new ULONG(comparisonFlags); + BOOLByReference pMatches = new BOOLByReference(); + COMUtils.checkRC(jnaData.CompareAgainst(pComparisonSymbol, ulComparisonFlags, pMatches)); + return pMatches.getValue().intValue(); + } + + @Override + public DebugHostBaseClass asBaseClass() { + return X_DebugHostBaseClassInternal.tryPreferredInterfaces(jnaData::QueryInterface); + } + + @Override + public DebugHostConstant asConstant() { + return DebugHostConstantInternal.tryPreferredInterfaces(jnaData::QueryInterface); + } + + @Override + public DebugHostData asData() { + return DebugHostDataInternal.tryPreferredInterfaces(jnaData::QueryInterface); + } + + @Override + public DebugHostField asField() { + return DebugHostFieldInternal.tryPreferredInterfaces(jnaData::QueryInterface); + } + + @Override + public DebugHostModule1 asModule() { + return DebugHostModuleInternal.tryPreferredInterfaces(jnaData::QueryInterface); + } + + @Override + public DebugHostPublic asPublic() { + return DebugHostPublicInternal.tryPreferredInterfaces(jnaData::QueryInterface); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolImpl2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolImpl2.java new file mode 100644 index 0000000000..d62f6a00bd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolImpl2.java @@ -0,0 +1,65 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostSymbol2; +import agent.dbgmodel.dbgmodel.debughost.DebugHostSymbolEnumerator; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostSymbol2; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostSymbolEnumerator; + +public class DebugHostSymbolImpl2 extends DebugHostSymbolImpl1 implements DebugHostSymbol2 { + private final IDebugHostSymbol2 jnaData; + + public DebugHostSymbolImpl2(IDebugHostSymbol2 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public DebugHostSymbolEnumerator enumerateChildrenEx(long kind, WString name, + Structure.ByReference searchInfo) { + ULONG ulKind = new ULONG(kind); + PointerByReference ppEnum = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateChildrenEx(ulKind, name, searchInfo, ppEnum)); + + WrapIDebugHostSymbolEnumerator wrap = new WrapIDebugHostSymbolEnumerator(ppEnum.getValue()); + try { + return DebugHostSymbolEnumeratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public int getLanguage() { + ULONGByReference pulKind = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetLanguage(pulKind)); + return pulKind.getValue().intValue(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolInternal.java new file mode 100644 index 0000000000..811c1c3027 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolInternal.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostSymbol1; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostSymbolInternal extends DebugHostSymbol1 { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostSymbolInternal instanceFor(WrapIDebugHostSymbol1 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostSymbolImpl1::new); + } + + static DebugHostSymbolInternal instanceFor(WrapIDebugHostSymbol2 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostSymbolImpl2::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostSymbol2.IID_IDEBUG_HOST_SYMBOL2), + WrapIDebugHostSymbol2.class) // + .put(new REFIID(IDebugHostSymbol1.IID_IDEBUG_HOST_SYMBOL), + WrapIDebugHostSymbol1.class) // + .build(); + + static DebugHostSymbolInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostSymbolInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolsImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolsImpl.java new file mode 100644 index 0000000000..e86ebfa18d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolsImpl.java @@ -0,0 +1,171 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.debughost.*; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.debughost.*; + +public class DebugHostSymbolsImpl implements DebugHostSymbolsInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostSymbols jnaData; + + private LOCATION derivedLocation; + + public DebugHostSymbolsImpl(IDebugHostSymbols jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public DebugHostModuleSignature createModuleSignature(WString pwszModuleName, + WString pwszMinVersion, WString pwszMaxVersion) { + PointerByReference ppModuleSignature = new PointerByReference(); + COMUtils.checkRC(jnaData.CreateModuleSignature(pwszModuleName, pwszMinVersion, + pwszMaxVersion, ppModuleSignature)); + + WrapIDebugHostModuleSignature wrap = + new WrapIDebugHostModuleSignature(ppModuleSignature.getValue()); + try { + return DebugHostModuleSignatureInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostTypeSignature createTypeSignature(WString signatureSpecification, + DebugHostModule1 module) { + Pointer pModule = module == null ? null : module.getPointer(); + PointerByReference ppTypeSignature = new PointerByReference(); + COMUtils.checkRC( + jnaData.CreateTypeSignature(signatureSpecification, pModule, + ppTypeSignature)); + + WrapIDebugHostTypeSignature wrap = + new WrapIDebugHostTypeSignature(ppTypeSignature.getValue()); + try { + return DebugHostTypeSignatureInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostTypeSignature createTypeSignatureForModuleRange(WString signatureSpecification, + WString pwszModuleName, WString pwszMinVersion, WString pwszMaxVersion) { + PointerByReference ppTypeSignature = new PointerByReference(); + COMUtils.checkRC(jnaData.CreateTypeSignatureForModuleRange(signatureSpecification, + pwszModuleName, pwszMinVersion, pwszMaxVersion, ppTypeSignature)); + + WrapIDebugHostTypeSignature wrap = + new WrapIDebugHostTypeSignature(ppTypeSignature.getValue()); + try { + return DebugHostTypeSignatureInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostSymbolEnumerator enumerateModules(DebugHostContext context) { + Pointer pContext = context.getPointer(); + PointerByReference moduleEnum = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateModules(pContext, moduleEnum)); + + WrapIDebugHostSymbolEnumerator wrap = + new WrapIDebugHostSymbolEnumerator(moduleEnum.getValue()); + try { + return DebugHostSymbolEnumeratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostModule1 findModuleByName(DebugHostContext context, String moduleName) { + Pointer pContext = context.getPointer(); + PointerByReference ppModule = new PointerByReference(); + COMUtils.checkRC(jnaData.FindModuleByName(pContext, new WString(moduleName), ppModule)); + + WrapIDebugHostModule1 wrap = new WrapIDebugHostModule1(ppModule.getValue()); + try { + return DebugHostModuleInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostModule1 findModuleByLocation(DebugHostContext context, + LOCATION moduleLocation) { + Pointer pContext = context.getPointer(); + PointerByReference ppModule = new PointerByReference(); + COMUtils.checkRC(jnaData.FindModuleByLocation(pContext, moduleLocation, ppModule)); + + WrapIDebugHostModule1 wrap = new WrapIDebugHostModule1(ppModule.getValue()); + try { + return DebugHostModuleInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostType1 getMostDerivedObject(DebugHostContext context, LOCATION location, + DebugHostType1 objectType) { + Pointer pContext = context.getPointer(); + Pointer pObjectType = objectType.getPointer(); + LOCATION.ByReference pDerivedLocation = new LOCATION.ByReference(); + PointerByReference ppDerivedType = new PointerByReference(); + COMUtils.checkRC( + jnaData.GetMostDerivedObject(pContext, location, pObjectType, pDerivedLocation, + ppDerivedType)); + + derivedLocation = new LOCATION(pDerivedLocation); + WrapIDebugHostType1 wrap = new WrapIDebugHostType1(ppDerivedType.getValue()); + try { + return DebugHostTypeInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + public LOCATION getDerivedLocation() { + return derivedLocation; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolsInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolsInternal.java new file mode 100644 index 0000000000..10f884e5fa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostSymbolsInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostSymbols; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostSymbols; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostSymbols; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostSymbolsInternal extends DebugHostSymbols { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostSymbolsInternal instanceFor(WrapIDebugHostSymbols data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostSymbolsImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostSymbols.IID_IDEBUG_HOST_SYMBOLS), + WrapIDebugHostSymbols.class) // + .build(); + + static DebugHostSymbolsInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostSymbolsInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeImpl1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeImpl1.java new file mode 100644 index 0000000000..b8f146a30c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeImpl1.java @@ -0,0 +1,265 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WTypes.VARTYPE; +import com.sun.jna.platform.win32.WTypes.VARTYPEByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.COMUtilsExtra; +import agent.dbgmodel.dbgmodel.debughost.DebugHostSymbol1; +import agent.dbgmodel.dbgmodel.debughost.DebugHostType1; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.*; +import agent.dbgmodel.jna.dbgmodel.debughost.*; + +public class DebugHostTypeImpl1 extends DebugHostBaseClassImpl implements DebugHostTypeInternal { + @SuppressWarnings("unused") + private final IDebugHostType1 jnaData; + + private ULONG intrinsicKind; + private VARTYPE carrierType; + private ULONG lsbOfField; + private ULONG lengthOfField; + + public DebugHostTypeImpl1(IDebugHostType1 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public TypeKind getTypeKind() { + ULONGByReference pulKind = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetTypeKind(pulKind)); + return TypeKind.values()[pulKind.getValue().intValue()]; + } + + @Override + public long getSize() { + ULONGLONGByReference pulSize = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.GetSize(pulSize)); + return pulSize.getValue().longValue(); + } + + @Override + public DebugHostType1 getBaseType() { + PointerByReference ppBaseType = new PointerByReference(); + HRESULT hr = jnaData.GetBaseType(ppBaseType); + if (hr.equals(COMUtilsExtra.E_FAIL)) { + return null; + } + COMUtils.checkRC(hr); + + WrapIDebugHostType1 wrap = new WrapIDebugHostType1(ppBaseType.getValue()); + try { + return DebugHostTypeInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public int getHashCode() { + ULONGByReference pulHashCode = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetHashCode(pulHashCode)); + return pulHashCode.getValue().intValue(); + } + + @Override + public IntrinsicKind getIntrinsicType() { + ULONGByReference pulIntrinsicKind = new ULONGByReference(); + VARTYPEByReference pCarrierType = new VARTYPEByReference(); + HRESULT hr = jnaData.GetIntrinsicType(pulIntrinsicKind, pCarrierType); + if (hr.equals(COMUtilsExtra.E_FAIL)) { + return null; + } + COMUtils.checkRC(hr); + + carrierType = pCarrierType.getValue(); + int intValue = pulIntrinsicKind.getValue().intValue(); + return IntrinsicKind.values()[intValue]; + } + + @Override + public void getBitField() { + ULONGByReference pulLsbOfField = new ULONGByReference(); + ULONGByReference pulLengthOfField = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetBitField(pulLsbOfField, pulLengthOfField)); + lsbOfField = pulLsbOfField.getValue(); + lengthOfField = pulLengthOfField.getValue(); + } + + @Override + public PointerKind getPointerKind() { + ULONGByReference pulPointerKind = new ULONGByReference(); + HRESULT hr = jnaData.GetPointerKind(pulPointerKind); + if (hr.equals(COMUtilsExtra.E_FAIL)) { + return null; + } + COMUtils.checkRC(hr); + return PointerKind.values()[pulPointerKind.getValue().intValue()]; + } + + @Override + public DebugHostType1 getMemberType() { + PointerByReference ppMemberType = new PointerByReference(); + COMUtils.checkRC(jnaData.GetMemberType(ppMemberType)); + + WrapIDebugHostType1 wrap = new WrapIDebugHostType1(ppMemberType.getValue()); + try { + return DebugHostTypeInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostType1 createPointerTo(int kind) { + ULONG ulKind = new ULONG(kind); + PointerByReference ppMemberType = new PointerByReference(); + COMUtils.checkRC(jnaData.CreatePointerTo(ulKind, ppMemberType)); + + WrapIDebugHostType1 wrap = new WrapIDebugHostType1(ppMemberType.getValue()); + try { + return DebugHostTypeInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public long getArrayDimensionality() { + ULONGLONGByReference pulArrayDimensionality = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.GetArrayDimensionality(pulArrayDimensionality)); + return pulArrayDimensionality.getValue().longValue(); + } + + @Override + public ARRAY_DIMENSION getArrayDimensions(long dimensions) { + ULONGLONG pDimensions = new ULONGLONG(dimensions); + ARRAY_DIMENSION.ByReference ppDimensions = new ARRAY_DIMENSION.ByReference(); + COMUtils.checkRC(jnaData.GetArrayDimensions(pDimensions, ppDimensions)); + return new ARRAY_DIMENSION(ppDimensions); + } + + @Override + public DebugHostType1 createArrayOf(long dimensions, ARRAY_DIMENSION.ByReference pDimensions) { + ULONGLONG ulDimensions = new ULONGLONG(dimensions); + PointerByReference ppNewType = new PointerByReference(); + COMUtils.checkRC(jnaData.CreateArrayOf(ulDimensions, pDimensions, ppNewType)); + + WrapIDebugHostType1 wrap = new WrapIDebugHostType1(ppNewType.getValue()); + try { + return DebugHostTypeInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public int getFunctionCallingConvention() { + ULONGByReference pulConventionKind = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetFunctionCallingConvention(pulConventionKind)); + return pulConventionKind.getValue().intValue(); + } + + @Override + public DebugHostType1 getFunctionReturnType() { + PointerByReference ppReturnType = new PointerByReference(); + COMUtils.checkRC(jnaData.GetFunctionReturnType(ppReturnType)); + + WrapIDebugHostType1 wrap = new WrapIDebugHostType1(ppReturnType.getValue()); + try { + return DebugHostTypeInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostType1 getFunctionParameterTypeAt(int i) { + ULONG ulI = new ULONG(i); + PointerByReference ppReturnType = new PointerByReference(); + COMUtils.checkRC(jnaData.GetFunctionParameterTypeAt(ulI, ppReturnType)); + + WrapIDebugHostType1 wrap = new WrapIDebugHostType1(ppReturnType.getValue()); + try { + return DebugHostTypeInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public boolean isGeneric() { + BOOLByReference bIsGeneric = new BOOLByReference(); + COMUtils.checkRC(jnaData.IsGeneric(bIsGeneric)); + return bIsGeneric.getValue().booleanValue(); + } + + @Override + public long getGenericArgumentCount() { + ULONGLONGByReference pulArgCount = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.GetGenericArgumentCount(pulArgCount)); + return pulArgCount.getValue().longValue(); + } + + @Override + public DebugHostSymbol1 getGenericArgumentAt(int i) { + ULONG ulI = new ULONG(i); + PointerByReference ppArgument = new PointerByReference(); + COMUtils.checkRC(jnaData.GetGenericArgumentAt(ulI, ppArgument)); + + WrapIDebugHostSymbol1 wrap = new WrapIDebugHostSymbol1(ppArgument.getValue()); + try { + return DebugHostSymbolInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + public ULONG getIntrinsicKind() { + return intrinsicKind; + } + + public VARTYPE getCarrierType() { + return carrierType; + } + + public ULONG getLsbOfField() { + return lsbOfField; + } + + public ULONG getLengthOfField() { + return lengthOfField; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeImpl2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeImpl2.java new file mode 100644 index 0000000000..e3d3cf7637 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeImpl2.java @@ -0,0 +1,100 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostType2; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostType2; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostType2; + +public class DebugHostTypeImpl2 extends DebugHostTypeImpl1 implements DebugHostType2 { + private final IDebugHostType2 jnaData; + + public DebugHostTypeImpl2(IDebugHostType2 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public boolean isTypedef() { + BOOLByReference bIsTypedef = new BOOLByReference(); + COMUtils.checkRC(jnaData.IsTypedef(bIsTypedef)); + return bIsTypedef.getValue().booleanValue(); + } + + @Override + public DebugHostType2 getTypedefBaseType() { + PointerByReference ppBaseType = new PointerByReference(); + COMUtils.checkRC(jnaData.GetTypedefBaseType(ppBaseType)); + + WrapIDebugHostType2 wrap = new WrapIDebugHostType2(ppBaseType.getValue()); + try { + return (DebugHostType2) DebugHostTypeInternal.tryPreferredInterfaces( + wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostType2 getTypedefFinalBaseType() { + PointerByReference ppFinalBaseType = new PointerByReference(); + COMUtils.checkRC(jnaData.GetTypedefFinalBaseType(ppFinalBaseType)); + + WrapIDebugHostType2 wrap = new WrapIDebugHostType2(ppFinalBaseType.getValue()); + try { + return (DebugHostType2) DebugHostTypeInternal.tryPreferredInterfaces( + wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public int getFunctionVarArgsKind() { + ULONGByReference pulVarArgsKind = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetFunctionVarArgsKind(pulVarArgsKind)); + return pulVarArgsKind.getValue().intValue(); + } + + @Override + public DebugHostType2 getFunctionInstancePointerType() { + PointerByReference ppInstancePointerType = new PointerByReference(); + COMUtils.checkRC(jnaData.GetFunctionInstancePointerType(ppInstancePointerType)); + + WrapIDebugHostType2 wrap = new WrapIDebugHostType2(ppInstancePointerType.getValue()); + try { + return (DebugHostType2) DebugHostTypeInternal.tryPreferredInterfaces( + wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeInternal.java new file mode 100644 index 0000000000..b6e9c42278 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeInternal.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostType1; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostTypeInternal extends DebugHostType1 { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostTypeInternal instanceFor(WrapIDebugHostType1 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostTypeImpl1::new); + } + + static DebugHostTypeInternal instanceFor(WrapIDebugHostType2 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostTypeImpl2::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostType2.IID_IDEBUG_HOST_TYPE2), WrapIDebugHostType2.class) // + .put(new REFIID(IDebugHostType1.IID_IDEBUG_HOST_TYPE), WrapIDebugHostType1.class) // + .build(); + + static DebugHostTypeInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostTypeInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeSignatureImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeSignatureImpl.java new file mode 100644 index 0000000000..003fa82c73 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeSignatureImpl.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.debughost.DebugHostType1; +import agent.dbgmodel.dbgmodel.debughost.DebugHostTypeSignature; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostTypeSignature; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelObject; + +public class DebugHostTypeSignatureImpl implements DebugHostTypeSignatureInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IDebugHostTypeSignature jnaData; + + private DebugHostSymbolEnumeratorInternal wildcardMatches; + + public DebugHostTypeSignatureImpl(IDebugHostTypeSignature jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public int getHashCode() { + ULONGByReference pulHashCode = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetHashCode(pulHashCode)); + return pulHashCode.getValue().intValue(); + } + + @Override + public boolean isMatch(DebugHostType1 type) { + Pointer pType = type.getPointer(); + BOOLByReference pIsMatch = new BOOLByReference(); + PointerByReference ppWildcardMatches = new PointerByReference(); + COMUtils.checkRC(jnaData.IsMatch(pType, pIsMatch, ppWildcardMatches)); + + WrapIModelObject wrap1 = new WrapIModelObject(ppWildcardMatches.getValue()); + try { + wildcardMatches = + DebugHostSymbolEnumeratorInternal.tryPreferredInterfaces(wrap1::QueryInterface); + } + finally { + wrap1.Release(); + } + + return pIsMatch.getValue().booleanValue(); + } + + @Override + public int compareAgainst(DebugHostTypeSignature typeSignature) { + Pointer pTypeSignature = typeSignature.getPointer(); + ULONGByReference pResult = new ULONGByReference(); + COMUtils.checkRC(jnaData.CompareAgainst(pTypeSignature, pResult)); + return pResult.getValue().intValue(); + } + + public DebugHostSymbolEnumeratorInternal getWildcardMatches() { + return wildcardMatches; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeSignatureInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeSignatureInternal.java new file mode 100644 index 0000000000..34e894110d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/DebugHostTypeSignatureInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostTypeSignature; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.IDebugHostTypeSignature; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostTypeSignature; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface DebugHostTypeSignatureInternal extends DebugHostTypeSignature { + Map CACHE = new WeakValueHashMap<>(); + + static DebugHostTypeSignatureInternal instanceFor(WrapIDebugHostTypeSignature data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, DebugHostTypeSignatureImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IDebugHostTypeSignature.IID_IDEBUG_HOST_TYPE_SIGNATURE), + WrapIDebugHostTypeSignature.class) // + .build(); + + static DebugHostTypeSignatureInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(DebugHostTypeSignatureInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/X_DebugHostBaseClassImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/X_DebugHostBaseClassImpl.java new file mode 100644 index 0000000000..57ad29f8d5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/X_DebugHostBaseClassImpl.java @@ -0,0 +1,51 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; + +import agent.dbgmodel.jna.dbgmodel.debughost.X_IDebugHostBaseClass; + +import com.sun.jna.platform.win32.COM.COMUtils; + +public class X_DebugHostBaseClassImpl extends DebugHostBaseClassImpl + implements X_DebugHostBaseClassInternal { + @SuppressWarnings("unused") + private final X_IDebugHostBaseClass jnaData; + + public X_DebugHostBaseClassImpl(X_IDebugHostBaseClass jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public long getOffset() { + ULONGLONGByReference ppOffset = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.GetOffset(ppOffset)); + return ppOffset.getValue().longValue(); + } + + public X_IDebugHostBaseClass getJnaData() { + return jnaData; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/X_DebugHostBaseClassInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/X_DebugHostBaseClassInternal.java new file mode 100644 index 0000000000..7f8923b4c2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/debughost/X_DebugHostBaseClassInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.debughost; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.debughost.DebugHostBaseClass; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.debughost.X_IDebugHostBaseClass; +import agent.dbgmodel.jna.dbgmodel.debughost.X_WrapIDebugHostBaseClass; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface X_DebugHostBaseClassInternal extends DebugHostBaseClass { + Map CACHE = new WeakValueHashMap<>(); + + static X_DebugHostBaseClassInternal instanceFor(X_WrapIDebugHostBaseClass data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, X_DebugHostBaseClassImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(X_IDebugHostBaseClass.IID_IDEBUG_HOST_BASE_CLASS), + X_WrapIDebugHostBaseClass.class) // + .build(); + + static X_DebugHostBaseClassInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(X_DebugHostBaseClassInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyEnumeratorImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyEnumeratorImpl.java new file mode 100644 index 0000000000..208607ba82 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyEnumeratorImpl.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.COMUtilsExtra; +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.main.IKeyEnumerator; + +public class KeyEnumeratorImpl implements KeyEnumeratorInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IKeyEnumerator jnaData; + + private ModelObject value; + + public KeyEnumeratorImpl(IKeyEnumerator jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void reset() { + COMUtils.checkRC(jnaData.Reset()); + } + + @Override + public String getNext() { + BSTRByReference bref = new BSTRByReference(); + PointerByReference ppValue = new PointerByReference(); + PointerByReference ppMetaData = new PointerByReference(); + HRESULT hr = jnaData.GetNext(bref, ppValue, ppMetaData); + if (hr.equals(COMUtilsExtra.E_BOUNDS)) { + //System.err.println("ret null"); + return null; + } + COMUtils.checkRC(hr); + + Pointer val = ppValue.getValue(); + if (val != null) { + value = ModelObjectImpl.getObjectWithMetadata(ppValue, ppMetaData); + } + else { + value = null; + } + BSTR bstr = bref.getValue(); + String key = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return key; + } + + @Override + public ModelObject getValue() { + return value; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyEnumeratorInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyEnumeratorInternal.java new file mode 100644 index 0000000000..48551b3941 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyEnumeratorInternal.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.main.KeyEnumerator; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.main.IKeyEnumerator; +import agent.dbgmodel.jna.dbgmodel.main.WrapIKeyEnumerator; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface KeyEnumeratorInternal extends KeyEnumerator { + Map CACHE = new WeakValueHashMap<>(); + + static KeyEnumeratorInternal instanceFor(WrapIKeyEnumerator data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, KeyEnumeratorImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IKeyEnumerator.IID_IKEY_ENUMERATOR), WrapIKeyEnumerator.class) // + .build(); + + static KeyEnumeratorInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(KeyEnumeratorInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyStoreImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyStoreImpl.java new file mode 100644 index 0000000000..2e82df8a75 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyStoreImpl.java @@ -0,0 +1,80 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.main.IKeyStore; + +public class KeyStoreImpl implements KeyStoreInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IKeyStore jnaData; + + public KeyStoreImpl(IKeyStore jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public ModelObject getKey(WString key) { + PointerByReference ppObject = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + COMUtils.checkRC(jnaData.GetKey(key, ppObject, ppMetadata)); + + return ModelObjectImpl.getObjectWithMetadata(ppObject, ppMetadata); + } + + @Override + public void setKey(WString key, ModelObject object, KeyStore metadata) { + Pointer pObject = object.getPointer(); + Pointer pMetadata = metadata.getPointer(); + COMUtils.checkRC(jnaData.SetKey(key, pObject, pMetadata)); + } + + @Override + public ModelObject getKeyValue(WString key) { + PointerByReference ppObject = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + COMUtils.checkRC(jnaData.GetKeyValue(key, ppObject, ppMetadata)); + + return ModelObjectImpl.getObjectWithMetadata(ppObject, ppMetadata); + } + + @Override + public void setKeyValue(WString key, ModelObject object) { + Pointer pObject = object.getPointer(); + COMUtils.checkRC(jnaData.SetKeyValue(key, pObject)); + } + + @Override + public void clearKeys() { + COMUtils.checkRC(jnaData.ClearKeys()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyStoreInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyStoreInternal.java new file mode 100644 index 0000000000..4cc34f2e01 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/KeyStoreInternal.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.main.IKeyStore; +import agent.dbgmodel.jna.dbgmodel.main.WrapIKeyStore; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface KeyStoreInternal extends KeyStore { + Map CACHE = new WeakValueHashMap<>(); + + static KeyStoreInternal instanceFor(WrapIKeyStore data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, KeyStoreImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IKeyStore.IID_IKEY_STORE), WrapIKeyStore.class) // + .build(); + + static KeyStoreInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(KeyStoreInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelIteratorImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelIteratorImpl.java new file mode 100644 index 0000000000..42b07a5abc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelIteratorImpl.java @@ -0,0 +1,83 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.COMUtilsExtra; +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.main.IModelIterator; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelObject; + +public class ModelIteratorImpl implements ModelIteratorInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IModelIterator jnaData; + + private ModelObject indexers; + + public ModelIteratorImpl(IModelIterator jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void reset() { + COMUtils.checkRC(jnaData.Reset()); + } + + @Override + public ModelObject getNext(long dimensions) { + PointerByReference ppObject = new PointerByReference(); + ULONGLONG ulDimensions = new ULONGLONG(dimensions); + PointerByReference ppIndexers = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + HRESULT hr = jnaData.GetNext(ppObject, ulDimensions, ppIndexers, ppMetadata); + if (hr.equals(COMUtilsExtra.E_BOUNDS)) { + return null; + } + COMUtils.checkRC(hr); + + if (ppIndexers.getValue() != null) { + WrapIModelObject wrap = new WrapIModelObject(ppIndexers.getValue()); + try { + indexers = ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + return ModelObjectImpl.getObjectWithMetadata(ppObject, ppMetadata); + } + + @Override + public ModelObject getIndexers() { + return indexers; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelIteratorInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelIteratorInternal.java new file mode 100644 index 0000000000..4f2706a66c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelIteratorInternal.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.main.ModelIterator; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.main.IModelIterator; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelIterator; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface ModelIteratorInternal extends ModelIterator { + Map CACHE = new WeakValueHashMap<>(); + + static ModelIteratorInternal instanceFor(WrapIModelIterator data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, ModelIteratorImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IModelIterator.IID_IMODEL_ITERATOR), WrapIModelIterator.class) // + .build(); + + static ModelIteratorInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(ModelIteratorInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceImpl1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceImpl1.java new file mode 100644 index 0000000000..31c50f1b6c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceImpl1.java @@ -0,0 +1,117 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.KeyStore; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.main.IModelKeyReference; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelObject; + +public class ModelKeyReferenceImpl1 implements ModelKeyReferenceInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IModelKeyReference jnaData; + + public ModelKeyReferenceImpl1(IModelKeyReference jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public String getKeyName() { + BSTRByReference bref = new BSTRByReference(); + COMUtils.checkRC(jnaData.GetKeyName(bref)); + BSTR bstr = bref.getValue(); + String keyName = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return keyName; + } + + @Override + public ModelObject getOriginalObject() { + PointerByReference ppOriginalObject = new PointerByReference(); + COMUtils.checkRC(jnaData.GetOriginalObject(ppOriginalObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppOriginalObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject getContextObject() { + PointerByReference ppContainingObject = new PointerByReference(); + COMUtils.checkRC(jnaData.GetContextObject(ppContainingObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppContainingObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject getKey() { + PointerByReference ppObject = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + COMUtils.checkRC(jnaData.GetKey(ppObject, ppMetadata)); + + return ModelObjectImpl.getObjectWithMetadata(ppObject, ppMetadata); + } + + @Override + public ModelObject getKeyValue() { + PointerByReference ppObject = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + COMUtils.checkRC(jnaData.GetKeyValue(ppObject, ppMetadata)); + + return ModelObjectImpl.getObjectWithMetadata(ppObject, ppMetadata); + } + + @Override + public void setKey(ModelObject object, KeyStore metadata) { + Pointer pObject = object.getPointer(); + Pointer pMetadata = metadata.getPointer(); + COMUtils.checkRC(jnaData.SetKey(pObject, pMetadata)); + } + + @Override + public void setKeyValue(ModelObject object, KeyStore metadata) { + Pointer pObject = object.getPointer(); + Pointer pMetadata = metadata.getPointer(); + COMUtils.checkRC(jnaData.SetKey(pObject, pMetadata)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceImpl2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceImpl2.java new file mode 100644 index 0000000000..6c52fbf60f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceImpl2.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.COM.COMUtils; + +import agent.dbgmodel.dbgmodel.main.ModelKeyReference2; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.main.IModelKeyReference2; + +public class ModelKeyReferenceImpl2 extends ModelKeyReferenceImpl1 implements ModelKeyReference2 { + @SuppressWarnings("unused") + private final IModelKeyReference2 jnaData; + + public ModelKeyReferenceImpl2(IModelKeyReference2 jnaData) { + super(jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void overrideContextObject(ModelObject newContextObject) { + Pointer pNewContextObject = newContextObject.getPointer(); + COMUtils.checkRC(jnaData.OverrideContextObject(pNewContextObject)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceInternal.java new file mode 100644 index 0000000000..72c237e80d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelKeyReferenceInternal.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.main.ModelKeyReference1; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.main.*; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface ModelKeyReferenceInternal extends ModelKeyReference1 { + Map CACHE = new WeakValueHashMap<>(); + + static ModelKeyReferenceInternal instanceFor(WrapIModelKeyReference1 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, ModelKeyReferenceImpl1::new); + } + + static ModelKeyReferenceInternal instanceFor(WrapIModelKeyReference2 data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, ModelKeyReferenceImpl2::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IModelKeyReference2.IID_IMODEL_REFERENCE2), + WrapIModelKeyReference2.class) // + .put(new REFIID(IModelKeyReference.IID_IMODEL_REFERENCE), + WrapIModelKeyReference1.class) // + .build(); + + static ModelKeyReferenceInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(ModelKeyReferenceInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelMethodImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelMethodImpl.java new file mode 100644 index 0000000000..1c8e0a19f8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelMethodImpl.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.main.IModelMethod; + +public class ModelMethodImpl implements ModelMethodInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IModelMethod jnaData; + + public ModelMethodImpl(IModelMethod jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public ModelObject call(ModelObject contextObject, long argCount, Pointer[] ppArguments) { + Pointer pContextObject = contextObject.getPointer(); + ULONGLONG ulArgCount = new ULONGLONG(argCount); + + PointerByReference ppResult = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + COMUtils.checkRC( + jnaData.Call(pContextObject, ulArgCount, ppArguments, ppResult, ppMetadata)); + + return ModelObjectImpl.getObjectWithMetadata(ppResult, ppMetadata); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelMethodInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelMethodInternal.java new file mode 100644 index 0000000000..3b4db49ce4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelMethodInternal.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.main.ModelMethod; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.main.IModelMethod; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelMethod; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface ModelMethodInternal extends ModelMethod { + Map CACHE = new WeakValueHashMap<>(); + + static ModelMethodInternal instanceFor(WrapIModelMethod data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, ModelMethodImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IModelMethod.IID_IMODEL_METHOD), WrapIModelMethod.class) // + .build(); + + static ModelMethodInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(ModelMethodInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelObjectImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelObjectImpl.java new file mode 100644 index 0000000000..417b9fe46d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelObjectImpl.java @@ -0,0 +1,912 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import java.util.*; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WTypes.VARTYPE; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.platform.win32.COM.Unknown; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.*; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.concept.*; +import agent.dbgmodel.dbgmodel.datamodel.DataModelManager1; +import agent.dbgmodel.dbgmodel.debughost.DebugHostContext; +import agent.dbgmodel.dbgmodel.debughost.DebugHostType1; +import agent.dbgmodel.dbgmodel.main.*; +import agent.dbgmodel.impl.dbgmodel.UnknownExImpl; +import agent.dbgmodel.impl.dbgmodel.UnknownExInternal; +import agent.dbgmodel.impl.dbgmodel.concept.*; +import agent.dbgmodel.impl.dbgmodel.debughost.DebugHostContextInternal; +import agent.dbgmodel.impl.dbgmodel.debughost.DebugHostTypeInternal; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.*; +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.WrapIUnknownEx; +import agent.dbgmodel.jna.dbgmodel.concept.*; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostContext; +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostType1; +import agent.dbgmodel.jna.dbgmodel.main.*; +import ghidra.util.Msg; + +public class ModelObjectImpl implements ModelObjectInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IModelObject jnaData; + + private KeyStore metadata; + private ModelObject contextObject; + + private LOCATION targetLocation; + private ModelObject indexer; + private String key; + + public ModelObjectImpl(IModelObject jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public IModelObject getJnaData() { + return jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public DebugHostContext getContext() { + PointerByReference ppContext = new PointerByReference(); + COMUtils.checkRC(jnaData.GetContext(ppContext)); + + Pointer value = ppContext.getValue(); + if (value == null) { + return null; + } + + WrapIDebugHostContext wrap = new WrapIDebugHostContext(value); + try { + return DebugHostContextInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObjectKind getKind() { + try { + ULONGByReference pulKind = new ULONGByReference(); + COMUtils.checkRC(jnaData.GetKind(pulKind)); + int i = pulKind.getValue().intValue(); + return ModelObjectKind.values()[i]; + } + catch (Exception e) { + System.err.println("GetKind error " + getSearchKey() + ":" + e); + } + return ModelObjectKind.OBJECT_ERROR; + } + + @Override + public Object getIntrinsicValue() { + VARIANT.ByReference pIntrinsicData = new VARIANT.ByReference(); + HRESULT hr = jnaData.GetIntrinsicValue(pIntrinsicData); + if (hr.equals(COMUtilsExtra.E_FAIL)) { + return null; + } + COMUtils.checkRC(hr); + return pIntrinsicData.getValue(); + } + + @Override + public VARIANT getIntrinsicValueAs(VARTYPE vt) { + VARIANT.ByReference pIntrinsicData = new VARIANT.ByReference(); + COMUtils.checkRC(jnaData.GetIntrinsicValueAs(vt, pIntrinsicData)); + return (VARIANT) pIntrinsicData.getValue(); + } + + @Override + public ModelObject getKeyValue(String searchKey) { + PointerByReference ppObject = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + HRESULT hr = jnaData.GetKeyValue(new WString(searchKey), ppObject, ppMetadata); + if (!hr.equals(new HRESULT(0)) && + (searchKey.equals("Parameters") || searchKey.equals("LocalVariables"))) { + return null; + } + if (hr.equals(COMUtilsExtra.E_FAIL)) { + System.err.println(searchKey + " failed"); + return null; + } + if (hr.equals(COMUtilsExtra.E_BOUNDS)) { + System.err.println(searchKey + " out of bounds"); + return null; + } + if (hr.equals(COMUtilsExtra.E_INVALID_PARAM)) { + Msg.debug(this, searchKey + " invalid param"); + return null; + } + if (hr.equals(COMUtilsExtra.E_SCOPE_NOT_FOUND)) { + Msg.debug(this, searchKey + " scope not found"); + return null; + } + COMUtils.checkRC(hr); + + ModelObject retval = getObjectWithMetadata(ppObject, ppMetadata); + retval.setSearchKey(searchKey); + return retval; + } + + @Override + public void setKeyValue(WString key, ModelObject object) { + Pointer pObject = object.getPointer(); + COMUtils.checkRC(jnaData.SetKeyValue(key, pObject)); + } + + @Override + public KeyEnumerator enumerateKeyValues() { + PointerByReference ppEnumerator = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateKeyValues(ppEnumerator)); + + WrapIKeyEnumerator wrap = new WrapIKeyEnumerator(ppEnumerator.getValue()); + try { + return KeyEnumeratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject getRawValue(int kind, WString name, int searchFlags) { + ULONG ulKind = new ULONG(kind); + ULONG ulSearchFlags = new ULONG(searchFlags); + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC(jnaData.GetRawValue(ulKind, name, ulSearchFlags, ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public RawEnumerator enumerateRawValues(int kind, int searchFlags) { + ULONG ulKind = new ULONG(kind); + ULONG ulSearchFlags = new ULONG(searchFlags); + PointerByReference ppEnumerator = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateRawValues(ulKind, ulSearchFlags, ppEnumerator)); + + WrapIRawEnumerator wrap = new WrapIRawEnumerator(ppEnumerator.getValue()); + try { + return RawEnumeratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject dereference() { + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC(jnaData.Dereference(ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public ModelObject tryCastToRuntimeType() { + PointerByReference ppRuntimeTypedObject = new PointerByReference(); + COMUtils.checkRC(jnaData.TryCastToRuntimeType(ppRuntimeTypedObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppRuntimeTypedObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public Concept getConcept(REFIID conceptId) { + PointerByReference ppConceptInterface = new PointerByReference(); + PointerByReference ppConceptMetadata = new PointerByReference(); + HRESULT hr = jnaData.GetConcept(conceptId, ppConceptInterface, ppConceptMetadata); + if (hr.equals(COMUtilsExtra.E_NOINTERFACE)) { + //System.err.println("No such interface " + conceptId); + return null; + } + COMUtils.checkRC(hr); + + Concept object = null; + KeyStore mdata = null; + + Pointer value = ppConceptMetadata.getValue(); + if (value != null) { + WrapIKeyStore wrap1 = new WrapIKeyStore(value); + try { + mdata = KeyStoreInternal.tryPreferredInterfaces(wrap1::QueryInterface); + } + finally { + wrap1.Release(); + } + } + + if (conceptId.getValue() + .equals(IStringDisplayableConcept.IID_ISTRING_DISPLAYABLE_CONCEPT)) { + WrapIStringDisplayableConcept wrap = + new WrapIStringDisplayableConcept(ppConceptInterface.getValue()); + try { + object = + StringDisplayableConceptInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + if (conceptId.getValue() + .equals(IPreferredRuntimeTypeConcept.IID_IPREFERRED_RUNTIME_TYPE_CONCEPT)) { + WrapIPreferredRuntimeTypeConcept wrap = + new WrapIPreferredRuntimeTypeConcept(ppConceptInterface.getValue()); + try { + object = PreferredRuntimeTypeConceptInternal + .tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + if (conceptId.getValue().equals(IIterableConcept.IID_IITERABLE_CONCEPT)) { + WrapIIterableConcept wrap = new WrapIIterableConcept(ppConceptInterface.getValue()); + try { + object = IterableConceptInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + if (conceptId.getValue().equals(IIndexableConcept.IID_IINDEXABLE_CONCEPT)) { + WrapIIndexableConcept wrap = new WrapIIndexableConcept(ppConceptInterface.getValue()); + try { + object = IndexableConceptInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + if (conceptId.getValue().equals(IEquatableConcept.IID_IEQUATABLE_CONCEPT)) { + WrapIEquatableConcept wrap = new WrapIEquatableConcept(ppConceptInterface.getValue()); + try { + object = EquatableConceptInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + if (conceptId.getValue() + .equals(IDynamicKeyProviderConcept.IID_IDYNAMIC_KEY_PROVIDER_CONCEPT)) { + WrapIDynamicKeyProviderConcept wrap = + new WrapIDynamicKeyProviderConcept(ppConceptInterface.getValue()); + try { + object = + DynamicKeyProviderConceptInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + if (conceptId.getValue() + .equals(IDynamicConceptProviderConcept.IID_IDYNAMIC_CONCEPT_PROVIDER_CONCEPT)) { + WrapIDynamicConceptProviderConcept wrap = + new WrapIDynamicConceptProviderConcept(ppConceptInterface.getValue()); + try { + object = DynamicConceptProviderConceptInternal + .tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + if (conceptId.getValue().equals(IDataModelConcept.IID_IDATA_MODEL_CONCEPT)) { + WrapIDataModelConcept wrap = new WrapIDataModelConcept(ppConceptInterface.getValue()); + try { + object = DataModelConceptInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + if (conceptId.getValue().equals(IComparableConcept.IID_ICOMPARABLE_CONCEPT)) { + WrapIComparableConcept wrap = new WrapIComparableConcept(ppConceptInterface.getValue()); + try { + object = ComparableConceptInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + if (object != null && mdata != null) { + object.setMetadata(mdata); + } + return object; + } + + @Override + public LOCATION getLocation() { + LOCATION.ByReference pLocation = new LOCATION.ByReference(); + COMUtils.checkRC(jnaData.GetLocation(pLocation)); + return new LOCATION(pLocation); + } + + @Override + public DebugHostType1 getTypeInfo() { + PointerByReference ppType = new PointerByReference(); + COMUtils.checkRC(jnaData.GetTypeInfo(ppType)); + + Pointer value = ppType.getValue(); + if (value == null) { + return null; + } + + WrapIDebugHostType1 wrap = new WrapIDebugHostType1(value); + try { + return DebugHostTypeInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public DebugHostType1 getTargetInfo() { + LOCATION.ByReference pLocation = new LOCATION.ByReference(); + PointerByReference ppType = new PointerByReference(); + COMUtils.checkRC(jnaData.GetTargetInfo(pLocation, ppType)); + + targetLocation = new LOCATION(pLocation); + + Pointer value = ppType.getValue(); + if (value == null) { + return null; + } + + WrapIDebugHostType1 wrap = new WrapIDebugHostType1(value); + try { + return DebugHostTypeInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public long getNumberOfParentModels() { + ULONGLONGByReference pulNumModels = new ULONGLONGByReference(); + COMUtils.checkRC(jnaData.GetNumberOfParentModels(pulNumModels)); + return pulNumModels.getValue().longValue(); + } + + @Override + public ModelObject getParentModel(int i) { + ULONG ulI = new ULONG(i); + PointerByReference ppModel = new PointerByReference(); + PointerByReference ppContextObject = new PointerByReference(); + COMUtils.checkRC(jnaData.GetParentModel(ulI, ppModel, ppContextObject)); + + return getObjectWithContext(ppModel, ppContextObject); + } + + @Override + public void addParentModel(ModelObject model, ModelObject contextObject, boolean override) { + Pointer pModel = model.getPointer(); + Pointer pContextObject = contextObject.getPointer(); + BOOL bOverride = new BOOL(override); + COMUtils.checkRC(jnaData.AddParentModel(pModel, pContextObject, bOverride)); + } + + @Override + public void removeParentModel(ModelObject model) { + Pointer pModel = model.getPointer(); + COMUtils.checkRC(jnaData.RemoveParentModel(pModel)); + } + + @Override + public ModelObject getKey(String searchKey) { + WString kstr = new WString(searchKey); + PointerByReference ppObject = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + COMUtils.checkRC(jnaData.GetKey(kstr, ppObject, ppMetadata)); + + ModelObject retval = getObjectWithMetadata(ppObject, ppMetadata); + retval.setSearchKey(key); + return retval; + } + + @Override + public ModelObject getKeyReference(String searchKey) { + WString kstr = new WString(searchKey); + PointerByReference ppObjectReference = new PointerByReference(); + PointerByReference ppMetadata = new PointerByReference(); + COMUtils.checkRC(jnaData.GetKeyReference(kstr, ppObjectReference, ppMetadata)); + + ModelObject retval = getObjectWithMetadata(ppObjectReference, ppMetadata); + retval.setSearchKey(key); + return retval; + } + + @Override + public void setKey(WString key, ModelObject object, KeyStore conceptMetadata) { + Pointer pObject = object.getPointer(); + Pointer pMetadata = conceptMetadata.getPointer(); + COMUtils.checkRC(jnaData.SetKey(key, pObject, pMetadata)); + } + + @Override + public void clearKeys() { + COMUtils.checkRC(jnaData.ClearKeys()); + } + + @Override + public KeyEnumerator enumerateKeys() { + PointerByReference ppEnumerator = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateKeys(ppEnumerator)); + + WrapIKeyEnumerator wrap = new WrapIKeyEnumerator(ppEnumerator.getValue()); + try { + return KeyEnumeratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public KeyEnumerator enumerateKeyReferences() { + PointerByReference ppEnumerator = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateKeyReferences(ppEnumerator)); + + WrapIKeyEnumerator wrap = new WrapIKeyEnumerator(ppEnumerator.getValue()); + try { + return KeyEnumeratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public void setConcept(REFIID conceptId, ModelObject conceptInterface, + ModelObject conceptMetadata) { + Pointer pConceptInterface = conceptInterface.getPointer(); + Pointer pConceptMetadata = conceptMetadata.getPointer(); + COMUtils.checkRC(jnaData.SetConcept(conceptId, pConceptInterface, pConceptMetadata)); + + } + + @Override + public void clearConcepts() { + COMUtils.checkRC(jnaData.ClearConcepts()); + } + + @Override + public ModelObject getRawReference(int kind, WString name, int searchFlags) { + ULONG ulKind = new ULONG(kind); + ULONG ulSearchFlags = new ULONG(searchFlags); + PointerByReference ppObject = new PointerByReference(); + COMUtils.checkRC(jnaData.GetRawValue(ulKind, name, ulSearchFlags, ppObject)); + + WrapIModelObject wrap = new WrapIModelObject(ppObject.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public RawEnumerator enumerateRawReferences(int kind, int searchFlags) { + ULONG ulKind = new ULONG(kind); + ULONG ulSearchFlags = new ULONG(searchFlags); + PointerByReference ppEnumerator = new PointerByReference(); + COMUtils.checkRC(jnaData.EnumerateRawValues(ulKind, ulSearchFlags, ppEnumerator)); + + WrapIRawEnumerator wrap = new WrapIRawEnumerator(ppEnumerator.getValue()); + try { + return RawEnumeratorInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public void setContextForDataModel(ModelObject dataModelObject, IUnknownEx context) { + Pointer pDataModelObject = dataModelObject.getPointer(); + Pointer pContext = context.getPointer(); + COMUtils.checkRC(jnaData.SetContextForDataModel(pDataModelObject, pContext)); + + } + + @Override + public UnknownEx getContextForDataModel(ModelObject dataModelObject) { + Pointer pDataModelObject = dataModelObject.getPointer(); + PointerByReference ppContext = new PointerByReference(); + COMUtils.checkRC(jnaData.GetContextForDataModel(pDataModelObject, ppContext)); + + WrapIUnknownEx wrap = new WrapIUnknownEx(ppContext.getValue()); + try { + return UnknownExInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public boolean compare(ModelObject contextObject, ModelObject other) { + Pointer pOther = other.getPointer(); + BOOLByReference bEqual = new BOOLByReference(); + COMUtils.checkRC(jnaData.Compare(pOther, bEqual)); + return bEqual.getValue().booleanValue(); + } + + /***********************/ + /* CONVENIENCE METHODS */ + /***********************/ + + @Override + public KeyStore getMetadata() { + return metadata; + } + + @Override + public void setMetadata(KeyStore metadata) { + this.metadata = metadata; + } + + public ModelObject getContextObject() { + return contextObject; + } + + @Override + public void setContextObject(ModelObject context) { + this.contextObject = context; + } + + public LOCATION getTargetLocation() { + return targetLocation; + } + + public static ModelObject getObjectWithContext(PointerByReference ppObject, + PointerByReference ppContext) { + WrapIModelObject wrap0 = new WrapIModelObject(ppObject.getValue()); + try { + ModelObject object = ModelObjectInternal.tryPreferredInterfaces(wrap0::QueryInterface); + WrapIModelObject wrap1 = new WrapIModelObject(ppContext.getValue()); + try { + ModelObject context = + ModelObjectInternal.tryPreferredInterfaces(wrap1::QueryInterface); + object.setContextObject(context); + } + finally { + wrap1.Release(); + } + return object; + } + finally { + wrap0.Release(); + } + } + + public static ModelObject getObjectWithMetadata(PointerByReference ppObject, + PointerByReference ppMetadata) { + WrapIModelObject wrap0 = new WrapIModelObject(ppObject.getValue()); + try { + ModelObject object = ModelObjectInternal.tryPreferredInterfaces(wrap0::QueryInterface); + Pointer value = ppMetadata.getValue(); + if (value != null) { + WrapIKeyStore wrap1 = new WrapIKeyStore(value); + try { + KeyStore mdata = KeyStoreInternal.tryPreferredInterfaces(wrap1::QueryInterface); + object.setMetadata(mdata); + } + finally { + wrap1.Release(); + } + } + return object; + } + finally { + wrap0.Release(); + } + } + + public static UnknownEx getUnknownWithMetadata(PointerByReference ppObject, + PointerByReference ppMetadata) { + WrapIUnknownEx wrap0 = new WrapIUnknownEx(ppObject.getValue()); + try { + UnknownEx object = UnknownExInternal.tryPreferredInterfaces(wrap0::QueryInterface); + Pointer value = ppMetadata.getValue(); + if (value != null) { + WrapIKeyStore wrap1 = new WrapIKeyStore(value); + try { + KeyStore mdata = KeyStoreInternal.tryPreferredInterfaces(wrap1::QueryInterface); + ((UnknownExImpl) object).setMetadata(mdata); + } + finally { + wrap1.Release(); + } + } + return object; + } + finally { + wrap0.Release(); + } + } + + @Override + public List getElements() { + List list = new ArrayList(); + REFIID ref = new REFIID(IIterableConcept.IID_IITERABLE_CONCEPT); + IterableConcept concept = (IterableConcept) this.getConcept(ref); + if (concept == null) { + return list; + } + + ModelIterator iterator = concept.getIterator(this); + //long dim = concept.getDefaultIndexDimensionality(this); + if (iterator != null) { + ModelObject next; + int i = 0; + while ((next = iterator.getNext(1)) != null) { + ModelObject index = iterator.getIndexers(); + if (index != null) { + next.setIndexer(index); + } + else { + next.setSearchKey(Integer.toHexString(i)); + } + list.add(next); + i++; + } + } + return list; + } + + @Override + public ModelObject getChild(DataModelManager1 manager, VARIANT v) { + REFIID ref = new REFIID(IIndexableConcept.IID_IINDEXABLE_CONCEPT); + IndexableConcept indexable = (IndexableConcept) this.getConcept(ref); + if (indexable == null) { + return null; + } + long dimensionality = indexable.getDimensionality(this); + Pointer[] ppIndexers = new Pointer[(int) dimensionality]; + VARIANT.ByReference vbr = new VARIANT.ByReference(v); + ModelObject mo = manager.createIntrinsicObject(ModelObjectKind.OBJECT_INTRINSIC, vbr); + ppIndexers[0] = mo.getPointer(); + return indexable.getAt(this, dimensionality, ppIndexers); + } + + @Override + public void switchTo(DataModelManager1 manager, VARIANT v) { + Pointer[] args = new Pointer[1]; + VARIANT.ByReference vbr = new VARIANT.ByReference(v); + ModelObject mo = manager.createIntrinsicObject(ModelObjectKind.OBJECT_INTRINSIC, vbr); + args[0] = mo.getPointer(); + ModelMethod f = getMethod("SwitchTo"); + f.call(this, 1, args); + } + + @Override + public Object getValue() { + Object val = this.getIntrinsicValue(); + if (val instanceof SHORT) { + return ((SHORT) val).shortValue(); + } + if (val instanceof USHORT) { + return ((USHORT) val).shortValue(); + } + if (val instanceof LONG) { + return ((LONG) val).intValue(); + } + if (val instanceof ULONG) { + return ((ULONG) val).intValue(); + } + if (val instanceof ULONGLONG) { + return ((ULONGLONG) val).longValue(); + } + return val; + } + + @Override + public String getValueString() { + Object val = this.getIntrinsicValue(); + if (val instanceof SHORT) { + return Integer.toHexString(((SHORT) val).shortValue()); + } + if (val instanceof USHORT) { + return Integer.toHexString(((USHORT) val).shortValue()); + } + if (val instanceof LONG) { + return Integer.toHexString(((LONG) val).intValue()); + } + if (val instanceof ULONG) { + return Integer.toHexString(((ULONG) val).intValue()); + } + if (val instanceof ULONGLONG) { + return Long.toHexString(((ULONGLONG) val).longValue()); + } + return val == null ? "" : val.toString(); + } + + @Override + public String toString() { + REFIID ref = new REFIID(IStringDisplayableConcept.IID_ISTRING_DISPLAYABLE_CONCEPT); + StringDisplayableConcept displayable = (StringDisplayableConcept) this.getConcept(ref); + if (displayable == null) { + return super.toString(); + } + return displayable.toDisplayString(this, null); + } + + @Override + public synchronized Map getKeyValueMap() { + TreeMap map = new TreeMap(); + String kstr; + KeyEnumerator enumerator = this.enumerateKeys(); + while ((kstr = enumerator.getNext()) != null) { + ModelObject value = this.getKeyValue(kstr); + //ModelObject v2 = enumerator.getValue(); + //if (!v2.equals(value)) { + // System.err.println("getKVMap: "+kstr+":"+this.getSearchKey()); + //} + if (value != null) { + value.setSearchKey(kstr); + //System.err.println("kv:" + kstr + ":" + value); + map.put(kstr, value); + } + } + return map; + } + + @Override + public synchronized Map getRawValueMap() { + TreeMap map = new TreeMap(); + TypeKind typeKind = getTypeKind(); + if (typeKind == null) { + return map; + } + SymbolKind kind = null; + switch (typeKind) { + case TYPE_UDT: + kind = SymbolKind.SYMBOL_FIELD; + break; + case TYPE_POINTER: + kind = SymbolKind.SYMBOL_BASE_CLASS; + try { + ModelObject dereference = this.dereference(); + return dereference.getRawValueMap(); + } + catch (Exception e) { + kind = null; + break; + } + case TYPE_INTRINSIC: + case TYPE_ARRAY: + break; + default: + System.err.println(this.getSearchKey() + ":" + typeKind); + break; + } + + if (kind == null) { + return map; + } + String kstr; + RawEnumerator enumerator = this.enumerateRawValues(kind.ordinal(), 0); + while ((kstr = enumerator.getNext()) != null) { + ModelObject value = enumerator.getValue(); + value.setSearchKey(kstr); + map.put(kstr, value); + } + return map; + } + + @Override + public TypeKind getTypeKind() { + ModelObjectKind modelKind = getKind(); + TypeKind typeKind = null; + if (modelKind.equals(ModelObjectKind.OBJECT_TARGET_OBJECT)) { + DebugHostType1 targetInfo = getTargetInfo(); + typeKind = targetInfo.getTypeKind(); + } + if (modelKind.equals(ModelObjectKind.OBJECT_INTRINSIC)) { + DebugHostType1 typeInfo = getTypeInfo(); + if (typeInfo != null) { + typeKind = typeInfo.getTypeKind(); + } + } + return typeKind; + } + + @Override + public ModelMethod getMethod(String name) { + ModelObject m = getKeyValue(name); + if (m == null || !m.getKind().equals(ModelObjectKind.OBJECT_METHOD)) { + return null; + } + Unknown unk = (Unknown) m.getIntrinsicValue(); + return ModelMethodInternal.tryPreferredInterfaces(unk::QueryInterface); + } + + @Override + public ModelObject getIndexer() { + return indexer; + } + + @Override + public void setIndexer(ModelObject indexer) { + this.indexer = indexer; + String str = "0x" + indexer.getValueString(); + if (!str.equals("")) { + key = str; + } + } + + @Override + public String getSearchKey() { + if (key == null) { + throw new RuntimeException("null key for " + this); + } + return key; + } + + @Override + public void setSearchKey(String key) { + this.key = key; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelObjectInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelObjectInternal.java new file mode 100644 index 0000000000..a62119f6c4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelObjectInternal.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.main.IModelObject; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelObject; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface ModelObjectInternal extends ModelObject { + Map CACHE = new WeakValueHashMap<>(); + + static ModelObjectInternal instanceFor(WrapIModelObject data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, ModelObjectImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IModelObject.IID_IMODEL_OBJECT), WrapIModelObject.class) // + .build(); + + static ModelObjectInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(ModelObjectInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelPropertyAccessorImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelPropertyAccessorImpl.java new file mode 100644 index 0000000000..796fd186b1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelPropertyAccessorImpl.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.COMUtilsExtra; +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.main.IModelPropertyAccessor; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelObject; + +public class ModelPropertyAccessorImpl implements ModelPropertyAccessorInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IModelPropertyAccessor jnaData; + + public ModelPropertyAccessorImpl(IModelPropertyAccessor jnaData) { + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public ModelObject getValue(String key, ModelObject contextObject) { + Pointer pContextObject = contextObject.getPointer(); + PointerByReference ppValue = new PointerByReference(); + HRESULT hr = jnaData.GetValue(new WString(key), pContextObject, ppValue); + if (hr.equals(COMUtilsExtra.E_INVALID_PARAM)) { + System.err.println(key + " invalid param "); + return null; + } + COMUtils.checkRC(hr); + + WrapIModelObject wrap = new WrapIModelObject(ppValue.getValue()); + try { + return ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + } + + @Override + public void setValue(String key, ModelObject contextObject, ModelObject value) { + Pointer pContextObject = contextObject.getPointer(); + Pointer pValue = value.getPointer(); + COMUtils.checkRC(jnaData.SetValue(new WString(key), pContextObject, pValue)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelPropertyAccessorInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelPropertyAccessorInternal.java new file mode 100644 index 0000000000..8187cee6b9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/ModelPropertyAccessorInternal.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.main.ModelPropertyAccessor; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.main.IModelPropertyAccessor; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelPropertyAccessor; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface ModelPropertyAccessorInternal extends ModelPropertyAccessor { + Map CACHE = new WeakValueHashMap<>(); + + static ModelPropertyAccessorInternal instanceFor(WrapIModelPropertyAccessor data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, ModelPropertyAccessorImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IModelPropertyAccessor.IID_IMODEL_PROPERTY_ACCESSOR), + WrapIModelPropertyAccessor.class) // + .build(); + + static ModelPropertyAccessorInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(ModelPropertyAccessorInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/RawEnumeratorImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/RawEnumeratorImpl.java new file mode 100644 index 0000000000..1f3fc1366f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/RawEnumeratorImpl.java @@ -0,0 +1,96 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.dbgmodel.COMUtilsExtra; +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.DbgModel.OpaqueCleanable; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.impl.dbgmodel.UnknownExImpl; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ModelObjectKind; +import agent.dbgmodel.jna.dbgmodel.main.IRawEnumerator; +import agent.dbgmodel.jna.dbgmodel.main.WrapIModelObject; + +public class RawEnumeratorImpl extends UnknownExImpl implements RawEnumeratorInternal { + @SuppressWarnings("unused") + private final OpaqueCleanable cleanable; + private final IRawEnumerator jnaData; + + private ULONG kind; + private ModelObject value; + + public RawEnumeratorImpl(IRawEnumerator jnaData) { + super(jnaData); + this.cleanable = DbgModel.releaseWhenPhantom(this, jnaData); + this.jnaData = jnaData; + } + + @Override + public Pointer getPointer() { + return jnaData.getPointer(); + } + + @Override + public void reset() { + COMUtils.checkRC(jnaData.Reset()); + } + + @Override + public String getNext() { + BSTRByReference bref = new BSTRByReference(); + ULONGByReference ulKind = new ULONGByReference(); + PointerByReference ppValue = new PointerByReference(); + HRESULT hr = jnaData.GetNext(bref, ulKind, ppValue); + if (hr.equals(COMUtilsExtra.E_BOUNDS)) { + return null; + } + COMUtils.checkRC(hr); + + kind = ulKind.getValue(); + + WrapIModelObject wrap = new WrapIModelObject(ppValue.getValue()); + try { + value = ModelObjectInternal.tryPreferredInterfaces(wrap::QueryInterface); + } + finally { + wrap.Release(); + } + BSTR bstr = bref.getValue(); + String key = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + return key; + } + + @Override + public ModelObjectKind getKind() { + return ModelObjectKind.values()[kind.intValue()]; + } + + @Override + public ModelObject getValue() { + return value; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/RawEnumeratorInternal.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/RawEnumeratorInternal.java new file mode 100644 index 0000000000..351d11d8fc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/impl/dbgmodel/main/RawEnumeratorInternal.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.impl.dbgmodel.main; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; + +import agent.dbgmodel.dbgmodel.main.RawEnumerator; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil; +import agent.dbgmodel.impl.dbgmodel.DbgModelUtil.InterfaceSupplier; +import agent.dbgmodel.jna.dbgmodel.main.IRawEnumerator; +import agent.dbgmodel.jna.dbgmodel.main.WrapIRawEnumerator; +import ghidra.util.datastruct.WeakValueHashMap; + +public interface RawEnumeratorInternal extends RawEnumerator { + Map CACHE = new WeakValueHashMap<>(); + + static RawEnumeratorInternal instanceFor(WrapIRawEnumerator data) { + return DbgModelUtil.lazyWeakCache(CACHE, data, RawEnumeratorImpl::new); + } + + ImmutableMap.Builder> PREFERRED_DATA_SPACES_IIDS_BUILDER = + ImmutableMap.builder(); + Map> PREFERRED_DATA_SPACES_IIDS = + PREFERRED_DATA_SPACES_IIDS_BUILDER // + .put(new REFIID(IRawEnumerator.IID_IRAW_ENUMERATOR), WrapIRawEnumerator.class) // + .build(); + + static RawEnumeratorInternal tryPreferredInterfaces(InterfaceSupplier supplier) { + return DbgModelUtil.tryPreferredInterfaces(RawEnumeratorInternal.class, + PREFERRED_DATA_SPACES_IIDS, supplier); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgApplyMethodsCommand.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgApplyMethodsCommand.java new file mode 100644 index 0000000000..65d26f9c97 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgApplyMethodsCommand.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.cmd; + +import java.util.List; + +import agent.dbgeng.manager.cmd.AbstractDbgCommand; +import agent.dbgeng.manager.cmd.DbgPendingCommand; +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.gadp.impl.WrappedDbgModel; +import agent.dbgmodel.manager.DbgManager2Impl; +import agent.dbgmodel.model.impl.DbgModel2TargetObjectImpl; +import agent.dbgmodel.model.impl.DelegateDbgModel2TargetObject; +import ghidra.dbg.target.TargetObject; + +public class DbgApplyMethodsCommand extends AbstractDbgCommand { + + private WrappedDbgModel access; + private List path; + private DbgModel2TargetObjectImpl targetObject; + private DbgModelTargetObject result; + + public DbgApplyMethodsCommand(DbgManager2Impl manager, List path, + DbgModel2TargetObjectImpl targetObject) { + super(manager); + this.access = manager.getAccess(); + this.path = path; + this.targetObject = targetObject; + } + + @Override + public TargetObject complete(DbgPendingCommand pending) { + return result; + } + + @Override + public void invoke() { + ModelObject obj = access.getMethod(path); + obj.setSearchKey(path.get(path.size() - 1)); + result = DelegateDbgModel2TargetObject.makeProxy(targetObject.getModel(), targetObject, + obj.getSearchKey(), obj); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgGetRegisterMapCommand.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgGetRegisterMapCommand.java new file mode 100644 index 0000000000..485b955d6f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgGetRegisterMapCommand.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.cmd; + +import java.util.*; + +import agent.dbgeng.dbgeng.DebugValue; +import agent.dbgeng.manager.cmd.AbstractDbgCommand; +import agent.dbgeng.manager.cmd.DbgPendingCommand; +import agent.dbgeng.manager.impl.DbgRegister; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.gadp.impl.WrappedDbgModel; +import agent.dbgmodel.manager.DbgManager2Impl; + +public class DbgGetRegisterMapCommand extends AbstractDbgCommand> { + + private Map map = new HashMap<>(); + + private WrappedDbgModel access; + private List path; + + public DbgGetRegisterMapCommand(DbgManager2Impl manager, List path) { + super(manager); + this.access = manager.getAccess(); + this.path = path; + } + + @Override + public Map complete(DbgPendingCommand pending) { + return map; + } + + @Override + public void invoke() { + List npath = new ArrayList(); + npath.add("Debugger"); + npath.addAll(path); + Map attributes = access.getAttributes(npath); + int i = 0; + for (String key : attributes.keySet()) { + ModelObject modelObject = attributes.get(key); + DebugValue debugValue = access.getDebugValue(modelObject); + if (debugValue != null) { + DbgRegister register = + new DbgRegister(key, i++, debugValue.getValueType().byteLength); + map.put(key, register); + } + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgListAttributesCommand.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgListAttributesCommand.java new file mode 100644 index 0000000000..2741887e15 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgListAttributesCommand.java @@ -0,0 +1,76 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.cmd; + +import java.util.*; + +import agent.dbgeng.manager.cmd.AbstractDbgCommand; +import agent.dbgeng.manager.cmd.DbgPendingCommand; +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.gadp.impl.WrappedDbgModel; +import agent.dbgmodel.manager.DbgManager2Impl; +import agent.dbgmodel.model.impl.DbgModel2TargetObjectImpl; +import agent.dbgmodel.model.impl.DelegateDbgModel2TargetObject; +import ghidra.dbg.util.PathUtils.TargetObjectKeyComparator; + +public class DbgListAttributesCommand extends AbstractDbgCommand> { + + private Map updatedAttributes; + + private WrappedDbgModel access; + private List path; + private DbgModel2TargetObjectImpl targetObject; + + public DbgListAttributesCommand(DbgManager2Impl manager, List path, + DbgModel2TargetObjectImpl targetObject) { + super(manager); + this.access = manager.getAccess(); + this.path = path; + this.targetObject = targetObject; + } + + @Override + public Map complete(DbgPendingCommand pending) { + return updatedAttributes; + } + + @Override + public void invoke() { + updatedAttributes = new TreeMap<>(TargetObjectKeyComparator.ATTRIBUTE); + Map map = access.getAttributes(path); + Map existingAttributes = targetObject.getCachedAttributes(); + for (String key : map.keySet()) { + DbgModelTargetObject proxyAttribute; + ModelObject obj = map.get(key); + Object object = existingAttributes.get(key); + if (object != null && (object instanceof DbgModelTargetObject)) { + proxyAttribute = (DbgModelTargetObject) object; + DelegateDbgModel2TargetObject delegate = + DelegateDbgModel2TargetObject.getDelegate(proxyAttribute); + delegate.setModelObject(obj); + updatedAttributes.put(key, proxyAttribute); + } + else { + String atKey = obj.getSearchKey(); + proxyAttribute = DelegateDbgModel2TargetObject.makeProxy(targetObject.getModel(), + targetObject, atKey, obj); + updatedAttributes.put(key, proxyAttribute); + } + } + updatedAttributes.putAll(targetObject.getIntrinsics()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgListElementsCommand.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgListElementsCommand.java new file mode 100644 index 0000000000..bb6561e6c0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/cmd/DbgListElementsCommand.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.cmd; + +import java.util.*; + +import agent.dbgeng.manager.cmd.AbstractDbgCommand; +import agent.dbgeng.manager.cmd.DbgPendingCommand; +import agent.dbgeng.model.iface2.DbgModelTargetObject; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.gadp.impl.WrappedDbgModel; +import agent.dbgmodel.manager.DbgManager2Impl; +import agent.dbgmodel.model.impl.DbgModel2TargetObjectImpl; +import agent.dbgmodel.model.impl.DelegateDbgModel2TargetObject; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.target.TargetObject; + +public class DbgListElementsCommand extends AbstractDbgCommand> { + + private List updatedElements; + + private WrappedDbgModel access; + private List path; + private DbgModel2TargetObjectImpl targetObject; + + public DbgListElementsCommand(DbgManager2Impl manager, List path, + DbgModel2TargetObjectImpl targetObject) { + super(manager); + this.access = manager.getAccess(); + this.path = path; + this.targetObject = targetObject; + } + + @Override + public List complete(DbgPendingCommand pending) { + return updatedElements; + } + + @Override + public void invoke() { + updatedElements = new ArrayList<>(); + List list = access.getElements(path); + Map existingElements = targetObject.getCachedElements(); + for (ModelObject obj : list) { + DbgModelTargetObject proxyElement; + if (existingElements.containsKey(obj.getSearchKey())) { + proxyElement = (DbgModelTargetObject) existingElements.get(obj.getSearchKey()); + DelegateDbgModel2TargetObject delegate = + DelegateDbgModel2TargetObject.getDelegate(proxyElement); + delegate.setModelObject(obj); + } + else { + String elKey = DbgModel2TargetObjectImpl.keyObject(obj); + proxyElement = DelegateDbgModel2TargetObject.makeProxy(targetObject.getModel(), + targetObject, elKey, obj); + } + updatedElements.add(proxyElement); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/DbgModelNative.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/DbgModelNative.java new file mode 100644 index 0000000000..74626d1d69 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/DbgModelNative.java @@ -0,0 +1,183 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel; + +import java.util.List; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.DWORD; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.win32.StdCallLibrary; + +public interface DbgModelNative extends StdCallLibrary { + DbgModelNative INSTANCE = Native.load("dbgmodel.dll", DbgModelNative.class); + + HRESULT DebugConnect(String RemoteOptions, REFIID InterfaceId, PointerByReference Interface); + + HRESULT DebugConnectWide(WString RemoteOptions, REFIID InterfaceId, + PointerByReference Interface); + + HRESULT DebugCreate(REFIID InterfaceId, PointerByReference Interface); + + HRESULT DebugCreateEx(REFIID InterfaceId, DWORD DbgEngOptions, PointerByReference Interface); + + public static enum ModelObjectKind { + OBJECT_PROPERTY_ACCESSOR, + OBJECT_CONTEXT, + OBJECT_TARGET_OBJECT, + OBJECT_TARGET_OBJECT_REFERENCE, + OBJECT_SYNTHETIC, + OBJECT_NO_VALUE, + OBJECT_ERROR, + OBJECT_INTRINSIC, + OBJECT_METHOD, + OBJECT_KEY_REFERENCE; + } + + public static enum SymbolKind { + SYMBOL, + SYMBOL_MODULE, + SYMBOL_TYPE, + SYMBOL_FIELD, + SYMBOL_CONSTANT, + SYMBOL_DATA, + SYMBOL_BASE_CLASS, + SYMBOL_PUBLIC, + SYMBOL_FUNCTION; + } + + public static enum TypeKind { + TYPE_UDT, + TYPE_POINTER, + TYPE_MEMBER_POINTER, + TYPE_ARRAY, + TYPE_FUNCTION, + TYPE_TYPEDEF, + TYPE_ENUM, + TYPE_INTRINSIC; + } + + public static enum IntrinsicKind { + INTRINSIC_VOID, + INTRINSIC_BOOL, + INTRINSIC_CHAR, + INTRINSIC_WCHAR, + INTRINSIC_INT, + INTRINSIC_UINT, + INTRINSIC_LONG, + INTRINSIC_ULONG, + INTRINSIC_FLOAT, + INTRINSIC_HRESULT, + INTRINSIC_CHAR16, + INTRINSIC_CHAR32; + } + + public static enum PointerKind { + POINTER_STANDARD, POINTER_REFERENCE, POINTER_VALUE_REFERENCE, POINTER_CX_HAT; + } + + public static enum CallingConventionKind { + CALLING_CONVENTION_UNKNOWN, + CALLING_CONVENTION_CDECL, + CALLING_CONVENTION_FASTCALL, + CALLING_CONVENTION_STDCALL, + CALLING_CONVENTION_SYSCALL, + CALLING_CONVENTION_THISCALL; + } + + public static enum LocationKind { + LOCATION_MEMBER, LOCATION_STATIC, LOCATION_CONSTANT, LOCATION_NONE; + } + + public static enum PreferredFormat { + FORMAT_NONE, + FORMAT_SINGLE_CHARACTER, + FORMAT_QUOTED_STRING, + FORMAT_STRING, + FORMAT_QUOTED_UNICODE_STRING, + FORMAT_UNICODE_STRING, + FORMAT_QUOTED_UTF8_STRING, + FORMAT_UTF8_STRING, + FORMAT_BSTR_STRING, + FORMAT_QUOTED_HSTRING, + FORMAT_HSTRING, + FORMAT_RAW, + FORMAT_ENUM_NAME_ONLY, + FORMAT_ESCAPED_STRING_WITH_QUOTE, + FORMAT_UTF32_STRING, + FORMAT_QUOTED_UTF32_STRING; + } + + public static class LOCATION extends Structure { + public static class ByReference extends LOCATION + implements Structure.ByReference { + } + + public LOCATION() { + this.HostDefined = new ULONGLONG(0); + this.Offset = new ULONGLONG(0); + } + + public LOCATION(ULONGLONG virtualAddress) { + this.HostDefined = new ULONGLONG(0); + this.Offset = virtualAddress; + } + + public LOCATION(ByReference pLocation) { + this.HostDefined = pLocation.HostDefined; + this.Offset = pLocation.Offset; + } + + public static final List FIELDS = createFieldsOrder("HostDefined", "Offset"); + + public ULONGLONG HostDefined; + public ULONGLONG Offset; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class ARRAY_DIMENSION extends Structure { + public static class ByReference extends ARRAY_DIMENSION + implements Structure.ByReference { + + public ByReference() { + super(); + // TODO Auto-generated constructor stub + } + } + + public ARRAY_DIMENSION() { + // TODO Auto-generated constructor stub + } + + public ARRAY_DIMENSION(ByReference pLocation) { + // TODO Auto-generated constructor stub + } + + @Override + protected List getFieldOrder() { + // TODO Auto-generated method stub + return null; + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/IDebugClientEx.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/IDebugClientEx.java new file mode 100644 index 0000000000..d02c985998 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/IDebugClientEx.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel; + +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgeng.jna.dbgeng.client.IDebugClient; + +public interface IDebugClientEx extends IDebugClient { + + HRESULT As(PointerByReference hdma); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/IUnknownEx.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/IUnknownEx.java new file mode 100644 index 0000000000..3f97090c93 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/IUnknownEx.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.COM.IUnknown; + +public interface IUnknownEx extends IUnknown { + + public Pointer getPointer(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/UnknownWithUtils.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/UnknownWithUtils.java new file mode 100644 index 0000000000..2a15b93b78 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/UnknownWithUtils.java @@ -0,0 +1,303 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel; + +import java.io.*; +import java.util.*; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.Unknown; +import com.sun.jna.ptr.PointerByReference; + +import ghidra.util.Msg; + +public class UnknownWithUtils extends Unknown { + public static void pause() { + try { + new BufferedReader(new InputStreamReader(System.in)).readLine(); + } + catch (IOException e) { + throw new AssertionError(e); + } + } + + public static void error(String message) { + Msg.error(UnknownWithUtils.class, message, new Throwable()); + //pause(); + } + + public interface RefAnalyzer { + default void observeCall(Pointer ptr, UnknownWithUtils wrapper, String name) { + } + + default void observeCall(UnknownWithUtils wrapper, String name) { + } + + default void observedAddRefViaResult(Pointer ptr, UnknownWithUtils wrapper) { + } + + default void observedQueryInterface(REFIID riid, PointerByReference ppvObject, HRESULT hr, + UnknownWithUtils wrapper) { + } + + default void observedAddRef(int count, UnknownWithUtils wrapper) { + } + + default void observedRelease(int count, UnknownWithUtils wrapper) { + } + + default void checkLeaks() { + } + } + + public static class RefAnalyzerEntry { + public final Pointer ptr; + + public long myCount = 0; // presume 1 elsewhere + public Thread thread; + + public RefAnalyzerEntry(Pointer ptr) { + this.ptr = ptr; + } + + public void verifyValid(long actual, UnknownWithUtils wrapper, String name) { + if (this.myCount < 0 || actual < 0) { + error( + "COM mine or actual ref-count below 0 in " + name + + " wrapper=" + wrapper + + ", ptr=" + ptr + + ", myCount=" + this.myCount + + ", actual=" + actual); + } + } + + public int verifyCount(UnknownWithUtils wrapper, String name) { + int actual = wrapper.getRefCount(); + verifyValid(actual, wrapper, name); + return actual; + } + + public void verifyThread() { + Thread current = Thread.currentThread(); + if (this.thread == null) { + this.thread = current; + } + if (this.thread != current) { + // TODO: This could actually cause a problem + // But right now, it's just distracting + if (current.getName().contains("Cleaner")) { + return; + } + //error("COM use by distinct threads ptr=" + ptr + + // ", first=" + thread + + // ", current=" + current); + } + } + } + + public static class DisabledRefAnalyzer implements RefAnalyzer { + } + + public static class EnabledRefAnalyzer implements RefAnalyzer { + public static final Map REFS = new HashMap<>(); + public static final List QIS = new ArrayList<>(); + + protected RefAnalyzerEntry getEntry(Pointer ptr) { + synchronized (REFS) { + return REFS.get(Pointer.nativeValue(ptr)); + } + } + + protected RefAnalyzerEntry getEntryOrCreate(Pointer ptr) { + synchronized (REFS) { + return REFS.computeIfAbsent(Pointer.nativeValue(ptr), + addr -> new RefAnalyzerEntry(ptr)); + } + } + + protected RefAnalyzerEntry removeEntry(Pointer ptr) { + synchronized (REFS) { + return REFS.remove(Pointer.nativeValue(ptr)); + } + } + + protected void expectWrapper(Pointer ptr) { + synchronized (REFS) { + QIS.add(Pointer.nativeValue(ptr)); + } + } + + protected void unexpectWrapper(Pointer ptr) { + synchronized (REFS) { + QIS.remove(Pointer.nativeValue(ptr)); + } + } + + @Override + public void observeCall(Pointer ptr, UnknownWithUtils wrapper, String name) { + synchronized (REFS) { + RefAnalyzerEntry entry = getEntryOrCreate(ptr); + long actual = entry.verifyCount(wrapper, name); + entry.verifyValid(actual, wrapper, name); + entry.verifyThread(); + } + } + + @Override + public void observeCall(UnknownWithUtils wrapper, String name) { + observeCall(wrapper.getPointer(), wrapper, name); + } + + @Override + public void observedAddRefViaResult(Pointer ptr, UnknownWithUtils wrapper) { + Msg.debug(this, "COM Presumed AddRef: " + ptr + ", wrapper=" + wrapper); + synchronized (REFS) { + unexpectWrapper(ptr); + RefAnalyzerEntry entry = getEntryOrCreate(ptr); + Msg.debug(this, "COM count after AddRef: " + wrapper + " mine=" + entry.myCount); + entry.myCount++; + } + } + + @Override + public void observedQueryInterface(REFIID riid, PointerByReference ppvObject, HRESULT hr, + UnknownWithUtils wrapper) { + Pointer ptr = ppvObject.getValue(); + Msg.debug(this, + "COM QueryInterface: " + wrapper + "(riid->" + riid.getValue().toGuidString() + + ",ppvObject->" + ptr + ") = " + hr); + expectWrapper(ptr); + } + + @Override + public void observedAddRef(int count, UnknownWithUtils wrapper) { + Msg.debug(this, "COM AddRef: " + wrapper + "() = " + count); + Pointer ptr = wrapper.getPointer(); + synchronized (REFS) { + RefAnalyzerEntry entry = getEntry(ptr); + if (entry == null) { + error("COM AddRef on non-refed object ptr=" + ptr + ", wrapper=" + wrapper); + return; + } + entry.myCount++; + entry.verifyValid(count, wrapper, "AddRef"); + entry.verifyThread(); + } + } + + @Override + public void observedRelease(int count, UnknownWithUtils wrapper) { + Msg.debug(this, "COM Release: " + wrapper + "() = " + count); + Pointer ptr = wrapper.getPointer(); + synchronized (REFS) { + RefAnalyzerEntry entry = getEntry(ptr); + if (entry == null) { + error("COM Released on non-refed object ptr=" + ptr + ", wrapper=" + wrapper); + return; + } + entry.myCount--; + Msg.debug(this, + "COM count after Release: " + wrapper + " mine=" + entry.myCount + ", actual=" + + count); + if (entry.myCount == 0) { + removeEntry(ptr); + } + entry.verifyValid(count, wrapper, "Release"); + entry.verifyThread(); + } + } + + @Override + public void checkLeaks() { + // TODO: Can't really guarantee all GC has happened + // This will create many false positives + System.gc(); + synchronized (REFS) { + for (RefAnalyzerEntry entry : REFS.values()) { + if (entry.myCount != 0) { + Msg.warn(this, "COM potential ref leak: ptr=" + entry.ptr + ", count=" + + entry.myCount); + } + } + + for (long addr : QIS) { + Msg.error(this, "Observed QueryInterface without a wrapper: ptr=0x" + + Long.toHexString(addr)); + } + } + } + } + + public static final RefAnalyzer ANALYZER = new DisabledRefAnalyzer(); + + public static interface VTableIndex { + int getIndex(); + + public static & VTableIndex> int follow(Class prev) { + I[] all = prev.getEnumConstants(); + int start = all[0].getIndex() - all[0].ordinal(); + return all.length + start; + } + } + + public UnknownWithUtils() { + } + + public UnknownWithUtils(Pointer pvInstance) { + super(pvInstance); + // TODO: HACK? + ANALYZER.observedAddRefViaResult(pvInstance, this); + } + + protected HRESULT _invokeHR(VTableIndex idx, Object... args) { + //System.err.println(idx); + return (HRESULT) this._invokeNativeObject(idx.getIndex(), args, HRESULT.class); + } + + @Override + public HRESULT QueryInterface(REFIID riid, PointerByReference ppvObject) { + ANALYZER.observeCall(this, "QueryInterface"); + HRESULT hr = super.QueryInterface(riid, ppvObject); + ANALYZER.observedQueryInterface(riid, ppvObject, hr, this); + return hr; + } + + @Override + public int AddRef() { + int count = super.AddRef(); + ANALYZER.observedAddRef(count, this); + return count; + } + + @Override + public int Release() { + int count = super.Release(); + ANALYZER.observedRelease(count, this); + return count; + } + + public int getRefCount() { + int added = super.AddRef(); + int count = super.Release(); + if (added - 1 != count) { + Msg.warn(this, "COM ref-count impl anomaly wrapper=" + + this + ", added=" + added + ", count=" + count); + } + return count; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/WrapIUnknownEx.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/WrapIUnknownEx.java new file mode 100644 index 0000000000..475f8ad73d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/WrapIUnknownEx.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +import agent.dbgmodel.jna.dbgmodel.debughost.WrapIDebugHostType1; + +public class WrapIUnknownEx extends UnknownWithUtils implements IUnknownEx { + public static class ByReference extends WrapIDebugHostType1 implements Structure.ByReference { + } + + public WrapIUnknownEx() { + } + + public WrapIUnknownEx(Pointer pvInstance) { + super(pvInstance); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/bridge/IHostDataModelAccess.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/bridge/IHostDataModelAccess.java new file mode 100644 index 0000000000..e136c4b5cc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/bridge/IHostDataModelAccess.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.bridge; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IHostDataModelAccess extends IUnknownEx { + final IID IID_IHOST_DATA_MODEL_ACCESS = new IID("F2BCE54E-4835-4f8a-836E-7981E29904D1"); + + enum VTIndices implements VTableIndex { + GET_DATA_MODEL, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetDataModel(PointerByReference manager, PointerByReference host); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/bridge/WrapIHostDataModelAccess.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/bridge/WrapIHostDataModelAccess.java new file mode 100644 index 0000000000..96424e5187 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/bridge/WrapIHostDataModelAccess.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.bridge; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIHostDataModelAccess extends UnknownWithUtils implements IHostDataModelAccess { + public static class ByReference extends WrapIHostDataModelAccess + implements Structure.ByReference { + } + + public WrapIHostDataModelAccess() { + } + + public WrapIHostDataModelAccess(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetDataModel(PointerByReference manager, PointerByReference host) { + return _invokeHR(VTIndices.GET_DATA_MODEL, getPointer(), manager, host); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IComparableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IComparableConcept.java new file mode 100644 index 0000000000..7f2014ec63 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IComparableConcept.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IComparableConcept extends IUnknownEx { + final IID IID_ICOMPARABLE_CONCEPT = new IID("A7830646-9F0C-4a31-BA19-503F33E6C8A3"); + + enum VTIndices implements VTableIndex { + COMPARE_OBJECTS, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT CompareObjects(Pointer contextObject, Pointer otherObject, + ULONGByReference comparisonResult); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDataModelConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDataModelConcept.java new file mode 100644 index 0000000000..d98839d534 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDataModelConcept.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelConcept extends IUnknownEx { + final IID IID_IDATA_MODEL_CONCEPT = new IID("FCB98D1D-1114-4fbf-B24C-EFFCB5DEF0D3"); + + enum VTIndices implements VTableIndex { + INITIALIZE_OBJECT, // + GET_NAME, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT InitializeObject(Pointer modelObject, Pointer matchingTypeSignature, + Pointer wildcardMatches); + + HRESULT GetName(BSTRByReference modelName); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDynamicConceptProviderConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDynamicConceptProviderConcept.java new file mode 100644 index 0000000000..4f18b5e6d0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDynamicConceptProviderConcept.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDynamicConceptProviderConcept extends IUnknownEx { + final IID IID_IDYNAMIC_CONCEPT_PROVIDER_CONCEPT = + new IID("95A7F7DD-602E-483f-9D06-A15C0EE13174"); + + enum VTIndices implements VTableIndex { + GET_CONCEPT, // + SET_CONCEPT, // + NOTIFY_PARENT, // + NOTIFY_PARENT_CHANGE, // + NOTIFY_DESTRUCT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetConcept(Pointer contextObject, REFIID conceptId, PointerByReference conceptInterface, + PointerByReference conceptMetadata, BOOLByReference hasConcept); + + HRESULT SetConcept(Pointer contextObject, REFIID conceptId, Pointer conceptInterface, + Pointer conceptMetadata); + + HRESULT NotifyParent(Pointer parentModel); + + HRESULT NotifyParentChange(Pointer parentModel); + + HRESULT NotifyDestruct(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDynamicKeyProviderConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDynamicKeyProviderConcept.java new file mode 100644 index 0000000000..0e44ab9f7d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IDynamicKeyProviderConcept.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDynamicKeyProviderConcept extends IUnknownEx { + final IID IID_IDYNAMIC_KEY_PROVIDER_CONCEPT = new IID("E7983FA1-80A7-498c-988F-518DDC5D4025"); + + enum VTIndices implements VTableIndex { + GET_KEY, // + SET_KEY, // + ENUMERATE_KEYS, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetKey(Pointer contextObject, WString key, PointerByReference keyValue, + PointerByReference metadata, BOOLByReference hasKey); + + HRESULT SetKey(Pointer contextObject, WString key, Pointer keyValue, Pointer metadata); + + HRESULT EnumerateKeys(Pointer contextObject, PointerByReference ppEnumerator); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IEquatableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IEquatableConcept.java new file mode 100644 index 0000000000..cd51072b78 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IEquatableConcept.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IEquatableConcept extends IUnknownEx { + final IID IID_IEQUATABLE_CONCEPT = new IID("C52D5D3D-609D-4d5d-8A82-46B0ACDEC4F4"); + + enum VTIndices implements VTableIndex { + ARE_OBJECTS_EQUAL, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT AreObjectsEqual(Pointer contextObject, Pointer otherObject, BOOLByReference isEqual); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IIndexableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IIndexableConcept.java new file mode 100644 index 0000000000..d539aca4b7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IIndexableConcept.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IIndexableConcept extends IUnknownEx { + final IID IID_IINDEXABLE_CONCEPT = new IID("D1FAD99F-3F53-4457-850C-8051DF2D3FB5"); + + enum VTIndices implements VTableIndex { + GET_DIMENSIONALITY, // + GET_AT, // + SET_AT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetDimensionality(Pointer contextObject, ULONGLONGByReference dimensionality); + + HRESULT GetAt(Pointer contextObject, ULONGLONG indexerCount, Pointer[] indexers, + PointerByReference object, PointerByReference metadata); + + HRESULT SetAt(Pointer contextObject, ULONGLONG indexerCount, PointerByReference indexers, + Pointer value); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IIterableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IIterableConcept.java new file mode 100644 index 0000000000..aa7fd35372 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IIterableConcept.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IIterableConcept extends IUnknownEx { + final IID IID_IITERABLE_CONCEPT = new IID("F5D49D0C-0B02-4301-9C9B-B3A6037628F3"); + + enum VTIndices implements VTableIndex { + GET_DEFAULT_INDEX_DIMENSIONALITY, // + GET_ITERATOR, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetDefaultIndexDimensionality(Pointer contextObject, + ULONGLONGByReference dimensionality); + + HRESULT GetIterator(Pointer contextObject, PointerByReference iterator); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IPreferredRuntimeTypeConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IPreferredRuntimeTypeConcept.java new file mode 100644 index 0000000000..337ea28fb6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IPreferredRuntimeTypeConcept.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IPreferredRuntimeTypeConcept extends IUnknownEx { + final IID IID_IPREFERRED_RUNTIME_TYPE_CONCEPT = new IID("9D6C1D7B-A76F-4618-8068-5F76BD9A4E8A"); + + enum VTIndices implements VTableIndex { + CAST_TO_PREFERRED_RUNTIME_TYPE, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT CastToPreferredRuntimeType(Pointer contextObject, PointerByReference object); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IStringDisplayableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IStringDisplayableConcept.java new file mode 100644 index 0000000000..54020faf05 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/IStringDisplayableConcept.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IStringDisplayableConcept extends IUnknownEx { + final IID IID_ISTRING_DISPLAYABLE_CONCEPT = new IID("D28E8D70-6C00-4205-940D-501016601EA3"); + + enum VTIndices implements VTableIndex { + TO_DISPLAY_STRING, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT ToDisplayString(Pointer contextObject, Pointer metadata, BSTRByReference displayString); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIComparableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIComparableConcept.java new file mode 100644 index 0000000000..e35df8cab3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIComparableConcept.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIComparableConcept extends UnknownWithUtils implements IComparableConcept { + public static class ByReference extends WrapIComparableConcept + implements Structure.ByReference { + } + + public WrapIComparableConcept() { + } + + public WrapIComparableConcept(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT CompareObjects(Pointer contextObject, Pointer otherObject, + ULONGByReference comparisonResult) { + return _invokeHR(VTIndices.COMPARE_OBJECTS, getPointer(), contextObject, otherObject, + comparisonResult); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDataModelConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDataModelConcept.java new file mode 100644 index 0000000000..d1d3a3b6a6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDataModelConcept.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelConcept extends UnknownWithUtils implements IDataModelConcept { + public static class ByReference extends WrapIDataModelConcept implements Structure.ByReference { + } + + public WrapIDataModelConcept() { + } + + public WrapIDataModelConcept(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT InitializeObject(Pointer modelObject, Pointer matchingTypeSignature, + Pointer wildcardMatches) { + return _invokeHR(VTIndices.INITIALIZE_OBJECT, getPointer(), modelObject, + matchingTypeSignature, wildcardMatches); + } + + @Override + public HRESULT GetName(BSTRByReference modelName) { + return _invokeHR(VTIndices.GET_NAME, getPointer(), modelName); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDynamicConceptProviderConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDynamicConceptProviderConcept.java new file mode 100644 index 0000000000..88e7ad8752 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDynamicConceptProviderConcept.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDynamicConceptProviderConcept extends UnknownWithUtils + implements IDynamicConceptProviderConcept { + public static class ByReference extends WrapIDynamicConceptProviderConcept + implements Structure.ByReference { + } + + public WrapIDynamicConceptProviderConcept() { + } + + public WrapIDynamicConceptProviderConcept(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetConcept(Pointer contextObject, REFIID conceptId, + PointerByReference conceptInterface, + PointerByReference conceptMetadata, BOOLByReference hasConcept) { + return _invokeHR(VTIndices.GET_CONCEPT, getPointer(), contextObject, conceptId, + conceptInterface, conceptMetadata, hasConcept); + } + + @Override + public HRESULT SetConcept(Pointer contextObject, REFIID conceptId, Pointer conceptInterface, + Pointer conceptMetadata) { + return _invokeHR(VTIndices.SET_CONCEPT, getPointer(), contextObject, conceptId, + conceptInterface, conceptMetadata); + } + + @Override + public HRESULT NotifyParent(Pointer parentModel) { + return _invokeHR(VTIndices.NOTIFY_PARENT, getPointer(), parentModel); + } + + @Override + public HRESULT NotifyParentChange(Pointer parentModel) { + return _invokeHR(VTIndices.NOTIFY_PARENT_CHANGE, getPointer(), parentModel); + } + + @Override + public HRESULT NotifyDestruct() { + return _invokeHR(VTIndices.NOTIFY_DESTRUCT, getPointer()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDynamicKeyProviderConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDynamicKeyProviderConcept.java new file mode 100644 index 0000000000..b8c3635294 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIDynamicKeyProviderConcept.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDynamicKeyProviderConcept extends UnknownWithUtils + implements IDynamicKeyProviderConcept { + public static class ByReference extends WrapIDynamicKeyProviderConcept + implements Structure.ByReference { + } + + public WrapIDynamicKeyProviderConcept() { + } + + public WrapIDynamicKeyProviderConcept(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetKey(Pointer contextObject, WString key, PointerByReference keyValue, + PointerByReference metadata, BOOLByReference hasKey) { + return _invokeHR(VTIndices.GET_KEY, getPointer(), contextObject, key, keyValue, metadata, + hasKey); + } + + @Override + public HRESULT SetKey(Pointer contextObject, WString key, Pointer keyValue, Pointer metadata) { + return _invokeHR(VTIndices.SET_KEY, getPointer(), contextObject, key, keyValue, metadata); + } + + @Override + public HRESULT EnumerateKeys(Pointer contextObject, PointerByReference ppEnumerator) { + return _invokeHR(VTIndices.ENUMERATE_KEYS, getPointer(), contextObject, ppEnumerator); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIEquatableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIEquatableConcept.java new file mode 100644 index 0000000000..48db71b532 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIEquatableConcept.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIEquatableConcept extends UnknownWithUtils implements IEquatableConcept { + public static class ByReference extends WrapIEquatableConcept implements Structure.ByReference { + } + + public WrapIEquatableConcept() { + } + + public WrapIEquatableConcept(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT AreObjectsEqual(Pointer contextObject, Pointer otherObject, + BOOLByReference isEqual) { + return _invokeHR(VTIndices.ARE_OBJECTS_EQUAL, getPointer(), contextObject, otherObject, + isEqual); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIIndexableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIIndexableConcept.java new file mode 100644 index 0000000000..5187531058 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIIndexableConcept.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIIndexableConcept extends UnknownWithUtils implements IIndexableConcept { + public static class ByReference extends WrapIIndexableConcept implements Structure.ByReference { + } + + public WrapIIndexableConcept() { + } + + public WrapIIndexableConcept(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetDimensionality(Pointer contextObject, ULONGLONGByReference dimensionality) { + return _invokeHR(VTIndices.GET_DIMENSIONALITY, getPointer(), contextObject, dimensionality); + } + + @Override + public HRESULT GetAt(Pointer contextObject, ULONGLONG indexerCount, Pointer[] indexers, + PointerByReference object, PointerByReference metadata) { + return _invokeHR(VTIndices.GET_AT, getPointer(), contextObject, indexerCount, indexers, + object, metadata); + } + + @Override + public HRESULT SetAt(Pointer contextObject, ULONGLONG indexerCount, PointerByReference indexers, + Pointer value) { + return _invokeHR(VTIndices.SET_AT, getPointer(), contextObject, indexerCount, indexers, + value); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIIterableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIIterableConcept.java new file mode 100644 index 0000000000..30fe618075 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIIterableConcept.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIIterableConcept extends UnknownWithUtils implements IIterableConcept { + public static class ByReference extends WrapIIterableConcept implements Structure.ByReference { + } + + public WrapIIterableConcept() { + } + + public WrapIIterableConcept(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetDefaultIndexDimensionality(Pointer contextObject, + ULONGLONGByReference dimensionality) { + return _invokeHR(VTIndices.GET_DEFAULT_INDEX_DIMENSIONALITY, getPointer(), dimensionality); + } + + @Override + public HRESULT GetIterator(Pointer contextObject, PointerByReference iterator) { + return _invokeHR(VTIndices.GET_ITERATOR, getPointer(), contextObject, iterator); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIPreferredRuntimeTypeConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIPreferredRuntimeTypeConcept.java new file mode 100644 index 0000000000..559a5a22b3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIPreferredRuntimeTypeConcept.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIPreferredRuntimeTypeConcept extends UnknownWithUtils + implements IPreferredRuntimeTypeConcept { + public static class ByReference extends WrapIPreferredRuntimeTypeConcept + implements Structure.ByReference { + } + + public WrapIPreferredRuntimeTypeConcept() { + } + + public WrapIPreferredRuntimeTypeConcept(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT CastToPreferredRuntimeType(Pointer contextObject, PointerByReference object) { + return _invokeHR(VTIndices.CAST_TO_PREFERRED_RUNTIME_TYPE, getPointer(), contextObject, + object); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIStringDisplayableConcept.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIStringDisplayableConcept.java new file mode 100644 index 0000000000..1ef2501343 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/concept/WrapIStringDisplayableConcept.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.concept; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIStringDisplayableConcept extends UnknownWithUtils + implements IStringDisplayableConcept { + public static class ByReference extends WrapIStringDisplayableConcept + implements Structure.ByReference { + } + + public WrapIStringDisplayableConcept() { + } + + public WrapIStringDisplayableConcept(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT ToDisplayString(Pointer contextObject, Pointer metadata, + BSTRByReference displayString) { + return _invokeHR(VTIndices.TO_DISPLAY_STRING, getPointer(), contextObject, metadata, + displayString); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/IDataModelManager1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/IDataModelManager1.java new file mode 100644 index 0000000000..59ca1a5f71 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/IDataModelManager1.java @@ -0,0 +1,109 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ModelObjectKind; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelManager1 extends IUnknownEx { + final IID IID_IDATA_MODEL_MANAGER = new IID("73FE19F4-A110-4500-8ED9-3C28896F508C"); + + enum VTIndices1 implements VTableIndex { + CLOSE, // + CREATE_NO_VALUE, // + CREATE_ERROR_OBJECT, // + CREATE_TYPED_OBJECT, // + CREATE_TYPED_OBJECT_REFERENCE, // + CREATE_SYNTHETIC_OBJECT, // + CREATE_DATA_MODEL_OBJECT, // + CREATE_INTRINSIC_OBJECT, // + CREATE_TYPED_INTRINSIC_OBJECT, // + GET_MODEL_FOR_TYPE_SIGNATURE, // + GET_MODEL_FOR_TYPE, // + REGISTER_MODEL_FOR_TYPE_SIGNATURE, // + UNREGISTER_MODEL_FOR_TYPE_SIGNATURE, // + REGISTER_EXTENSION_FOR_TYPE_SIGNATURE, // + UNREGISTER_EXTENSION_FOR_TYPE_SIGNATURE, // + CREATE_METADATA_STORE, // + GET_ROOT_NAMESPACE, // + REGISTER_NAMED_MODEL, // + UNREGISTER_NAMED_MODEL, // + ACQUIRE_NAMED_MODEL, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT Close(); + + HRESULT CreateNoValue(PointerByReference object); + + HRESULT CreateErrorObject(HRESULT hrError, WString pwszMessage, PointerByReference object); + + HRESULT CreateTypedObject(Pointer context, LOCATION objectLocation, Pointer objectType, + PointerByReference object); + + HRESULT CreateTypedObjectReference(Pointer context, LOCATION objectLocation, Pointer objectTye, + PointerByReference object); + + HRESULT CreateSyntheticObject(Pointer context, PointerByReference object); + + HRESULT CreateDataModelObject(Pointer dataModel, PointerByReference object); + + HRESULT CreateIntrinsicObject(ModelObjectKind objectKind, VARIANT.ByReference intrinsicData, + PointerByReference object); + + HRESULT CreateTypedIntrinsicObject(VARIANT.ByReference intrinsicData, Pointer type, + PointerByReference object); + + HRESULT GetModelForTypeSignature(Pointer typeSignature, PointerByReference dataModel); + + HRESULT GetModelForType(Pointer type, PointerByReference dataModel, + PointerByReference typeSignature, PointerByReference wildcardMatches); + + HRESULT RegisterModelForTypeSignature(Pointer typeSignature, Pointer dataModel); + + HRESULT UnregisterModelForTypeSignature(Pointer dataModel, Pointer typeSignature); + + HRESULT RegisterExtensionForTypeSignature(Pointer typeSignature, Pointer dataModel); + + HRESULT UnregisterExtensionForTypeSignature(Pointer dataModel, Pointer typeSignature); + + HRESULT CreateMetadataStore(Pointer parentStore, PointerByReference metadataStore); + + HRESULT GetRootNamespace(PointerByReference rootNamespace); + + HRESULT RegisterNamedModel(WString modelName, Pointer modelObject); + + HRESULT UnregisterNamedModel(WString modelName); + + HRESULT AcquireNamedModel(WString modelName, PointerByReference modelObject); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/IDataModelManager2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/IDataModelManager2.java new file mode 100644 index 0000000000..207b547d65 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/IDataModelManager2.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelManager2 extends IDataModelManager1 { + final IID IID_IDATA_MODEL_MANAGER2 = new IID("F412C5EA-2284-4622-A660-A697160D3312"); + + enum VTIndices2 implements VTableIndex { + ACQUIRE_SUBNAMESPACE, // + CREATE_TYPED_INTRINSIC_OBJECT_EX, // + ; + + public int start = VTableIndex.follow(VTIndices1.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT AcquireSubNamespace(WString modelName, WString subNamespaceModelName, + WString accessName, Pointer metadata, PointerByReference namespaceModelObject); + + HRESULT CreateTypedIntrinsicObjectEx(Pointer context, VARIANT.ByReference intrinsicData, + Pointer type, PointerByReference object); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/WrapIDataModelManager1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/WrapIDataModelManager1.java new file mode 100644 index 0000000000..ac15983a75 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/WrapIDataModelManager1.java @@ -0,0 +1,159 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ModelObjectKind; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelManager1 extends UnknownWithUtils implements IDataModelManager1 { + public static class ByReference extends WrapIDataModelManager1 + implements Structure.ByReference { + } + + public WrapIDataModelManager1() { + } + + public WrapIDataModelManager1(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT Close() { + return _invokeHR(VTIndices1.CLOSE, getPointer()); + } + + @Override + public HRESULT CreateNoValue(PointerByReference object) { + return _invokeHR(VTIndices1.CREATE_NO_VALUE, getPointer(), object); + } + + @Override + public HRESULT CreateErrorObject(HRESULT hrError, WString pwszMessage, + PointerByReference object) { + return _invokeHR(VTIndices1.CREATE_ERROR_OBJECT, getPointer(), hrError, pwszMessage, + object); + } + + @Override + public HRESULT CreateTypedObject(Pointer context, LOCATION objectLocation, Pointer objectTye, + PointerByReference object) { + return _invokeHR(VTIndices1.CREATE_TYPED_OBJECT, getPointer(), context, objectLocation, + objectTye, object); + } + + @Override + public HRESULT CreateTypedObjectReference(Pointer context, LOCATION objectLocation, + Pointer objectTye, + PointerByReference object) { + return _invokeHR(VTIndices1.CREATE_TYPED_OBJECT_REFERENCE, getPointer(), context, + objectLocation, objectTye, object); + } + + @Override + public HRESULT CreateSyntheticObject(Pointer context, PointerByReference object) { + return _invokeHR(VTIndices1.CREATE_SYNTHETIC_OBJECT, getPointer(), context, object); + } + + @Override + public HRESULT CreateDataModelObject(Pointer dataModel, PointerByReference object) { + return _invokeHR(VTIndices1.CREATE_DATA_MODEL_OBJECT, getPointer(), dataModel, object); + } + + @Override + public HRESULT CreateIntrinsicObject(ModelObjectKind objectKind, + VARIANT.ByReference intrinsicData, + PointerByReference object) { + return _invokeHR(VTIndices1.CREATE_INTRINSIC_OBJECT, getPointer(), objectKind.ordinal(), + intrinsicData, object); + } + + @Override + public HRESULT CreateTypedIntrinsicObject(VARIANT.ByReference intrinsicData, Pointer type, + PointerByReference object) { + return _invokeHR(VTIndices1.CREATE_TYPED_INTRINSIC_OBJECT, getPointer(), intrinsicData, + type, object); + } + + @Override + public HRESULT GetModelForTypeSignature(Pointer typeSignature, PointerByReference dataModel) { + return _invokeHR(VTIndices1.GET_MODEL_FOR_TYPE_SIGNATURE, getPointer(), typeSignature, + dataModel); + } + + @Override + public HRESULT GetModelForType(Pointer type, PointerByReference dataModel, + PointerByReference typeSignature, PointerByReference wildcardMatches) { + return _invokeHR(VTIndices1.GET_MODEL_FOR_TYPE, getPointer(), type, dataModel, + typeSignature, wildcardMatches); + } + + @Override + public HRESULT RegisterModelForTypeSignature(Pointer typeSignature, Pointer dataModel) { + return _invokeHR(VTIndices1.REGISTER_MODEL_FOR_TYPE_SIGNATURE, getPointer(), typeSignature, + dataModel); + } + + @Override + public HRESULT UnregisterModelForTypeSignature(Pointer dataModel, Pointer typeSignature) { + return _invokeHR(VTIndices1.UNREGISTER_MODEL_FOR_TYPE_SIGNATURE, getPointer(), dataModel, + typeSignature); + } + + @Override + public HRESULT RegisterExtensionForTypeSignature(Pointer typeSignature, Pointer dataModel) { + return _invokeHR(VTIndices1.REGISTER_EXTENSION_FOR_TYPE_SIGNATURE, getPointer(), + typeSignature, dataModel); + } + + @Override + public HRESULT UnregisterExtensionForTypeSignature(Pointer typeSignature, Pointer dataModel) { + return _invokeHR(VTIndices1.UNREGISTER_EXTENSION_FOR_TYPE_SIGNATURE, getPointer(), + typeSignature, dataModel); + } + + @Override + public HRESULT CreateMetadataStore(Pointer parentStore, PointerByReference metadataStore) { + return _invokeHR(VTIndices1.CREATE_METADATA_STORE, getPointer(), parentStore, + metadataStore); + } + + @Override + public HRESULT GetRootNamespace(PointerByReference rootNamespace) { + return _invokeHR(VTIndices1.GET_ROOT_NAMESPACE, getPointer(), rootNamespace); + } + + @Override + public HRESULT RegisterNamedModel(WString modelName, Pointer modelObject) { + return _invokeHR(VTIndices1.REGISTER_NAMED_MODEL, getPointer(), modelName, modelObject); + } + + @Override + public HRESULT UnregisterNamedModel(WString modelName) { + return _invokeHR(VTIndices1.UNREGISTER_NAMED_MODEL, getPointer(), modelName); + } + + @Override + public HRESULT AcquireNamedModel(WString modelName, PointerByReference modelObject) { + return _invokeHR(VTIndices1.ACQUIRE_NAMED_MODEL, getPointer(), modelName, modelObject); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/WrapIDataModelManager2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/WrapIDataModelManager2.java new file mode 100644 index 0000000000..66999cb48f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/WrapIDataModelManager2.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +public class WrapIDataModelManager2 extends WrapIDataModelManager1 implements IDataModelManager2 { + public static class ByReference extends WrapIDataModelManager2 + implements Structure.ByReference { + } + + public WrapIDataModelManager2() { + } + + public WrapIDataModelManager2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT AcquireSubNamespace(WString modelName, WString subNamespaceModelName, + WString accessName, Pointer metadata, PointerByReference namespaceModelObject) { + return _invokeHR(VTIndices2.ACQUIRE_SUBNAMESPACE, getPointer(), modelName, + subNamespaceModelName, accessName, metadata, namespaceModelObject); + } + + @Override + public HRESULT CreateTypedIntrinsicObjectEx(Pointer context, VARIANT.ByReference intrinsicData, + Pointer type, PointerByReference object) { + return _invokeHR(VTIndices2.CREATE_TYPED_INTRINSIC_OBJECT_EX, getPointer(), context, + intrinsicData, type, object); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelNameBinder.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelNameBinder.java new file mode 100644 index 0000000000..27b2dff970 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelNameBinder.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelNameBinder extends IUnknownEx { + final IID IID_IDATA_MODEL_NAME_BINDER = new IID("AF352B7B-8292-4c01-B360-2DC3696C65E7"); + + enum VTIndices implements VTableIndex { + BIND_VALUE, // + BIND_REFERENCE, // + ENUMERATE_VALUES, // + ENUMERATE_REFERENCES, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT BindValue(Pointer contextObject, WString name, PointerByReference value, + PointerByReference metadata); + + HRESULT BindReference(Pointer contextObject, WString name, PointerByReference reference, + PointerByReference metadata); + + HRESULT EnumerateValues(Pointer contextObject, PointerByReference enumerator); + + HRESULT EnumerateReferences(Pointer contextObject, PointerByReference enumerator); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScript.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScript.java new file mode 100644 index 0000000000..f86f300e16 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScript.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScript extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT = new IID("7B4D30FC-B14A-49f8-8D87-D9A1480C97F7"); + + enum VTIndices implements VTableIndex { + GET_NAME, // + RENAME, // + POPULATE, // + EXECUTE, // + UNLINK, // + IS_INVOCABLE, // + INVOKE_MAIN, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetName(BSTRByReference scriptName); + + HRESULT Rename(WString scriptName); + + HRESULT Populate(Pointer contentStream); + + HRESULT Execute(Pointer client); + + HRESULT Unlink(); + + HRESULT IsInvocable(BOOLByReference isInvocable); + + HRESULT InvokeMain(Pointer client); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptClient.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptClient.java new file mode 100644 index 0000000000..a63716f651 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptClient.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptClient extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_CLIENT = new IID("3B362B0E-89F0-46c6-A663-DFDC95194AEF"); + + enum VTIndices implements VTableIndex { + REPORT_ERROR, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT ReportError(ULONG errClass, HRESULT hrFail, WString message, ULONG line, + ULONG position); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptHostContext.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptHostContext.java new file mode 100644 index 0000000000..6a3e44e827 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptHostContext.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptHostContext extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_HOST_CONTEXT = new IID("014D366A-1F23-4981-9219-B2DB8B402054"); + + enum VTIndices implements VTableIndex { + NOTIFY_SCRIPT_CHANGE, // + GET_NAMESPACE_OBJECT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT NotifyScriptChange(Pointer script, ULONG changeKind); + + HRESULT GetNamespaceObject(PointerByReference namespaceObject); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptManager.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptManager.java new file mode 100644 index 0000000000..8fb29908e2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptManager.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptManager extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_MANAGER = new IID("6FD11E33-E5AD-410b-8011-68C6BC4BF80D"); + + enum VTIndices implements VTableIndex { + GET_DEFAULT_NAME_BINDER, // + REGISTER_SCRIPT_PROVIDER, // + UNREGISTER_SCRIPT_PROVIDER, // + FIND_PROVIDER_FOR_SCRIPT_TYPE, // + FIND_PROVIDER_FOR_SCRIPT_EXTENSION, // + ENUMERATE_SCRIPT_PROVIDERS, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetDefaultNameBinder(PointerByReference ppNameBinder); + + HRESULT RegisterScriptProvider(Pointer provider); + + HRESULT UnregisterScriptProvider(Pointer provider); + + HRESULT FindProviderForScriptType(WString scriptType, PointerByReference provider); + + HRESULT FindProviderForScriptExtension(WString scriptExternsion, PointerByReference provider); + + HRESULT EnumerateScriptProviders(PointerByReference enumerator); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptProvider.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptProvider.java new file mode 100644 index 0000000000..90e3cbc37d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptProvider.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptProvider extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_PROVIDER = new IID("513461E0-4FCA-48ce-8658-32F3E2056F3B"); + + enum VTIndices implements VTableIndex { + GET_NAME, // + GET_EXTENSION, // + CREATE_SCRIPT, // + GET_DEFAULT_TEMPLATE_CONTENT, // + ENUMERATE_TEMPLATES, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetName(BSTRByReference name); + + HRESULT GetExtension(BSTRByReference extension); + + HRESULT CreateScript(PointerByReference script); + + HRESULT GetDefaultTemplateContent(PointerByReference templateContent); + + HRESULT EnumerateTemplates(PointerByReference enumerator); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptProviderEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptProviderEnumerator.java new file mode 100644 index 0000000000..f91ee0301e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptProviderEnumerator.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptProviderEnumerator extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_PROVIDER_ENUMERATOR = + new IID("95BA00E2-704A-4fe2-A8F1-A7E7D8FB0941"); + + enum VTIndices implements VTableIndex { + RESET, // + GET_NEXT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT Reset(); + + HRESULT GetNext(PointerByReference provider); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptTemplate.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptTemplate.java new file mode 100644 index 0000000000..7a44b0dac0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptTemplate.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptTemplate extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_TEMPLATE = new IID("1303DEC4-FA3B-4F1B-9224-B953D16BABB5"); + + enum VTIndices implements VTableIndex { + GET_NAME, // + GET_DESCRIPTION, // + GET_CONTENT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetName(BSTRByReference templateName); + + HRESULT GetDescription(BSTRByReference templateDescription); + + HRESULT GetContent(PointerByReference contentStream); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptTemplateEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptTemplateEnumerator.java new file mode 100644 index 0000000000..53f17260a3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/IDataModelScriptTemplateEnumerator.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptTemplateEnumerator extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_TEMPLATE_ENUMERATOR = + new IID("69CE6AE2-2268-4e6f-B062-20CE62BFE677"); + + enum VTIndices implements VTableIndex { + RESET, // + GET_NEXT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT Reset(); + + HRESULT GetNext(PointerByReference templateContent); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelNameBinder.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelNameBinder.java new file mode 100644 index 0000000000..c57491f027 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelNameBinder.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelNameBinder extends UnknownWithUtils implements IDataModelNameBinder { + public static class ByReference extends WrapIDataModelNameBinder + implements Structure.ByReference { + } + + public WrapIDataModelNameBinder() { + } + + public WrapIDataModelNameBinder(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT BindValue(Pointer contextObject, WString name, PointerByReference value, + PointerByReference metadata) { + return _invokeHR(VTIndices.BIND_VALUE, getPointer(), contextObject, name, value, metadata); + } + + @Override + public HRESULT BindReference(Pointer contextObject, WString name, PointerByReference reference, + PointerByReference metadata) { + return _invokeHR(VTIndices.BIND_REFERENCE, getPointer(), contextObject, name, reference, + metadata); + } + + @Override + public HRESULT EnumerateValues(Pointer contextObject, PointerByReference enumerator) { + return _invokeHR(VTIndices.ENUMERATE_VALUES, getPointer(), contextObject, enumerator); + } + + @Override + public HRESULT EnumerateReferences(Pointer contextObject, PointerByReference enumerator) { + return _invokeHR(VTIndices.ENUMERATE_REFERENCES, getPointer(), contextObject, enumerator); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScript.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScript.java new file mode 100644 index 0000000000..530f2cda16 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScript.java @@ -0,0 +1,71 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScript extends UnknownWithUtils implements IDataModelScript { + public static class ByReference extends WrapIDataModelScript implements Structure.ByReference { + } + + public WrapIDataModelScript() { + } + + public WrapIDataModelScript(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetName(BSTRByReference scriptName) { + return _invokeHR(VTIndices.GET_NAME, getPointer(), scriptName); + } + + @Override + public HRESULT Rename(WString scriptName) { + return _invokeHR(VTIndices.RENAME, getPointer(), scriptName); + } + + @Override + public HRESULT Populate(Pointer contentStream) { + return _invokeHR(VTIndices.POPULATE, getPointer(), contentStream); + } + + @Override + public HRESULT Execute(Pointer client) { + return _invokeHR(VTIndices.EXECUTE, getPointer(), client); + } + + @Override + public HRESULT Unlink() { + return _invokeHR(VTIndices.UNLINK, getPointer()); + } + + @Override + public HRESULT IsInvocable(BOOLByReference isInvocable) { + return _invokeHR(VTIndices.IS_INVOCABLE, getPointer(), isInvocable); + } + + @Override + public HRESULT InvokeMain(Pointer client) { + return _invokeHR(VTIndices.INVOKE_MAIN, getPointer(), client); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptClient.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptClient.java new file mode 100644 index 0000000000..08bf64aad9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptClient.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptClient extends UnknownWithUtils implements IDataModelScriptClient { + public static class ByReference extends WrapIDataModelScriptClient + implements Structure.ByReference { + } + + public WrapIDataModelScriptClient() { + } + + public WrapIDataModelScriptClient(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT ReportError(ULONG errClass, HRESULT hrFail, WString message, ULONG line, + ULONG position) { + return _invokeHR(VTIndices.REPORT_ERROR, getPointer(), errClass, hrFail, message, line, + position); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptHostContext.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptHostContext.java new file mode 100644 index 0000000000..fa5979dc40 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptHostContext.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptHostContext extends UnknownWithUtils + implements IDataModelScriptHostContext { + public static class ByReference extends WrapIDataModelScriptHostContext + implements Structure.ByReference { + } + + public WrapIDataModelScriptHostContext() { + } + + public WrapIDataModelScriptHostContext(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT NotifyScriptChange(Pointer script, ULONG changeKind) { + return _invokeHR(VTIndices.NOTIFY_SCRIPT_CHANGE, getPointer(), script, changeKind); + } + + @Override + public HRESULT GetNamespaceObject(PointerByReference namespaceObject) { + return _invokeHR(VTIndices.GET_NAMESPACE_OBJECT, getPointer(), namespaceObject); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptManager.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptManager.java new file mode 100644 index 0000000000..dd03612bfa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptManager.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptManager extends UnknownWithUtils + implements IDataModelScriptManager { + public static class ByReference extends WrapIDataModelScriptManager + implements Structure.ByReference { + } + + public WrapIDataModelScriptManager() { + } + + public WrapIDataModelScriptManager(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetDefaultNameBinder(PointerByReference ppNameBinder) { + return _invokeHR(VTIndices.GET_DEFAULT_NAME_BINDER, getPointer(), ppNameBinder); + } + + @Override + public HRESULT RegisterScriptProvider(Pointer provider) { + return _invokeHR(VTIndices.REGISTER_SCRIPT_PROVIDER, provider); + } + + @Override + public HRESULT UnregisterScriptProvider(Pointer provider) { + return _invokeHR(VTIndices.UNREGISTER_SCRIPT_PROVIDER, getPointer(), provider); + } + + @Override + public HRESULT FindProviderForScriptType(WString scriptType, PointerByReference provider) { + return _invokeHR(VTIndices.FIND_PROVIDER_FOR_SCRIPT_TYPE, getPointer(), scriptType, + provider); + } + + @Override + public HRESULT FindProviderForScriptExtension(WString scriptExternsion, + PointerByReference provider) { + return _invokeHR(VTIndices.FIND_PROVIDER_FOR_SCRIPT_EXTENSION, getPointer(), + scriptExternsion, provider); + } + + @Override + public HRESULT EnumerateScriptProviders(PointerByReference enumerator) { + return _invokeHR(VTIndices.ENUMERATE_SCRIPT_PROVIDERS, getPointer(), enumerator); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptProvider.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptProvider.java new file mode 100644 index 0000000000..c6a97a0044 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptProvider.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptProvider extends UnknownWithUtils + implements IDataModelScriptProvider { + public static class ByReference extends WrapIDataModelScriptProvider + implements Structure.ByReference { + } + + public WrapIDataModelScriptProvider() { + } + + public WrapIDataModelScriptProvider(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetName(BSTRByReference name) { + return _invokeHR(VTIndices.GET_NAME, getPointer(), name); + } + + @Override + public HRESULT GetExtension(BSTRByReference extension) { + return _invokeHR(VTIndices.GET_EXTENSION, getPointer(), extension); + } + + @Override + public HRESULT CreateScript(PointerByReference script) { + return _invokeHR(VTIndices.CREATE_SCRIPT, getPointer(), script); + } + + @Override + public HRESULT GetDefaultTemplateContent(PointerByReference templateContent) { + return _invokeHR(VTIndices.GET_DEFAULT_TEMPLATE_CONTENT, getPointer(), templateContent); + } + + @Override + public HRESULT EnumerateTemplates(PointerByReference enumerator) { + return _invokeHR(VTIndices.ENUMERATE_TEMPLATES, getPointer(), enumerator); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptProviderEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptProviderEnumerator.java new file mode 100644 index 0000000000..c731d83a48 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptProviderEnumerator.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptProviderEnumerator extends UnknownWithUtils + implements IDataModelScriptProviderEnumerator { + public static class ByReference extends WrapIDataModelScriptProviderEnumerator + implements Structure.ByReference { + } + + public WrapIDataModelScriptProviderEnumerator() { + } + + public WrapIDataModelScriptProviderEnumerator(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT Reset() { + return _invokeHR(VTIndices.RESET, getPointer()); + } + + @Override + public HRESULT GetNext(PointerByReference provider) { + return _invokeHR(VTIndices.GET_NEXT, getPointer(), provider); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptTemplate.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptTemplate.java new file mode 100644 index 0000000000..e7fcc367f1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptTemplate.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptTemplate extends UnknownWithUtils + implements IDataModelScriptTemplate { + public static class ByReference extends WrapIDataModelScriptTemplate + implements Structure.ByReference { + } + + public WrapIDataModelScriptTemplate() { + } + + public WrapIDataModelScriptTemplate(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetName(BSTRByReference templateName) { + return _invokeHR(VTIndices.GET_NAME, getPointer(), templateName); + } + + @Override + public HRESULT GetDescription(BSTRByReference templateDescription) { + return _invokeHR(VTIndices.GET_DESCRIPTION, getPointer(), templateDescription); + } + + @Override + public HRESULT GetContent(PointerByReference contentStream) { + return _invokeHR(VTIndices.GET_CONTENT, getPointer(), contentStream); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptTemplateEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptTemplateEnumerator.java new file mode 100644 index 0000000000..57a22f615c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/WrapIDataModelScriptTemplateEnumerator.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptTemplateEnumerator extends UnknownWithUtils + implements IDataModelScriptTemplateEnumerator { + public static class ByReference extends WrapIDataModelScriptTemplateEnumerator + implements Structure.ByReference { + } + + public WrapIDataModelScriptTemplateEnumerator() { + } + + public WrapIDataModelScriptTemplateEnumerator(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT Reset() { + return _invokeHR(VTIndices.RESET, getPointer()); + } + + @Override + public HRESULT GetNext(PointerByReference templateContent) { + return _invokeHR(VTIndices.GET_NEXT, getPointer(), templateContent); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebug.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebug.java new file mode 100644 index 0000000000..8935713023 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebug.java @@ -0,0 +1,75 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptDebug extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_DEBUG = new IID("DE8E0945-9750-4471-AB76-A8F79D6EC350"); + + enum VTIndices implements VTableIndex { + GET_DEBUG_STATE, // + GET_CURRENT_POSITION, // + GET_STACK, // + SET_BREAKPOINT, // + FIND_BREAKPOINT_BY_ID, // + ENUMERATE_BREAKPOINTS, // + GET_EVENT_FILTER, // + SET_EVENT_FILTER, // + START_DEBUGGING, // + STOP_DEBUGGING, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT ScriptDebugState(); + + HRESULT GetDebugState(); + + HRESULT GetCurrentPosition(Pointer currentPosition, Pointer positionSpanEnd, + BSTRByReference lineText); + + HRESULT GetStack(PointerByReference stack); + + HRESULT SetBreakpoint(ULONG linePosition, ULONG columnPosition, PointerByReference breakpoint); + + HRESULT FindBreakpointById(ULONGLONG breakpointId, PointerByReference breakpoint); + + HRESULT EnumerateBreakpoints(PointerByReference breakpointEnum); + + HRESULT GetEventFilter(ULONG eventFilter, BOOLByReference isBreakEnabled); + + HRESULT SetEventFilter(ULONG eventFilter, BOOL isBreakEnabled); + + HRESULT StartDebugging(Pointer debugClient); + + HRESULT StopDebugging(Pointer debugClient); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebug2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebug2.java new file mode 100644 index 0000000000..688c02a8d0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebug2.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptDebug2 extends IDataModelScriptDebug { + final IID IID_IDATA_MODEL_SCRIPT_DEBUG2 = new IID("CBB10ED3-839E-426c-9243-E23535C1AE1A"); + + enum VTIndices2 implements VTableIndex { + SET_BREAKPOINT_AT_FUNCTION, // + ; + + public int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT SetBreakpointAtFunction(WString functionName, PointerByReference breakpoint); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugBreakpoint.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugBreakpoint.java new file mode 100644 index 0000000000..4a0e015cc9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugBreakpoint.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptDebugBreakpoint extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_DEBUG_BREAKPOINT = + new IID("6BB27B35-02E6-47cb-90A0-5371244032DE"); + + enum VTIndices implements VTableIndex { + GET_ID, // + IS_ENABLED, // + ENABLE, // + DISABLE, // + REMOVE, // + GET_POSITION, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetId(); + + HRESULT IsEnabled(); + + HRESULT Enable(); + + HRESULT Disable(); + + HRESULT Remove(); + + HRESULT GetPosition(Pointer position, Pointer positionSpanEnd, WString lineText); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugBreakpointEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugBreakpointEnumerator.java new file mode 100644 index 0000000000..b86662b17d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugBreakpointEnumerator.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptDebugBreakpointEnumerator extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_DEBUG_BREAKPOINT_ENUMERATOR = + new IID("39484A75-B4F3-4799-86DA-691AFA57B299"); + + enum VTIndices implements VTableIndex { + RESET, // + GET_NEXT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT Reset(); + + HRESULT GetNext(PointerByReference breakpoint); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugClient.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugClient.java new file mode 100644 index 0000000000..60c68abd75 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugClient.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptDebugClient extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_DEBUG_CLIENT = new IID("53159B6D-D4C4-471b-A863-5B110CA800CA"); + + enum VTIndices implements VTableIndex { + NOTIFY_DEBUG_EVENT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT NotifyDebugEvent(Pointer pEventInfo, Pointer pScript, Pointer pEventDataObject, + Pointer resumeEventKind); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugStack.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugStack.java new file mode 100644 index 0000000000..3cc7bf95bf --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugStack.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptDebugStack extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_DEBUG_STACK = new IID("051364DD-E449-443e-9762-FE578F4A5473"); + + enum VTIndices implements VTableIndex { + GET_FRAME_COUNT, // + GET_STACK_FRAME, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetFrameCount(); + + HRESULT GetStackFrame(ULONGLONG frameNumber, PointerByReference stackFrame); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugStackFrame.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugStackFrame.java new file mode 100644 index 0000000000..1a92943b64 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugStackFrame.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptDebugStackFrame extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_DEBUG_STACK_FRAME = + new IID("DEC6ED5E-6360-4941-AB4C-A26409DE4F82"); + + enum VTIndices implements VTableIndex { + GET_NAME, // + GET_POSITION, // + IS_TRANSITION_POINT, // + GET_TRANSITION, // + EVALUATE, // + ENUMERATE_LOCALS, // + ENUMERATE_ARGUMENTS, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetName(BSTRByReference name); + + HRESULT GetPosition(Pointer position, Pointer positionSpanEnd, BSTRByReference lineText); + + HRESULT IsTransitionPoint(BOOLByReference isTransitionPoint); + + HRESULT GetTransition(PointerByReference transitionScript, + BOOLByReference isTransitionContiguous); + + HRESULT Evaluate(WString pwszExpression, PointerByReference ppResult); + + HRESULT EnumerateLocals(PointerByReference variablesEnum); + + HRESULT EnumerateArguments(PointerByReference variablesEnum); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugVariableSetEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugVariableSetEnumerator.java new file mode 100644 index 0000000000..e006bffb55 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/IDataModelScriptDebugVariableSetEnumerator.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDataModelScriptDebugVariableSetEnumerator extends IUnknownEx { + final IID IID_IDATA_MODEL_SCRIPT_DEBUG_VARIABLE_SET_ENUMERATOR = + new IID("0F9FEED7-D045-4ac3-98A8-A98942CF6A35"); + + enum VTIndices implements VTableIndex { + RESET, // + GET_NEXT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT Reset(); + + HRESULT GetNext(BSTRByReference varableName, PointerByReference variableValue, + PointerByReference variableMetadata); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebug.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebug.java new file mode 100644 index 0000000000..c7fc96de64 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebug.java @@ -0,0 +1,98 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptDebug extends UnknownWithUtils implements IDataModelScriptDebug { + public static class ByReference extends WrapIDataModelScriptDebug + implements Structure.ByReference { + } + + public WrapIDataModelScriptDebug() { + } + + public WrapIDataModelScriptDebug(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT ScriptDebugState() { + return _invokeHR(VTIndices.GET_DEBUG_STATE, getPointer()); + } + + @Override + public HRESULT GetDebugState() { + return _invokeHR(VTIndices.GET_DEBUG_STATE, getPointer()); + } + + @Override + public HRESULT GetCurrentPosition(Pointer currentPosition, Pointer positionSpanEnd, + BSTRByReference lineText) { + return _invokeHR(VTIndices.GET_CURRENT_POSITION, getPointer(), currentPosition, + positionSpanEnd, lineText); + } + + @Override + public HRESULT GetStack(PointerByReference stack) { + return _invokeHR(VTIndices.GET_STACK, getPointer(), stack); + } + + @Override + public HRESULT SetBreakpoint(ULONG linePosition, ULONG columnPosition, + PointerByReference breakpoint) { + return _invokeHR(VTIndices.SET_BREAKPOINT, getPointer(), linePosition, columnPosition, + breakpoint); + } + + @Override + public HRESULT FindBreakpointById(ULONGLONG breakpointId, PointerByReference breakpoint) { + return _invokeHR(VTIndices.FIND_BREAKPOINT_BY_ID, getPointer(), breakpointId, breakpoint); + } + + @Override + public HRESULT EnumerateBreakpoints(PointerByReference breakpointEnum) { + return _invokeHR(VTIndices.ENUMERATE_BREAKPOINTS, getPointer(), breakpointEnum); + } + + @Override + public HRESULT GetEventFilter(ULONG eventFilter, BOOLByReference isBreakEnabled) { + return _invokeHR(VTIndices.GET_EVENT_FILTER, getPointer(), eventFilter, isBreakEnabled); + } + + @Override + public HRESULT SetEventFilter(ULONG eventFilter, BOOL isBreakEnabled) { + return _invokeHR(VTIndices.SET_EVENT_FILTER, getPointer(), eventFilter, isBreakEnabled); + } + + @Override + public HRESULT StartDebugging(Pointer debugClient) { + return _invokeHR(VTIndices.START_DEBUGGING, getPointer(), debugClient); + } + + @Override + public HRESULT StopDebugging(Pointer debugClient) { + return _invokeHR(VTIndices.STOP_DEBUGGING, getPointer(), debugClient); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebug2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebug2.java new file mode 100644 index 0000000000..7a0ec235e1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebug2.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +public class WrapIDataModelScriptDebug2 extends WrapIDataModelScriptDebug + implements IDataModelScriptDebug2 { + public static class ByReference extends WrapIDataModelScriptDebug2 + implements Structure.ByReference { + } + + public WrapIDataModelScriptDebug2() { + } + + public WrapIDataModelScriptDebug2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT SetBreakpointAtFunction(WString functionName, PointerByReference breakpoint) { + return _invokeHR(VTIndices2.SET_BREAKPOINT_AT_FUNCTION, getPointer(), functionName, + breakpoint); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugBreakpoint.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugBreakpoint.java new file mode 100644 index 0000000000..05fe01fbf4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugBreakpoint.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptDebugBreakpoint extends UnknownWithUtils + implements IDataModelScriptDebugBreakpoint { + public static class ByReference extends WrapIDataModelScriptDebugBreakpoint + implements Structure.ByReference { + } + + public WrapIDataModelScriptDebugBreakpoint() { + } + + public WrapIDataModelScriptDebugBreakpoint(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetId() { + return _invokeHR(VTIndices.GET_ID, getPointer()); + } + + @Override + public HRESULT IsEnabled() { + return _invokeHR(VTIndices.IS_ENABLED, getPointer()); + } + + @Override + public HRESULT Enable() { + return _invokeHR(VTIndices.ENABLE, getPointer()); + } + + @Override + public HRESULT Disable() { + return _invokeHR(VTIndices.DISABLE, getPointer()); + } + + @Override + public HRESULT Remove() { + return _invokeHR(VTIndices.REMOVE, getPointer()); + } + + @Override + public HRESULT GetPosition(Pointer position, Pointer positionSpanEnd, WString lineText) { + return _invokeHR(VTIndices.GET_POSITION, getPointer(), position, positionSpanEnd, lineText); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugBreakpointEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugBreakpointEnumerator.java new file mode 100644 index 0000000000..693d11b499 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugBreakpointEnumerator.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptDebugBreakpointEnumerator extends UnknownWithUtils + implements IDataModelScriptDebugBreakpointEnumerator { + public static class ByReference extends WrapIDataModelScriptDebugBreakpointEnumerator + implements Structure.ByReference { + } + + public WrapIDataModelScriptDebugBreakpointEnumerator() { + } + + public WrapIDataModelScriptDebugBreakpointEnumerator(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT Reset() { + return _invokeHR(VTIndices.RESET, getPointer()); + } + + @Override + public HRESULT GetNext(PointerByReference breakpoint) { + return _invokeHR(VTIndices.GET_NEXT, getPointer(), breakpoint); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugClient.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugClient.java new file mode 100644 index 0000000000..a79d012921 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugClient.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptDebugClient extends UnknownWithUtils + implements IDataModelScriptDebugClient { + public static class ByReference extends WrapIDataModelScriptDebugClient + implements Structure.ByReference { + } + + public WrapIDataModelScriptDebugClient() { + } + + public WrapIDataModelScriptDebugClient(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT NotifyDebugEvent(Pointer pEventInfo, Pointer pScript, Pointer pEventDataObject, + Pointer resumeEventKind) { + return _invokeHR(VTIndices.NOTIFY_DEBUG_EVENT, getPointer(), pEventInfo, pScript, + pEventDataObject, resumeEventKind); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugStack.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugStack.java new file mode 100644 index 0000000000..d1cb4ce3a1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugStack.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptDebugStack extends UnknownWithUtils + implements IDataModelScriptDebugStack { + public static class ByReference extends WrapIDataModelScriptDebugStack + implements Structure.ByReference { + } + + public WrapIDataModelScriptDebugStack() { + } + + public WrapIDataModelScriptDebugStack(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetFrameCount() { + return _invokeHR(VTIndices.GET_FRAME_COUNT, getPointer()); + } + + @Override + public HRESULT GetStackFrame(ULONGLONG frameNumber, PointerByReference stackFrame) { + return _invokeHR(VTIndices.GET_STACK_FRAME, getPointer(), frameNumber, stackFrame); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugStackFrame.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugStackFrame.java new file mode 100644 index 0000000000..3afada0429 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugStackFrame.java @@ -0,0 +1,77 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptDebugStackFrame extends UnknownWithUtils + implements IDataModelScriptDebugStackFrame { + public static class ByReference extends WrapIDataModelScriptDebugStackFrame + implements Structure.ByReference { + } + + public WrapIDataModelScriptDebugStackFrame() { + } + + public WrapIDataModelScriptDebugStackFrame(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetName(BSTRByReference name) { + return _invokeHR(VTIndices.GET_NAME, getPointer(), name); + } + + @Override + public HRESULT GetPosition(Pointer position, Pointer positionSpanEnd, + BSTRByReference lineText) { + return _invokeHR(VTIndices.GET_POSITION, getPointer(), position, positionSpanEnd, lineText); + } + + @Override + public HRESULT IsTransitionPoint(BOOLByReference isTransitionPoint) { + return _invokeHR(VTIndices.IS_TRANSITION_POINT, getPointer(), isTransitionPoint); + } + + @Override + public HRESULT GetTransition(PointerByReference transitionScript, + BOOLByReference isTransitionContiguous) { + return _invokeHR(VTIndices.GET_TRANSITION, getPointer(), transitionScript, + isTransitionContiguous); + } + + @Override + public HRESULT Evaluate(WString pwszExpression, PointerByReference ppResult) { + return _invokeHR(VTIndices.EVALUATE, getPointer(), pwszExpression, ppResult); + } + + @Override + public HRESULT EnumerateLocals(PointerByReference variablesEnum) { + return _invokeHR(VTIndices.ENUMERATE_LOCALS, getPointer(), variablesEnum); + } + + @Override + public HRESULT EnumerateArguments(PointerByReference variablesEnum) { + return _invokeHR(VTIndices.ENUMERATE_ARGUMENTS, getPointer(), variablesEnum); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugVariableSetEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugVariableSetEnumerator.java new file mode 100644 index 0000000000..efabf00ccc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/datamodel/script/debug/WrapIDataModelScriptDebugVariableSetEnumerator.java @@ -0,0 +1,51 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.datamodel.script.debug; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDataModelScriptDebugVariableSetEnumerator extends UnknownWithUtils + implements IDataModelScriptDebugVariableSetEnumerator { + public static class ByReference extends WrapIDataModelScriptDebugVariableSetEnumerator + implements Structure.ByReference { + } + + public WrapIDataModelScriptDebugVariableSetEnumerator() { + } + + public WrapIDataModelScriptDebugVariableSetEnumerator(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT Reset() { + return _invokeHR(VTIndices.RESET, getPointer()); + } + + @Override + public HRESULT GetNext(BSTRByReference varableName, PointerByReference variableValue, + PointerByReference variableMetadata) { + return _invokeHR(VTIndices.GET_NEXT, getPointer(), varableName, variableValue, + variableMetadata); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHost.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHost.java new file mode 100644 index 0000000000..d5099495e5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHost.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHost extends IUnknownEx { + final IID IID_IDEBUG_HOST = new IID("B8C74943-6B2C-4eeb-B5C5-35D378A6D99D"); + + enum VTIndices implements VTableIndex { + GET_HOST_DEFINED_INTERFACE, // + GET_CURRENT_CONTEXT, // + GET_DEFAULT_METADATA; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetHostDefinedInterface(PointerByReference hostUnk); + + HRESULT GetCurrentContext(PointerByReference context); + + HRESULT GetDefaultMetadata(PointerByReference defaultMetadataStore); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostBaseClass.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostBaseClass.java new file mode 100644 index 0000000000..51c3087e74 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostBaseClass.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostBaseClass extends IUnknownEx { + final IID IID_IDEBUG_HOST_BASE_CLASS = new IID("B94D57D2-390B-40f7-B5B4-B6DB897D974B"); + + enum VTIndices implements VTableIndex { + GET_CONTEXT, // + ENUMERATE_CHILDREN, // + GET_SYMBOL_KIND, // + GET_NAME, // + GET_TYPE, // + GET_CONTAINING_MODULE, // + GET_OFFSET, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetContext(PointerByReference context); + + HRESULT EnumerateChildren(ULONG kind, WString name, PointerByReference ppEnum); // SymbolKind + + HRESULT GetSymbolKind(ULONGByReference kind); // SymbolKind* + + HRESULT GetName(BSTRByReference symbolName); //? + + HRESULT GetType(PointerByReference type); + + HRESULT GetContainingModule(PointerByReference containingModule); + + HRESULT GetOffset(ULONGLONGByReference offset); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostConstant.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostConstant.java new file mode 100644 index 0000000000..7e78e163c3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostConstant.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostConstant extends IDebugHostBaseClass { + final IID IID_IDEBUG_HOST_CONSTANT = new IID("62787EDC-FA76-4690-BD71-5E8C3E2937EC"); + + enum VTIndicesX implements VTableIndex { + GET_VALUE, // + ; + + public int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetValue(VARIANT.ByReference value); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostContext.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostContext.java new file mode 100644 index 0000000000..eadbec653f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostContext.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostContext extends IUnknownEx { + final IID IID_IDEBUG_HOST_CONTEXT = new IID("A68C70D8-5EC0-46e5-B775-3134A48EA2E3"); + + enum VTIndices implements VTableIndex { + IS_EQUAL_TO, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT IsEqualTo(Pointer pContext, BOOLByReference pIsEqual); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostData.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostData.java new file mode 100644 index 0000000000..bf611bc07a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostData.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostData extends IDebugHostBaseClass { + final IID IID_IDEBUG_HOST_DATA = new IID("A3D64993-826C-44fa-897D-926F2FE7AD0B"); + + enum VTIndicesX implements VTableIndex { + GET_LOCATION_KIND, // + GET_LOCATION, // + GET_VALUE, // + ; + + public int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetLocationKind(ULONGByReference locationKind); // LocationKind* + + HRESULT GetLocation(LOCATION.ByReference location); + + HRESULT GetValue(VARIANT.ByReference value); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostErrorSink.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostErrorSink.java new file mode 100644 index 0000000000..2e51553939 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostErrorSink.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostErrorSink extends IUnknownEx { + final IID IID_IDEBUG_HOST_ERROR_SINK = new IID("C8FF0F0B-FCE9-467e-8BB3-5D69EF109C00"); + + enum VTIndices implements VTableIndex { + REPORT_ERROR, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT ReportError(ULONG errClass, HRESULT hrError, WString message); // ErrorClass + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostEvaluator1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostEvaluator1.java new file mode 100644 index 0000000000..df2478a166 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostEvaluator1.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostEvaluator1 extends IUnknownEx { + final IID IID_IDEBUG_HOST_EVALUATOR = new IID("0FEF9A21-577E-4997-AC7B-1C4883241D99"); + + enum VTIndices1 implements VTableIndex { + EVALUATE_EXPRESSION, // + EVALUATE_EXTENDED_EXPRESSION, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT EvaluateExpression(Pointer context, WString expression, Pointer bindingContext, + PointerByReference result, PointerByReference metadata); + + HRESULT EvaluateExtendedExpression(Pointer context, WString expression, Pointer bindingContext, + PointerByReference result, PointerByReference metadata); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostEvaluator2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostEvaluator2.java new file mode 100644 index 0000000000..13d2d5dc6c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostEvaluator2.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostEvaluator2 extends IDebugHostEvaluator1 { + final IID IID_IDEBUG_HOST_EVALUATOR2 = new IID("A117A435-1FB4-4092-A2AB-A929576C1E87"); + + enum VTIndices2 implements VTableIndex { + ASSIGN_TO, // + ; + + public int start = VTableIndex.follow(VTIndices1.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT AssignTo(Pointer assignmentReference, Pointer assignmentValue, + PointerByReference assignmentResult, PointerByReference assignmentMetadata); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostExtensability.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostExtensability.java new file mode 100644 index 0000000000..8dd65fa485 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostExtensability.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostExtensability extends IUnknownEx { + final IID IID_IDEBUG_HOST_EXTENSABILITY = new IID("3C2B24E1-11D0-4f86-8AE5-4DF166F73253"); + + enum VTIndices implements VTableIndex { + CREATE_FUNCTION_ALIAS, // + DESTROY_FUNCTION_ALIAS, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT CreateFunctionAlias(WString aliasName, Pointer functionObject); + + HRESULT DestroyFunctionAlias(WString aliasName); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostField.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostField.java new file mode 100644 index 0000000000..04a7486988 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostField.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostField extends IDebugHostBaseClass { + final IID IID_IDEBUG_HOST_FIELD = new IID("E06F6495-16BC-4cc9-B11D-2A6B23FA72F3"); + + enum VTIndicesX implements VTableIndex { + GET_LOCATION_KIND, // + GET_OFFSET, // + GET_LOCATION, // + GET_VALUE, // + ; + + public int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetLocationKind(ULONGByReference locationKind); // LocationKind* + + HRESULT GetOffset(ULONGLONGByReference offset); + + HRESULT GetLocation(LOCATION.ByReference location); + + HRESULT GetValue(VARIANT.ByReference value); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostMemory1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostMemory1.java new file mode 100644 index 0000000000..f44f2fb519 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostMemory1.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import java.nio.ByteBuffer; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostMemory1 extends IUnknownEx { + final IID IID_IDEBUG_HOST_MEMORY = new IID("212149C9-9183-4a3e-B00E-4FD1DC95339B"); + + enum VTIndices1 implements VTableIndex { + READ_BYTES, // + WRITE_BYTES, // + READ_POINTERS, // + WRITE_POINTERS, // + GET_DISPLAY_STRING_FOR_LOCATION, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT ReadBytes(Pointer context, LOCATION location, ByteBuffer buffer, ULONGLONG bufferSize, + ULONGLONGByReference bytesRead); + + HRESULT WriteBytes(Pointer context, LOCATION location, ByteBuffer buffer, ULONGLONG bufferSize, + ULONGLONGByReference bytesWritten); + + HRESULT ReadPointers(Pointer context, LOCATION location, ULONGLONG count, + ULONGLONGByReference pointers); + + HRESULT WritePointers(Pointer context, LOCATION location, ULONGLONG count, + ULONGLONGByReference pointers); + + HRESULT GetDisplayStringForLocation(Pointer context, LOCATION location, BOOL verbose, + BSTRByReference locationName); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostMemory2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostMemory2.java new file mode 100644 index 0000000000..cfe8b74b2a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostMemory2.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostMemory2 extends IDebugHostMemory1 { + final IID IID_IDEBUG_HOST_MEMORY2 = new IID("EEA033DE-38F6-416b-A251-1D3771001270"); + + enum VTIndices2 implements VTableIndex { + LINEARIZE_LOCATION, // + ; + + public int start = VTableIndex.follow(VTIndices1.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT LinearizeLocation(Pointer context, LOCATION location, + LOCATION.ByReference pLinearizedLocation); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModule1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModule1.java new file mode 100644 index 0000000000..a9bd3467ad --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModule1.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostModule1 extends IDebugHostBaseClass { + final IID IID_IDEBUG_HOST_MODULE = new IID("C9BA3E18-D070-4378-BBD0-34613B346E1E"); + + enum VTIndices1 implements VTableIndex { + GET_IMAGE_NAME, // + GET_BASE_LOCATION, // + GET_VERSION, // + FIND_TYPE_BY_NAME, // + FIND_SYMBOL_BY_RVA, // + FIND_SYMBOL_BY_NAME, // + ; + + public int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetImageName(BOOL allowPath, BSTRByReference imageName); + + HRESULT GetBaseLocation(LOCATION.ByReference moduleBaseLocation); + + HRESULT GetVersion(ULONGLONGByReference fileVersion, ULONGLONGByReference productVersion); + + HRESULT FindTypeByName(WString typeName, PointerByReference type); + + HRESULT FindSymbolByRVA(ULONGLONG rva, PointerByReference symbol); + + HRESULT FindSymbolByName(WString symbolName, PointerByReference symbol); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModule2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModule2.java new file mode 100644 index 0000000000..00de0bbb1d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModule2.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostModule2 extends IDebugHostModule1 { + final IID IID_IDEBUG_HOST_MODULE2 = new IID("B51887E8-BCD0-4e8f-A8C7-434398B78C37"); + + enum VTIndices2 implements VTableIndex { + FIND_CONTAINING_SYMBOL_BY_RVA, // + ; + + public int start = VTableIndex.follow(VTIndices1.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT FindContainingSymbolByRVA(ULONGLONG rva, PointerByReference symbol, + ULONGLONGByReference offset); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModuleSignature.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModuleSignature.java new file mode 100644 index 0000000000..686f3da369 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostModuleSignature.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostModuleSignature extends IUnknownEx { + final IID IID_IDEBUG_HOST_MODULE_SIGNATURE = new IID("31E53A5A-01EE-4BBB-B899-4B46AE7D595C"); + + enum VTIndices implements VTableIndex { + IS_MATCH, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT IsMatch(Pointer pModule, BOOLByReference isMatch); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostPublic.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostPublic.java new file mode 100644 index 0000000000..ec17d8ea3d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostPublic.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostPublic extends IDebugHostBaseClass { + final IID IID_IDEBUG_HOST_PUBLIC = new IID("6C597AC9-FB4D-4f6d-9F39-22488539F8F4"); + + enum VTIndicesX implements VTableIndex { + GET_LOCATION_KIND, // + GET_LOCATION, // + ; + + public int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetLocationKind(ULONGByReference locationKind); // LocationKind* + + HRESULT GetLocation(LOCATION.ByReference location); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostScriptHost.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostScriptHost.java new file mode 100644 index 0000000000..bae1fda5f8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostScriptHost.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostScriptHost extends IUnknownEx { + final IID IID_IDEBUG_HOST_SCRIPT_HOST = new IID("B70334A4-B92C-4570-93A1-D3EB686649A0"); + + enum VTIndices implements VTableIndex { + CREATE_CONTEXT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT CreateContext(Pointer script, PointerByReference scriptContext); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostStatus.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostStatus.java new file mode 100644 index 0000000000..7b82eb7f30 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostStatus.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostStatus extends IUnknownEx { + final IID IID_IDEBUG_HOST_STATUS = new IID("4F3E1CE2-86B2-4C7A-9C65-D0A9D0EECF44"); + + enum VTIndices implements VTableIndex { + POLL_USER_INTERRUPT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT PollUserInterrupt(BOOLByReference interruptRequested); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbol1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbol1.java new file mode 100644 index 0000000000..121a9f65b5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbol1.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostSymbol1 extends IDebugHostBaseClass { + final IID IID_IDEBUG_HOST_SYMBOL = new IID("0F819103-87DE-4e96-8277-E05CD441FB22"); + + enum VTIndices1 implements VTableIndex { + COMPARE_AGAINST, // + ; + + public int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT CompareAgainst(Pointer pComparisonSymbol, ULONG comparisonFlags, + BOOLByReference pMatches); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbol2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbol2.java new file mode 100644 index 0000000000..1ad0aaa261 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbol2.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Structure.ByReference; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostSymbol2 extends IDebugHostSymbol1 { + final IID IID_IDEBUG_HOST_SYMBOL2 = new IID("21515B67-6720-4257-8A68-077DC944471C"); + + enum VTIndices2 implements VTableIndex { + ENUMERATE_CHILDREN_EX, // + ; + + public int start = VTableIndex.follow(VTIndices1.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT EnumerateChildrenEx(ULONG ulKind, WString name, ByReference searchInfo, + PointerByReference ppEnum); + + HRESULT GetLanguage(ULONGByReference pKind); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbolEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbolEnumerator.java new file mode 100644 index 0000000000..c760b6d8f4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbolEnumerator.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostSymbolEnumerator extends IUnknownEx { + final IID IID_IDEBUG_HOST_SYMBOL_ENUMERATOR = new IID("28D96C86-10A3-4976-B14E-EAEF4790AA1F"); + + enum VTIndices implements VTableIndex { + RESET, // + GET_NEXT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT Reset(); + + HRESULT GetNext(PointerByReference symbol); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbols.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbols.java new file mode 100644 index 0000000000..87ea35ae6e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostSymbols.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostSymbols extends IUnknownEx { + final IID IID_IDEBUG_HOST_SYMBOLS = new IID("854FD751-C2E1-4eb2-B525-6619CB97A588"); + + enum VTIndices implements VTableIndex { + CREATE_MODULE_SIGNATURE, // + CREATE_TYPE_SIGNATURE, // + CREATE_TYPE_SIGNATURE_FOR_MODULE_RANGE, // + ENUMERATE_MODULES, // + FIND_MODULE_BY_NAME, // + FIND_MODULE_BY_LOCATION, // + GET_MOST_DERIVED_OBJECT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT CreateModuleSignature(WString pwszModuleName, WString pwszMinVersion, + WString pwszMaxVersion, PointerByReference ppModuleSignature); + + HRESULT CreateTypeSignature(WString signatureSpecification, Pointer module, + PointerByReference typeSignature); + + HRESULT CreateTypeSignatureForModuleRange(WString signatureSpecification, WString moduleName, + WString minVersion, WString maxVersion, PointerByReference typeSignature); + + HRESULT EnumerateModules(Pointer context, PointerByReference moduleEnum); + + HRESULT FindModuleByName(Pointer context, WString moduleName, PointerByReference module); + + HRESULT FindModuleByLocation(Pointer context, LOCATION moduleLocation, + PointerByReference module); + + HRESULT GetMostDerivedObject(Pointer pContext, LOCATION location, Pointer objectType, + LOCATION.ByReference derivedLocation, PointerByReference derivedType); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostType1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostType1.java new file mode 100644 index 0000000000..d01ac5010a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostType1.java @@ -0,0 +1,99 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.VARTYPEByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ARRAY_DIMENSION.ByReference; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostType1 extends IDebugHostBaseClass { + final IID IID_IDEBUG_HOST_TYPE = new IID("3AADC353-2B14-4abb-9893-5E03458E07EE"); + + enum VTIndices1 implements VTableIndex { + GET_TYPE_KIND, // + GET_SIZE, // + GET_BASE_TYPE, // + GET_HASH_CODE, // + GET_INTRINSIC_TYPE, // + GET_BIT_FIELD, // + GET_POINTER_KIND, // + GET_MEMBER_TYPE, // + CREATE_POINTER_TO, // + GET_ARRAY_DIMENSIONALITY, // + GET_ARRAY_DIMENSIONS, // + CREATE_ARRAY_OF, // + GET_FUNCTION_CALLING_CONVENTION, // + GET_FUNCTION_RETURN_TYPE, // + GET_FUNCTION_PARAMETER_TYPE_COUNT, // + GET_FUNCTION_PARAMETER_TYPE_AT, // + IS_GENERIC, // + GET_GENERIC_ARGUMENT_COUNT, // + GET_GENERIC_ARGUMENT_AT, // + ; + + public int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetTypeKind(ULONGByReference kind); // TypeKind* + + HRESULT GetSize(ULONGLONGByReference pulSize); + + HRESULT GetBaseType(PointerByReference baseType); + + HRESULT GetHashCode(ULONGByReference hashCode); + + HRESULT GetIntrinsicType(ULONGByReference pulIntrinsicKind, VARTYPEByReference pCarrierType); + + HRESULT GetBitField(ULONGByReference lsbOfField, ULONGByReference lengthOfField); + + HRESULT GetPointerKind(ULONGByReference pointerKind); // PointerKind* + + HRESULT GetMemberType(PointerByReference memberType); + + HRESULT CreatePointerTo(ULONG kind, PointerByReference newType); // PointerKind + + HRESULT GetArrayDimensionality(ULONGLONGByReference pulArrayDimensionality); + + HRESULT GetArrayDimensions(ULONGLONG pDimensions, ByReference ppDimensions); + + HRESULT CreateArrayOf(ULONGLONG ulDimensions, ByReference pDimensions, + PointerByReference newType); + + HRESULT GetFunctionCallingConvention(ULONGByReference conventionKind); // ConventionKind* + + HRESULT GetFunctionReturnType(PointerByReference returnType); + + HRESULT GetFunctionParameterTypeCount(ULONGByReference count); + + HRESULT GetFunctionParameterTypeAt(ULONG i, PointerByReference parameterType); + + HRESULT IsGeneric(BOOLByReference isGeneric); + + HRESULT GetGenericArgumentCount(ULONGLONGByReference pulArgCount); + + HRESULT GetGenericArgumentAt(ULONG i, PointerByReference argument); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostType2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostType2.java new file mode 100644 index 0000000000..424d7df772 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostType2.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostType2 extends IDebugHostType1 { + final IID IID_IDEBUG_HOST_TYPE2 = new IID("B28632B9-8506-4676-87CE-8F7E05E59876"); + + enum VTIndices2 implements VTableIndex { + IS_TYPEDEF, // + GET_TYPEDEF_BASE_TYPE, // + GET_TYPEDEF_FINAL_BASE_TYPE, // + GET_FUNCTION_VARARGS_KIND, // + GET_FUNCTION_INSTANCE_POINTER_TYPE, // + ; + + public int start = VTableIndex.follow(VTIndices1.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT IsTypedef(BOOLByReference isTypedef); + + HRESULT GetTypedefBaseType(PointerByReference baseType); + + HRESULT GetTypedefFinalBaseType(PointerByReference finalBaseType); + + HRESULT GetFunctionVarArgsKind(ULONGByReference varArgsKind); // VarArgsKind* + + HRESULT GetFunctionInstancePointerType(PointerByReference instancePointerType); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostTypeSignature.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostTypeSignature.java new file mode 100644 index 0000000000..9feaa89cba --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/IDebugHostTypeSignature.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IDebugHostTypeSignature extends IUnknownEx { + final IID IID_IDEBUG_HOST_TYPE_SIGNATURE = new IID("3AADC353-2B14-4abb-9893-5E03458E07EE"); + + enum VTIndices implements VTableIndex { + GET_HASH_CODE, // + IS_MATCH, // + COMPARE_AGAINST; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetHashCode(ULONGByReference hashCode); + + HRESULT IsMatch(Pointer type, BOOLByReference isMatch, PointerByReference wildcardMatches); + + HRESULT CompareAgainst(Pointer typeSignature, ULONGByReference result); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHost.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHost.java new file mode 100644 index 0000000000..23794f90bb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHost.java @@ -0,0 +1,51 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDebugHost extends UnknownWithUtils implements IDebugHost { + public static class ByReference extends WrapIDebugHost implements Structure.ByReference { + } + + public WrapIDebugHost() { + } + + public WrapIDebugHost(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetHostDefinedInterface(PointerByReference hostUnk) { + return _invokeHR(VTIndices.GET_HOST_DEFINED_INTERFACE, getPointer(), hostUnk); + } + + @Override + public HRESULT GetCurrentContext(PointerByReference context) { + return _invokeHR(VTIndices.GET_CURRENT_CONTEXT, getPointer(), context); + } + + @Override + public HRESULT GetDefaultMetadata(PointerByReference defaultMetadataStore) { + return _invokeHR(VTIndices.GET_DEFAULT_METADATA, getPointer(), defaultMetadataStore); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostBaseClass.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostBaseClass.java new file mode 100644 index 0000000000..336169c958 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostBaseClass.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDebugHostBaseClass extends UnknownWithUtils implements IDebugHostBaseClass { + public static class ByReference extends WrapIDebugHostBaseClass + implements Structure.ByReference { + } + + public WrapIDebugHostBaseClass() { + } + + public WrapIDebugHostBaseClass(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetContext(PointerByReference context) { + return _invokeHR(VTIndices.GET_CONTEXT, getPointer(), context); + } + + @Override + public HRESULT EnumerateChildren(ULONG kind, WString name, PointerByReference ppEnum) { + return _invokeHR(VTIndices.ENUMERATE_CHILDREN, getPointer(), kind, name, ppEnum); + } + + @Override + public HRESULT GetSymbolKind(ULONGByReference kind) { + return _invokeHR(VTIndices.GET_SYMBOL_KIND, getPointer(), kind); + } + + @Override + public HRESULT GetName(BSTRByReference symbolName) { + return _invokeHR(VTIndices.GET_NAME, getPointer(), symbolName); + } + + @Override + public HRESULT GetType(PointerByReference type) { + return _invokeHR(VTIndices.GET_TYPE, getPointer(), type); + } + + @Override + public HRESULT GetContainingModule(PointerByReference containingModule) { + return _invokeHR(VTIndices.GET_CONTAINING_MODULE, getPointer(), containingModule); + } + + @Override + public HRESULT GetOffset(ULONGLONGByReference offset) { + return _invokeHR(VTIndices.GET_OFFSET, getPointer(), offset); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostConstant.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostConstant.java new file mode 100644 index 0000000000..a983714a34 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostConstant.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class WrapIDebugHostConstant extends WrapIDebugHostBaseClass implements IDebugHostConstant { + public static class ByReference extends WrapIDebugHostConstant + implements Structure.ByReference { + } + + public WrapIDebugHostConstant() { + } + + public WrapIDebugHostConstant(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetValue(VARIANT.ByReference value) { + return _invokeHR(VTIndicesX.GET_VALUE, getPointer(), value); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostContext.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostContext.java new file mode 100644 index 0000000000..8e7fb10dc1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostContext.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDebugHostContext extends UnknownWithUtils implements IDebugHostContext { + public static class ByReference extends WrapIDebugHostContext implements Structure.ByReference { + } + + public WrapIDebugHostContext() { + } + + public WrapIDebugHostContext(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT IsEqualTo(Pointer pContext, BOOLByReference pIsEqual) { + return _invokeHR(VTIndices.IS_EQUAL_TO, getPointer(), pContext, pIsEqual); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostData.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostData.java new file mode 100644 index 0000000000..88047d3e32 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostData.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +public class WrapIDebugHostData extends WrapIDebugHostBaseClass implements IDebugHostData { + public static class ByReference extends WrapIDebugHostData implements Structure.ByReference { + } + + public WrapIDebugHostData() { + } + + public WrapIDebugHostData(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetLocationKind(ULONGByReference locationKind) { + return _invokeHR(VTIndicesX.GET_LOCATION_KIND, getPointer(), locationKind); + } + + @Override + public HRESULT GetLocation(LOCATION.ByReference location) { + return _invokeHR(VTIndicesX.GET_LOCATION, getPointer(), location); + } + + @Override + public HRESULT GetValue(VARIANT.ByReference value) { + return _invokeHR(VTIndicesX.GET_VALUE, getPointer(), value); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostErrorSink.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostErrorSink.java new file mode 100644 index 0000000000..5b4396021d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostErrorSink.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDebugHostErrorSink extends UnknownWithUtils implements IDebugHostErrorSink { + public static class ByReference extends WrapIDebugHostErrorSink + implements Structure.ByReference { + } + + public WrapIDebugHostErrorSink() { + } + + public WrapIDebugHostErrorSink(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT ReportError(ULONG errClass, HRESULT hrError, WString message) { + return _invokeHR(VTIndices.REPORT_ERROR, getPointer(), errClass, hrError, message); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostEvaluator1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostEvaluator1.java new file mode 100644 index 0000000000..726dfadb44 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostEvaluator1.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDebugHostEvaluator1 extends UnknownWithUtils implements IDebugHostEvaluator1 { + public static class ByReference extends WrapIDebugHostEvaluator1 + implements Structure.ByReference { + } + + public WrapIDebugHostEvaluator1() { + } + + public WrapIDebugHostEvaluator1(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT EvaluateExpression(Pointer context, WString expression, Pointer bindingContext, + PointerByReference result, PointerByReference metadata) { + return _invokeHR(VTIndices1.EVALUATE_EXPRESSION, getPointer(), context, expression, + bindingContext, result, metadata); + } + + @Override + public HRESULT EvaluateExtendedExpression(Pointer context, WString expression, + Pointer bindingContext, PointerByReference result, PointerByReference metadata) { + return _invokeHR(VTIndices1.EVALUATE_EXTENDED_EXPRESSION, getPointer(), context, expression, + bindingContext, result, metadata); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostEvaluator2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostEvaluator2.java new file mode 100644 index 0000000000..853b1b6fcd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostEvaluator2.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +public class WrapIDebugHostEvaluator2 extends WrapIDebugHostEvaluator1 + implements IDebugHostEvaluator2 { + public static class ByReference extends WrapIDebugHostEvaluator2 + implements Structure.ByReference { + } + + public WrapIDebugHostEvaluator2() { + } + + public WrapIDebugHostEvaluator2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT AssignTo(Pointer assignmentReference, Pointer assignmentValue, + PointerByReference assignmentResult, PointerByReference assignmentMetadata) { + return _invokeHR(VTIndices2.ASSIGN_TO, getPointer(), assignmentReference, assignmentValue, + assignmentResult, assignmentMetadata); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostExtensability.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostExtensability.java new file mode 100644 index 0000000000..601f138361 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostExtensability.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDebugHostExtensability extends UnknownWithUtils + implements IDebugHostExtensability { + public static class ByReference extends WrapIDebugHostExtensability + implements Structure.ByReference { + } + + public WrapIDebugHostExtensability() { + } + + public WrapIDebugHostExtensability(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT CreateFunctionAlias(WString aliasName, Pointer functionObject) { + return _invokeHR(VTIndices.CREATE_FUNCTION_ALIAS, getPointer(), aliasName, functionObject); + } + + @Override + public HRESULT DestroyFunctionAlias(WString aliasName) { + return _invokeHR(VTIndices.DESTROY_FUNCTION_ALIAS, getPointer(), aliasName); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostField.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostField.java new file mode 100644 index 0000000000..97d7b90f38 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostField.java @@ -0,0 +1,58 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +public class WrapIDebugHostField extends WrapIDebugHostBaseClass implements IDebugHostField { + public static class ByReference extends WrapIDebugHostField implements Structure.ByReference { + } + + public WrapIDebugHostField() { + } + + public WrapIDebugHostField(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetLocationKind(ULONGByReference locationKind) { + return _invokeHR(VTIndicesX.GET_LOCATION_KIND, getPointer(), locationKind); + } + + @Override + public HRESULT GetOffset(ULONGLONGByReference offset) { + return _invokeHR(VTIndicesX.GET_OFFSET, getPointer(), offset); + } + + @Override + public HRESULT GetLocation(LOCATION.ByReference location) { + return _invokeHR(VTIndicesX.GET_LOCATION, getPointer(), location); + } + + @Override + public HRESULT GetValue(VARIANT.ByReference value) { + return _invokeHR(VTIndicesX.GET_VALUE, getPointer(), value); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostMemory1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostMemory1.java new file mode 100644 index 0000000000..a6ba8ecdb9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostMemory1.java @@ -0,0 +1,79 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import java.nio.ByteBuffer; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +public class WrapIDebugHostMemory1 extends UnknownWithUtils implements IDebugHostMemory1 { + public static class ByReference extends WrapIDebugHostMemory1 implements Structure.ByReference { + } + + public WrapIDebugHostMemory1() { + } + + public WrapIDebugHostMemory1(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT ReadBytes(Pointer context, LOCATION location, ByteBuffer buffer, + ULONGLONG bufferSize, + ULONGLONGByReference bytesRead) { + return _invokeHR(VTIndices1.READ_BYTES, getPointer(), context, location, buffer, bufferSize, + bytesRead); + } + + @Override + public HRESULT WriteBytes(Pointer context, LOCATION location, ByteBuffer buffer, + ULONGLONG bufferSize, + ULONGLONGByReference bytesWritten) { + return _invokeHR(VTIndices1.WRITE_BYTES, getPointer(), context, location, buffer, + bufferSize, + bytesWritten); + } + + @Override + public HRESULT ReadPointers(Pointer context, LOCATION location, ULONGLONG count, + ULONGLONGByReference pointers) { + return _invokeHR(VTIndices1.READ_POINTERS, getPointer(), context, location, count, + pointers); + } + + @Override + public HRESULT WritePointers(Pointer context, LOCATION location, ULONGLONG count, + ULONGLONGByReference pointers) { + return _invokeHR(VTIndices1.WRITE_POINTERS, getPointer(), context, location, count, + pointers); + } + + @Override + public HRESULT GetDisplayStringForLocation(Pointer context, LOCATION location, BOOL verbose, + BSTRByReference locationName) { + return _invokeHR(VTIndices1.GET_DISPLAY_STRING_FOR_LOCATION, getPointer(), context, + location, + verbose, locationName); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostMemory2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostMemory2.java new file mode 100644 index 0000000000..b84bb29652 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostMemory2.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +public class WrapIDebugHostMemory2 extends WrapIDebugHostMemory1 implements IDebugHostMemory2 { + public static class ByReference extends WrapIDebugHostMemory2 implements Structure.ByReference { + } + + public WrapIDebugHostMemory2() { + } + + public WrapIDebugHostMemory2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT LinearizeLocation(Pointer context, LOCATION location, + LOCATION.ByReference pLinearizedLocation) { + return _invokeHR(VTIndices2.LINEARIZE_LOCATION, getPointer(), context, location, + pLinearizedLocation); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModule1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModule1.java new file mode 100644 index 0000000000..4bd13b96c4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModule1.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +public class WrapIDebugHostModule1 extends WrapIDebugHostBaseClass implements IDebugHostModule1 { + public static class ByReference extends WrapIDebugHostModule1 implements Structure.ByReference { + } + + public WrapIDebugHostModule1() { + } + + public WrapIDebugHostModule1(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetImageName(BOOL allowPath, BSTRByReference imageName) { + return _invokeHR(VTIndices1.GET_IMAGE_NAME, getPointer(), allowPath, imageName); + } + + @Override + public HRESULT GetBaseLocation(LOCATION.ByReference moduleBaseLocation) { + return _invokeHR(VTIndices1.GET_BASE_LOCATION, getPointer(), moduleBaseLocation); + } + + @Override + public HRESULT GetVersion(ULONGLONGByReference fileVersion, + ULONGLONGByReference productVersion) { + return _invokeHR(VTIndices1.GET_VERSION, getPointer(), fileVersion, productVersion); + } + + @Override + public HRESULT FindTypeByName(WString typeName, PointerByReference type) { + return _invokeHR(VTIndices1.FIND_TYPE_BY_NAME, getPointer(), typeName, type); + } + + @Override + public HRESULT FindSymbolByRVA(ULONGLONG rva, PointerByReference symbol) { + return _invokeHR(VTIndices1.FIND_SYMBOL_BY_RVA, getPointer(), symbol); + } + + @Override + public HRESULT FindSymbolByName(WString symbolName, PointerByReference symbol) { + return _invokeHR(VTIndices1.FIND_SYMBOL_BY_NAME, getPointer(), symbolName, symbol); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModule2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModule2.java new file mode 100644 index 0000000000..dbaeee3a82 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModule2.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +public class WrapIDebugHostModule2 extends WrapIDebugHostModule1 implements IDebugHostModule2 { + public static class ByReference extends WrapIDebugHostModule2 implements Structure.ByReference { + } + + public WrapIDebugHostModule2() { + } + + public WrapIDebugHostModule2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT FindContainingSymbolByRVA(ULONGLONG rva, PointerByReference symbol, + ULONGLONGByReference offset) { + return _invokeHR(VTIndices2.FIND_CONTAINING_SYMBOL_BY_RVA, getPointer(), rva, symbol, + offset); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModuleSignature.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModuleSignature.java new file mode 100644 index 0000000000..1699bed34b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostModuleSignature.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDebugHostModuleSignature extends UnknownWithUtils + implements IDebugHostModuleSignature { + public static class ByReference extends WrapIDebugHostModuleSignature + implements Structure.ByReference { + } + + public WrapIDebugHostModuleSignature() { + } + + public WrapIDebugHostModuleSignature(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT IsMatch(Pointer pModule, BOOLByReference isMatch) { + return _invokeHR(VTIndices.IS_MATCH, getPointer(), pModule, isMatch); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostPublic.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostPublic.java new file mode 100644 index 0000000000..f075754070 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostPublic.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +public class WrapIDebugHostPublic extends WrapIDebugHostBaseClass implements IDebugHostPublic { + public static class ByReference extends WrapIDebugHostPublic implements Structure.ByReference { + } + + public WrapIDebugHostPublic() { + } + + public WrapIDebugHostPublic(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetLocationKind(ULONGByReference locationKind) { + return _invokeHR(VTIndicesX.GET_LOCATION_KIND, getPointer(), locationKind); + } + + @Override + public HRESULT GetLocation(LOCATION.ByReference location) { + return _invokeHR(VTIndicesX.GET_LOCATION, getPointer(), location); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostScriptHost.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostScriptHost.java new file mode 100644 index 0000000000..9fb93393f3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostScriptHost.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDebugHostScriptHost extends UnknownWithUtils implements IDebugHostScriptHost { + public static class ByReference extends WrapIDebugHostScriptHost + implements Structure.ByReference { + } + + public WrapIDebugHostScriptHost() { + } + + public WrapIDebugHostScriptHost(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT CreateContext(Pointer script, PointerByReference scriptContext) { + return _invokeHR(VTIndices.CREATE_CONTEXT, getPointer(), script, scriptContext); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostStatus.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostStatus.java new file mode 100644 index 0000000000..8e9b795ba3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostStatus.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDebugHostStatus extends UnknownWithUtils implements IDebugHostStatus { + public static class ByReference extends WrapIDebugHostStatus implements Structure.ByReference { + } + + public WrapIDebugHostStatus() { + } + + public WrapIDebugHostStatus(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT PollUserInterrupt(BOOLByReference interruptRequested) { + return _invokeHR(VTIndices.POLL_USER_INTERRUPT, getPointer(), interruptRequested); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbol1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbol1.java new file mode 100644 index 0000000000..7ebafa1bbc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbol1.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class WrapIDebugHostSymbol1 extends WrapIDebugHostBaseClass implements IDebugHostSymbol1 { + public static class ByReference extends WrapIDebugHostSymbol1 implements Structure.ByReference { + } + + public WrapIDebugHostSymbol1() { + } + + public WrapIDebugHostSymbol1(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT CompareAgainst(Pointer pComparisonSymbol, ULONG comparisonFlags, + BOOLByReference pMatches) { + return _invokeHR(VTIndices1.COMPARE_AGAINST, getPointer(), pComparisonSymbol, + comparisonFlags, pMatches); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbol2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbol2.java new file mode 100644 index 0000000000..3088702637 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbol2.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinDef.ULONG; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +public class WrapIDebugHostSymbol2 extends WrapIDebugHostSymbol1 implements IDebugHostSymbol2 { + public static class ByReference extends WrapIDebugHostSymbol2 implements Structure.ByReference { + } + + public WrapIDebugHostSymbol2() { + } + + public WrapIDebugHostSymbol2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT EnumerateChildrenEx(ULONG kind, WString name, + com.sun.jna.Structure.ByReference searchInfo, + PointerByReference ppEnum) { + return _invokeHR(VTIndices2.ENUMERATE_CHILDREN_EX, getPointer(), kind, name, searchInfo, + ppEnum); + } + + @Override + public HRESULT GetLanguage(ULONGByReference pKind) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbolEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbolEnumerator.java new file mode 100644 index 0000000000..dfb83522ca --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbolEnumerator.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDebugHostSymbolEnumerator extends UnknownWithUtils + implements IDebugHostSymbolEnumerator { + public static class ByReference extends WrapIDebugHostSymbolEnumerator + implements Structure.ByReference { + } + + public WrapIDebugHostSymbolEnumerator() { + } + + public WrapIDebugHostSymbolEnumerator(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT Reset() { + return _invokeHR(VTIndices.RESET, getPointer()); + } + + @Override + public HRESULT GetNext(PointerByReference symbol) { + return _invokeHR(VTIndices.GET_NEXT, getPointer(), symbol); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbols.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbols.java new file mode 100644 index 0000000000..2830d313cc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostSymbols.java @@ -0,0 +1,83 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +public class WrapIDebugHostSymbols extends UnknownWithUtils implements IDebugHostSymbols { + public static class ByReference extends WrapIDebugHostSymbols implements Structure.ByReference { + } + + public WrapIDebugHostSymbols() { + } + + public WrapIDebugHostSymbols(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT CreateModuleSignature(WString pwszModuleName, WString pwszMinVersion, + WString pwszMaxVersion, PointerByReference ppModuleSignature) { + return _invokeHR(VTIndices.CREATE_MODULE_SIGNATURE, getPointer(), pwszModuleName, + pwszMinVersion, pwszMaxVersion, ppModuleSignature); + } + + @Override + public HRESULT CreateTypeSignature(WString signatureSpecification, Pointer module, + PointerByReference typeSignature) { + return _invokeHR(VTIndices.CREATE_TYPE_SIGNATURE, getPointer(), signatureSpecification, + module, typeSignature); + } + + @Override + public HRESULT CreateTypeSignatureForModuleRange(WString signatureSpecification, + WString moduleName, + WString minVersion, WString maxVersion, PointerByReference typeSignature) { + return _invokeHR(VTIndices.CREATE_TYPE_SIGNATURE_FOR_MODULE_RANGE, getPointer(), + signatureSpecification, moduleName, minVersion, maxVersion, typeSignature); + } + + @Override + public HRESULT EnumerateModules(Pointer context, PointerByReference moduleEnum) { + return _invokeHR(VTIndices.ENUMERATE_MODULES, getPointer(), context, moduleEnum); + } + + @Override + public HRESULT FindModuleByName(Pointer context, WString moduleName, + PointerByReference module) { + return _invokeHR(VTIndices.FIND_MODULE_BY_NAME, getPointer(), context, moduleName, module); + } + + @Override + public HRESULT FindModuleByLocation(Pointer context, LOCATION moduleLocation, + PointerByReference module) { + return _invokeHR(VTIndices.FIND_MODULE_BY_LOCATION, getPointer(), context, moduleLocation, + module); + } + + @Override + public HRESULT GetMostDerivedObject(Pointer pContext, LOCATION location, Pointer objectType, + LOCATION.ByReference derivedLocation, PointerByReference derivedType) { + return _invokeHR(VTIndices.GET_MOST_DERIVED_OBJECT, getPointer(), pContext, location, + objectType, derivedLocation, derivedType); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostType1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostType1.java new file mode 100644 index 0000000000..53a282884f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostType1.java @@ -0,0 +1,136 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WTypes.VARTYPEByReference; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ARRAY_DIMENSION; + +public class WrapIDebugHostType1 extends WrapIDebugHostBaseClass implements IDebugHostType1 { + public static class ByReference extends WrapIDebugHostType1 implements Structure.ByReference { + } + + public WrapIDebugHostType1() { + } + + public WrapIDebugHostType1(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetTypeKind(ULONGByReference kind) { + return _invokeHR(VTIndices1.GET_TYPE_KIND, getPointer(), kind); + } // TypeKind* + + @Override + public HRESULT GetSize(ULONGLONGByReference size) { + return _invokeHR(VTIndices1.GET_SIZE, getPointer(), size); + } + + @Override + public HRESULT GetBaseType(PointerByReference baseType) { + return _invokeHR(VTIndices1.GET_BASE_TYPE, getPointer(), baseType); + } + + @Override + public HRESULT GetHashCode(ULONGByReference hashCode) { + return _invokeHR(VTIndices1.GET_HASH_CODE, getPointer(), hashCode); + } + + @Override + public HRESULT GetIntrinsicType(ULONGByReference intrinsicKind, + VARTYPEByReference carrierType) { + return _invokeHR(VTIndices1.GET_INTRINSIC_TYPE, getPointer(), intrinsicKind, carrierType); + } + + @Override + public HRESULT GetBitField(ULONGByReference lsbOfField, ULONGByReference lengthOfField) { + return _invokeHR(VTIndices1.GET_BIT_FIELD, getPointer(), lsbOfField, lengthOfField); + } + + @Override + public HRESULT GetPointerKind(ULONGByReference pointerKind) { + return _invokeHR(VTIndices1.GET_POINTER_KIND, getPointer(), pointerKind); + } // PointerKind* + + @Override + public HRESULT GetMemberType(PointerByReference memberType) { + return _invokeHR(VTIndices1.GET_MEMBER_TYPE, getPointer(), memberType); + } + + @Override + public HRESULT CreatePointerTo(ULONG kind, PointerByReference newType) { + return _invokeHR(VTIndices1.CREATE_POINTER_TO, getPointer(), kind, newType); + } // PointerKind + + @Override + public HRESULT GetArrayDimensionality(ULONGLONGByReference arrayDimensionality) { + return _invokeHR(VTIndices1.GET_ARRAY_DIMENSIONALITY, getPointer(), arrayDimensionality); + } + + @Override + public HRESULT GetArrayDimensions(ULONGLONG dimensions, + ARRAY_DIMENSION.ByReference pDimensions) { + return _invokeHR(VTIndices1.GET_ARRAY_DIMENSIONS, getPointer(), dimensions, pDimensions); + } + + @Override + public HRESULT CreateArrayOf(ULONGLONG dimensions, ARRAY_DIMENSION.ByReference pDimensions, + PointerByReference newType) { + return _invokeHR(VTIndices1.CREATE_ARRAY_OF, getPointer(), dimensions, pDimensions, + newType); + } + + @Override + public HRESULT GetFunctionCallingConvention(ULONGByReference conventionKind) { + return _invokeHR(VTIndices1.GET_FUNCTION_CALLING_CONVENTION, getPointer(), conventionKind); + } // ConventionKind* + + @Override + public HRESULT GetFunctionReturnType(PointerByReference returnType) { + return _invokeHR(VTIndices1.GET_FUNCTION_RETURN_TYPE, getPointer(), returnType); + } + + @Override + public HRESULT GetFunctionParameterTypeCount(ULONGByReference count) { + return _invokeHR(VTIndices1.GET_FUNCTION_PARAMETER_TYPE_COUNT, getPointer(), count); + } + + @Override + public HRESULT GetFunctionParameterTypeAt(ULONG i, PointerByReference parameterType) { + return _invokeHR(VTIndices1.GET_FUNCTION_PARAMETER_TYPE_AT, getPointer(), i, parameterType); + } + + @Override + public HRESULT IsGeneric(BOOLByReference isGeneric) { + return _invokeHR(VTIndices1.IS_GENERIC, getPointer(), isGeneric); + } + + @Override + public HRESULT GetGenericArgumentCount(ULONGLONGByReference argCount) { + return _invokeHR(VTIndices1.GET_GENERIC_ARGUMENT_COUNT, getPointer(), argCount); + } + + @Override + public HRESULT GetGenericArgumentAt(ULONG i, PointerByReference argument) { + return _invokeHR(VTIndices1.GET_GENERIC_ARGUMENT_AT, getPointer(), i, argument); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostType2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostType2.java new file mode 100644 index 0000000000..af6511769f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostType2.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +public class WrapIDebugHostType2 extends WrapIDebugHostType1 implements IDebugHostType2 { + public static class ByReference extends WrapIDebugHostType2 implements Structure.ByReference { + } + + public WrapIDebugHostType2() { + } + + public WrapIDebugHostType2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT IsTypedef(BOOLByReference isTypedef) { + return _invokeHR(VTIndices2.IS_TYPEDEF, getPointer(), isTypedef); + } + + @Override + public HRESULT GetTypedefBaseType(PointerByReference baseType) { + return _invokeHR(VTIndices2.GET_TYPEDEF_BASE_TYPE, getPointer(), baseType); + } + + @Override + public HRESULT GetTypedefFinalBaseType(PointerByReference finalBaseType) { + return _invokeHR(VTIndices2.GET_TYPEDEF_FINAL_BASE_TYPE, getPointer(), finalBaseType); + } + + @Override + public HRESULT GetFunctionVarArgsKind(ULONGByReference varArgsKind) { + return _invokeHR(VTIndices2.GET_FUNCTION_VARARGS_KIND, getPointer(), varArgsKind); + } // VarArgsKind* + + @Override + public HRESULT GetFunctionInstancePointerType(PointerByReference instancePointerType) { + return _invokeHR(VTIndices2.GET_FUNCTION_INSTANCE_POINTER_TYPE, getPointer(), + instancePointerType); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostTypeSignature.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostTypeSignature.java new file mode 100644 index 0000000000..39053aa532 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/WrapIDebugHostTypeSignature.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.BOOLByReference; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIDebugHostTypeSignature extends UnknownWithUtils + implements IDebugHostTypeSignature { + public static class ByReference extends WrapIDebugHostTypeSignature + implements Structure.ByReference { + } + + public WrapIDebugHostTypeSignature() { + } + + public WrapIDebugHostTypeSignature(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetHashCode(ULONGByReference hashCode) { + return _invokeHR(VTIndices.GET_HASH_CODE, getPointer(), hashCode); + } + + @Override + public HRESULT IsMatch(Pointer type, BOOLByReference isMatch, + PointerByReference wildcardMatches) { + return _invokeHR(VTIndices.IS_MATCH, getPointer(), type, isMatch, wildcardMatches); + } + + @Override + public HRESULT CompareAgainst(Pointer typeSignature, ULONGByReference result) { + return _invokeHR(VTIndices.COMPARE_AGAINST, getPointer(), typeSignature, result); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/X_IDebugHostBaseClass.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/X_IDebugHostBaseClass.java new file mode 100644 index 0000000000..635e89deb0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/X_IDebugHostBaseClass.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface X_IDebugHostBaseClass extends IDebugHostBaseClass { + final IID IID_IDEBUG_HOST_BASE_CLASS = new IID("B94D57D2-390B-40f7-B5B4-B6DB897D974B"); + + enum VTIndicesX implements VTableIndex { + ; + + public int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetOffset(ULONGLONGByReference offset); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/X_WrapIDebugHostBaseClass.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/X_WrapIDebugHostBaseClass.java new file mode 100644 index 0000000000..bfe0576bcf --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/debughost/X_WrapIDebugHostBaseClass.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.debughost; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class X_WrapIDebugHostBaseClass extends WrapIDebugHostBaseClass + implements X_IDebugHostBaseClass { + public static class ByReference extends X_WrapIDebugHostBaseClass + implements Structure.ByReference { + } + + public X_WrapIDebugHostBaseClass() { + } + + public X_WrapIDebugHostBaseClass(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetOffset(ULONGLONGByReference offset) { + return _invokeHR(VTIndices.GET_OFFSET, getPointer(), offset); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IKeyEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IKeyEnumerator.java new file mode 100644 index 0000000000..765a2e04e4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IKeyEnumerator.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IKeyEnumerator extends IUnknownEx { + final IID IID_IKEY_ENUMERATOR = new IID("345FA92E-5E00-4319-9CAE-971F7601CDCF"); + + enum VTIndices implements VTableIndex { + RESET, // + GET_NEXT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT Reset(); + + HRESULT GetNext(BSTRByReference key, PointerByReference value, PointerByReference metadata); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IKeyStore.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IKeyStore.java new file mode 100644 index 0000000000..5229d6386a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IKeyStore.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IKeyStore extends IUnknownEx { + final IID IID_IKEY_STORE = new IID("0FC7557D-401D-4fca-9365-DA1E9850697C"); + + enum VTIndices implements VTableIndex { + GET_KEY, // + SET_KEY, // + GET_KEY_VALUE, // + SET_KEY_VALUE, // + CLEAR_KEYS, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetKey(WString key, PointerByReference object, PointerByReference metadata); + + HRESULT SetKey(WString key, Pointer object, Pointer metadata); + + HRESULT GetKeyValue(WString key, PointerByReference object, PointerByReference metadata); + + HRESULT SetKeyValue(WString key, Pointer object); + + HRESULT ClearKeys(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelIterator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelIterator.java new file mode 100644 index 0000000000..a071dafba9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelIterator.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IModelIterator extends IUnknownEx { + final IID IID_IMODEL_ITERATOR = new IID("E4622136-927D-4490-874F-581F3E4E3688"); + + enum VTIndices implements VTableIndex { + RESET, // + GET_NEXT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT Reset(); + + HRESULT GetNext(PointerByReference object, ULONGLONG ulDimensions, PointerByReference indexers, + PointerByReference metadata); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelKeyReference.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelKeyReference.java new file mode 100644 index 0000000000..9db835757b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelKeyReference.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IModelKeyReference extends IUnknownEx { + final IID IID_IMODEL_REFERENCE = new IID("5253DCF8-5AFF-4c62-B302-56A289E00998"); + + enum VTIndices implements VTableIndex { + GET_KEY_NAME, // + GET_ORIGINAL_OBJECT, // + GET_CONTAINING_OBJECT, // + GET_KEY, // + GET_KEY_VALUE, // + SET_KEY, // + SET_KEY_VALUE, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetKeyName(BSTRByReference keyName); + + HRESULT GetOriginalObject(PointerByReference originalObject); + + HRESULT GetContextObject(PointerByReference containingObject); + + HRESULT GetKey(PointerByReference object, PointerByReference metadata); + + HRESULT GetKeyValue(PointerByReference object, PointerByReference metadata); + + HRESULT SetKey(Pointer object, Pointer metadata); + + HRESULT SetKeyValue(Pointer object); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelKeyReference2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelKeyReference2.java new file mode 100644 index 0000000000..b393bf51ec --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelKeyReference2.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IModelKeyReference2 extends IModelKeyReference { + final IID IID_IMODEL_REFERENCE2 = new IID("80E2F7C5-7159-4e92-887E-7E0347E88406"); + + enum VTIndices2 implements VTableIndex { + OVERRIDE_CONTEXT_OBJECT, // + ; + + public int start = VTableIndex.follow(VTIndices.class); + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT OverrideContextObject(Pointer newContextObject); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelMethod.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelMethod.java new file mode 100644 index 0000000000..6771d349b7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelMethod.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IModelMethod extends IUnknownEx { + final IID IID_IMODEL_METHOD = new IID("80600C1F-B90B-4896-82AD-1C00207909E8"); + + enum VTIndices implements VTableIndex { + CALL, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT Call(Pointer pContextObject, ULONGLONG ulArgCount, Pointer[] ppArguments, + PointerByReference ppResult, PointerByReference ppMetadata); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelObject.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelObject.java new file mode 100644 index 0000000000..04a18f3ee5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelObject.java @@ -0,0 +1,150 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WTypes.VARTYPE; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION.ByReference; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IModelObject extends IUnknownEx { + final IID IID_IMODEL_OBJECT = new IID("E28C7893-3F4B-4b96-BACA-293CDC55F45D"); + + enum VTIndices implements VTableIndex { + GET_CONTEXT, // + GET_KIND, // + GET_INTRINSIC_VALUE, // + GET_INTRINSIC_VALUE_AS, // + GET_KEY_VALUE, // + SET_KEY_VALUE, // + ENUMERATE_KEY_VALUES, // + GET_RAW_VALUE, // + ENUMERATE_RAW_VALUES, // + DEREFERENCE, // + TRY_CAST_TO_RUNTIME_TYPE, // + GET_CONCEPT, // + GET_LOCATION, // + GET_TYPE_INFO, // + GET_TARGET_INFO, // + GET_NUMBER_OF_PARENT_MODELS, // + GET_PARENT_MODEL, // + ADD_PARENT_MODEL, // + REMOVE_PARENT_MODEL, // + GET_KEY, // + GET_KEY_REFERENCE, // + SET_KEY, // + CLEAR_KEYS, // + ENUMERATE_KEYS, // + ENUMERATE_KEY_REFERENCES, // + SET_CONCEPT, // + CLEAR_CONCEPTS, // + GET_RAW_REFERENCE, // + ENUMERATE_RAW_REFERENCES, // + SET_CONTEXT_FOR_DATA_MODEL, // + GET_CONTEXT_FOR_DATA_MODEL, // + COMPARE, // + IS_EQUAL_TO, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetContext(PointerByReference context); + + HRESULT GetKind(ULONGByReference kind); + + HRESULT GetIntrinsicValue(VARIANT.ByReference intrinsicData); + + HRESULT GetIntrinsicValueAs(VARTYPE vt, VARIANT.ByReference intrinsicData); + + HRESULT GetKeyValue(WString key, PointerByReference object, PointerByReference metadata); + + HRESULT SetKeyValue(WString key, Pointer object); + + HRESULT EnumerateKeyValues(PointerByReference enumerator); + + HRESULT GetRawValue(ULONG kind, WString name, ULONG searchFlags, PointerByReference object); // SymbolKind + + HRESULT EnumerateRawValues(ULONG kind, ULONG searchFlags, PointerByReference enumerator); // SymbolKind + + HRESULT Dereference(PointerByReference object); + + HRESULT TryCastToRuntimeType(PointerByReference runtimeTypedObject); + + HRESULT GetConcept(REFIID conceptId, PointerByReference conceptInterface, + PointerByReference conceptMetadata); + + HRESULT GetLocation(ByReference pLocation); + + HRESULT GetTypeInfo(PointerByReference type); + + HRESULT GetTargetInfo(LOCATION.ByReference location, PointerByReference type); + + HRESULT GetNumberOfParentModels(ULONGLONGByReference pulNumModels); + + HRESULT GetParentModel(ULONG i, PointerByReference model, PointerByReference contextObject); + + HRESULT AddParentModel(Pointer model, Pointer contextObject, BOOL override); + + HRESULT RemoveParentModel(Pointer model); + + HRESULT GetKey(WString key, PointerByReference object, PointerByReference metadata); + + HRESULT GetKeyReference(WString key, PointerByReference objectReference, + PointerByReference metadata); + + HRESULT SetKey(WString key, Pointer object, Pointer metadata); + + HRESULT ClearKeys(); + + HRESULT EnumerateKeys(PointerByReference enumerator); + + HRESULT EnumerateKeyReferences(PointerByReference enumerator); + + HRESULT SetConcept(REFIID conceptId, Pointer conceptInterface, Pointer conceptMetadata); + + HRESULT ClearConcepts(); + + HRESULT GetRawReference(ULONG kind, WString name, ULONG searchFlags, + PointerByReference object); // SymbolKind + + HRESULT EnumerateRawReferences(ULONGByReference kind, ULONG searchFlags, + PointerByReference enumerator); + + HRESULT SetContextForDataModel(Pointer dataModelObject, Pointer pContext); + + HRESULT GetContextForDataModel(Pointer dataModelObject, PointerByReference context); + + HRESULT Compare(Pointer other, BOOLByReference bEqual); + + HRESULT IsEqualTo(Pointer other, BOOLByReference equal); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelPropertyAccessor.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelPropertyAccessor.java new file mode 100644 index 0000000000..39d5cd6979 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IModelPropertyAccessor.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IModelPropertyAccessor extends IUnknownEx { + final IID IID_IMODEL_PROPERTY_ACCESSOR = new IID("5A0C63D9-0526-42b8-960C-9516A3254C85"); + + enum VTIndices implements VTableIndex { + GET_VALUE, // + SET_VALUE, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT GetValue(WString key, Pointer contextObject, PointerByReference value); + + HRESULT SetValue(WString key, Pointer contextObject, Pointer pValue); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IRawEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IRawEnumerator.java new file mode 100644 index 0000000000..68aebe2bb3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/IRawEnumerator.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.platform.win32.Guid.IID; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.IUnknownEx; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils.VTableIndex; + +public interface IRawEnumerator extends IUnknownEx { + final IID IID_IRAW_ENUMERATOR = new IID("E13613F9-3A3C-40b5-8F48-1E5EBFB9B21B"); + + enum VTIndices implements VTableIndex { + RESET, // + GET_NEXT, // + ; + + static int start = 3; + + @Override + public int getIndex() { + return this.ordinal() + start; + } + } + + HRESULT Reset(); + + HRESULT GetNext(BSTRByReference name, ULONGByReference kind, PointerByReference value); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIKeyEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIKeyEnumerator.java new file mode 100644 index 0000000000..a313afc7f4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIKeyEnumerator.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIKeyEnumerator extends UnknownWithUtils implements IKeyEnumerator { + public static class ByReference extends WrapIKeyEnumerator implements Structure.ByReference { + } + + public WrapIKeyEnumerator() { + } + + public WrapIKeyEnumerator(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT Reset() { + return _invokeHR(VTIndices.RESET, getPointer()); + } + + @Override + public HRESULT GetNext(BSTRByReference key, PointerByReference value, + PointerByReference metadata) { + return _invokeHR(VTIndices.GET_NEXT, getPointer(), key, value, metadata); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIKeyStore.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIKeyStore.java new file mode 100644 index 0000000000..c524edcfab --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIKeyStore.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIKeyStore extends UnknownWithUtils implements IKeyStore { + public static class ByReference extends WrapIKeyStore implements Structure.ByReference { + } + + public WrapIKeyStore() { + } + + public WrapIKeyStore(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetKey(WString key, PointerByReference object, PointerByReference metadata) { + return _invokeHR(VTIndices.GET_KEY, getPointer(), key, object, metadata); + } + + @Override + public HRESULT SetKey(WString key, Pointer object, Pointer metadata) { + return _invokeHR(VTIndices.SET_KEY, getPointer(), key, object, metadata); + } + + @Override + public HRESULT GetKeyValue(WString key, PointerByReference object, + PointerByReference metadata) { + return _invokeHR(VTIndices.GET_KEY_VALUE, getPointer(), key, object, metadata); + } + + @Override + public HRESULT SetKeyValue(WString key, Pointer object) { + return _invokeHR(VTIndices.SET_KEY_VALUE, getPointer(), key, object); + } + + @Override + public HRESULT ClearKeys() { + return _invokeHR(VTIndices.CLEAR_KEYS, getPointer()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelIterator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelIterator.java new file mode 100644 index 0000000000..a3ffe25e68 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelIterator.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIModelIterator extends UnknownWithUtils implements IModelIterator { + public static class ByReference extends WrapIModelIterator implements Structure.ByReference { + } + + public WrapIModelIterator() { + } + + public WrapIModelIterator(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT Reset() { + return _invokeHR(VTIndices.RESET, getPointer()); + } + + @Override + public HRESULT GetNext(PointerByReference object, ULONGLONG dimensions, + PointerByReference indexers, + PointerByReference metadata) { + return _invokeHR(VTIndices.GET_NEXT, getPointer(), object, dimensions, indexers, metadata); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelKeyReference1.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelKeyReference1.java new file mode 100644 index 0000000000..45409c4615 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelKeyReference1.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIModelKeyReference1 extends UnknownWithUtils implements IModelKeyReference { + public static class ByReference extends WrapIModelKeyReference1 + implements Structure.ByReference { + } + + public WrapIModelKeyReference1() { + } + + public WrapIModelKeyReference1(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetKeyName(BSTRByReference keyName) { + return _invokeHR(VTIndices.GET_KEY_NAME, getPointer(), keyName); + } + + @Override + public HRESULT GetOriginalObject(PointerByReference originalObject) { + return _invokeHR(VTIndices.GET_ORIGINAL_OBJECT, getPointer(), originalObject); + } + + @Override + public HRESULT GetContextObject(PointerByReference containingObject) { + return _invokeHR(VTIndices.GET_CONTAINING_OBJECT, getPointer(), containingObject); + } + + @Override + public HRESULT GetKey(PointerByReference object, PointerByReference metadata) { + return _invokeHR(VTIndices.GET_KEY, getPointer(), object, metadata); + } + + @Override + public HRESULT GetKeyValue(PointerByReference object, PointerByReference metadata) { + return _invokeHR(VTIndices.GET_KEY_VALUE, getPointer(), object, metadata); + } + + @Override + public HRESULT SetKey(Pointer object, Pointer metadata) { + return _invokeHR(VTIndices.SET_KEY, getPointer(), object, object, metadata); + } + + @Override + public HRESULT SetKeyValue(Pointer object) { + return _invokeHR(VTIndices.SET_KEY_VALUE, getPointer(), object); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelKeyReference2.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelKeyReference2.java new file mode 100644 index 0000000000..4d57681a54 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelKeyReference2.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public class WrapIModelKeyReference2 extends WrapIModelKeyReference1 + implements IModelKeyReference2 { + public static class ByReference extends WrapIModelKeyReference2 + implements Structure.ByReference { + } + + public WrapIModelKeyReference2() { + } + + public WrapIModelKeyReference2(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT OverrideContextObject(Pointer newContextObject) { + return _invokeHR(VTIndices2.OVERRIDE_CONTEXT_OBJECT, getPointer(), newContextObject); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelMethod.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelMethod.java new file mode 100644 index 0000000000..c8e2d281ee --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelMethod.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.ULONGLONG; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIModelMethod extends UnknownWithUtils implements IModelMethod { + public static class ByReference extends WrapIModelMethod implements Structure.ByReference { + } + + public WrapIModelMethod() { + } + + public WrapIModelMethod(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT Call(Pointer pContextObject, ULONGLONG argCount, Pointer[] ppArguments, + PointerByReference ppResult, PointerByReference ppMetadata) { + return _invokeHR(VTIndices.CALL, getPointer(), pContextObject, argCount, ppArguments, + ppResult, ppMetadata); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelObject.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelObject.java new file mode 100644 index 0000000000..455aa99bae --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelObject.java @@ -0,0 +1,220 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.Guid.REFIID; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WTypes.VARTYPE; +import com.sun.jna.platform.win32.WinDef.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.LOCATION; + +public class WrapIModelObject extends UnknownWithUtils implements IModelObject { + public static class ByReference extends WrapIModelObject implements Structure.ByReference { + } + + public WrapIModelObject() { + } + + public WrapIModelObject(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetContext(PointerByReference context) { + return _invokeHR(VTIndices.GET_CONTEXT, getPointer(), context); + } + + @Override + public HRESULT GetKind(ULONGByReference kind) { + return _invokeHR(VTIndices.GET_KIND, getPointer(), kind); + } + + @Override + public HRESULT GetIntrinsicValue(VARIANT.ByReference intrinsicData) { + return _invokeHR(VTIndices.GET_INTRINSIC_VALUE, getPointer(), intrinsicData); + } + + @Override + public HRESULT GetIntrinsicValueAs(VARTYPE vt, VARIANT.ByReference intrinsicData) { + return _invokeHR(VTIndices.GET_INTRINSIC_VALUE, getPointer(), vt, intrinsicData); + } + + @Override + public HRESULT GetKeyValue(WString key, PointerByReference object, + PointerByReference metadata) { + return _invokeHR(VTIndices.GET_KEY_VALUE, getPointer(), key, object, metadata); + } + + @Override + public HRESULT SetKeyValue(WString key, Pointer object) { + return _invokeHR(VTIndices.SET_KEY_VALUE, getPointer(), key, object); + } + + @Override + public HRESULT EnumerateKeyValues(PointerByReference enumerator) { + return _invokeHR(VTIndices.ENUMERATE_KEY_VALUES, getPointer(), enumerator); + } + + @Override + public HRESULT GetRawValue(ULONG kind, WString name, ULONG searchFlags, + PointerByReference object) { + return _invokeHR(VTIndices.GET_RAW_VALUE, getPointer(), kind, name, searchFlags, object); + } + + @Override + public HRESULT EnumerateRawValues(ULONG kind, ULONG searchFlags, + PointerByReference enumerator) { + return _invokeHR(VTIndices.ENUMERATE_RAW_VALUES, getPointer(), kind, searchFlags, + enumerator); + } + + @Override + public HRESULT Dereference(PointerByReference object) { + return _invokeHR(VTIndices.DEREFERENCE, getPointer(), object); + } + + @Override + public HRESULT TryCastToRuntimeType(PointerByReference runtimeTypedObject) { + return _invokeHR(VTIndices.TRY_CAST_TO_RUNTIME_TYPE, getPointer(), runtimeTypedObject); + } + + @Override + public HRESULT GetConcept(REFIID conceptId, PointerByReference conceptInterface, + PointerByReference conceptMetadata) { + return _invokeHR(VTIndices.GET_CONCEPT, getPointer(), conceptId, conceptInterface, + conceptMetadata); + } + + @Override + public HRESULT GetLocation(LOCATION.ByReference location) { + return _invokeHR(VTIndices.GET_LOCATION, getPointer(), location); + } + + @Override + public HRESULT GetTypeInfo(PointerByReference type) { + return _invokeHR(VTIndices.GET_TYPE_INFO, getPointer(), type); + } + + @Override + public HRESULT GetTargetInfo(LOCATION.ByReference location, PointerByReference type) { + return _invokeHR(VTIndices.GET_TARGET_INFO, getPointer(), location, type); + } + + @Override + public HRESULT GetNumberOfParentModels(ULONGLONGByReference numModels) { + return _invokeHR(VTIndices.GET_NUMBER_OF_PARENT_MODELS, getPointer(), numModels); + } + + @Override + public HRESULT GetParentModel(ULONG i, PointerByReference model, + PointerByReference contextObject) { + return _invokeHR(VTIndices.GET_PARENT_MODEL, getPointer(), i, model, contextObject); + } + + @Override + public HRESULT AddParentModel(Pointer model, Pointer contextObject, BOOL override) { + return _invokeHR(VTIndices.ADD_PARENT_MODEL, getPointer(), model, contextObject, override); + } + + @Override + public HRESULT RemoveParentModel(Pointer model) { + return _invokeHR(VTIndices.REMOVE_PARENT_MODEL, getPointer(), model); + } + + @Override + public HRESULT GetKey(WString key, PointerByReference object, PointerByReference metadata) { + return _invokeHR(VTIndices.GET_KEY, getPointer(), key, object, metadata); + } + + @Override + public HRESULT GetKeyReference(WString key, PointerByReference objectReference, + PointerByReference metadata) { + return _invokeHR(VTIndices.GET_KEY_REFERENCE, getPointer(), key, objectReference, metadata); + } + + @Override + public HRESULT SetKey(WString key, Pointer object, Pointer metadata) { + return _invokeHR(VTIndices.SET_KEY, getPointer(), key, object, metadata); + } + + @Override + public HRESULT ClearKeys() { + return _invokeHR(VTIndices.CLEAR_KEYS, getPointer()); + } + + @Override + public HRESULT EnumerateKeys(PointerByReference enumerator) { + return _invokeHR(VTIndices.ENUMERATE_KEYS, getPointer(), enumerator); + } + + @Override + public HRESULT EnumerateKeyReferences(PointerByReference enumerator) { + return _invokeHR(VTIndices.ENUMERATE_KEY_REFERENCES, getPointer(), enumerator); + } + + @Override + public HRESULT SetConcept(REFIID conceptId, Pointer conceptInterface, Pointer conceptMetadata) { + return _invokeHR(VTIndices.SET_CONCEPT, getPointer(), conceptId, conceptInterface, + conceptMetadata); + } + + @Override + public HRESULT ClearConcepts() { + return _invokeHR(VTIndices.CLEAR_CONCEPTS, getPointer()); + } + + @Override + public HRESULT GetRawReference(ULONG kind, WString name, ULONG searchFlags, + PointerByReference object) { + return _invokeHR(VTIndices.GET_RAW_REFERENCE, getPointer(), kind, name, searchFlags, + object); + } + + @Override + public HRESULT EnumerateRawReferences(ULONGByReference kind, ULONG searchFlags, + PointerByReference enumerator) { + return _invokeHR(VTIndices.ENUMERATE_RAW_REFERENCES, getPointer(), kind, searchFlags, + enumerator); + } + + @Override + public HRESULT SetContextForDataModel(Pointer dataModelObject, Pointer context) { + return _invokeHR(VTIndices.SET_CONTEXT_FOR_DATA_MODEL, getPointer(), dataModelObject, + context); + } + + @Override + public HRESULT GetContextForDataModel(Pointer dataModelObject, PointerByReference context) { + return _invokeHR(VTIndices.GET_CONTEXT_FOR_DATA_MODEL, getPointer(), dataModelObject, + context); + } + + @Override + public HRESULT Compare(Pointer other, BOOLByReference ppResult) { + return _invokeHR(VTIndices.COMPARE, getPointer(), other, ppResult); + } + + @Override + public HRESULT IsEqualTo(Pointer other, BOOLByReference equal) { + return _invokeHR(VTIndices.IS_EQUAL_TO, getPointer(), other, equal); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelPropertyAccessor.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelPropertyAccessor.java new file mode 100644 index 0000000000..cee541f160 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIModelPropertyAccessor.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIModelPropertyAccessor extends UnknownWithUtils implements IModelPropertyAccessor { + public static class ByReference extends WrapIModelPropertyAccessor + implements Structure.ByReference { + } + + public WrapIModelPropertyAccessor() { + } + + public WrapIModelPropertyAccessor(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT GetValue(WString key, Pointer contextObject, PointerByReference value) { + return _invokeHR(VTIndices.GET_VALUE, getPointer(), key, contextObject, value); + } + + @Override + public HRESULT SetValue(WString key, Pointer contextObject, Pointer value) { + return _invokeHR(VTIndices.SET_VALUE, getPointer(), key, contextObject, value); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIRawEnumerator.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIRawEnumerator.java new file mode 100644 index 0000000000..7ba3c77dc9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/dbgmodel/main/WrapIRawEnumerator.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.dbgmodel.main; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WTypes.BSTRByReference; +import com.sun.jna.platform.win32.WinDef.ULONGByReference; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.PointerByReference; + +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; + +public class WrapIRawEnumerator extends UnknownWithUtils implements IRawEnumerator { + public static class ByReference extends WrapIRawEnumerator implements Structure.ByReference { + } + + public WrapIRawEnumerator() { + } + + public WrapIRawEnumerator(Pointer pvInstance) { + super(pvInstance); + } + + @Override + public HRESULT Reset() { + return _invokeHR(VTIndices.RESET, getPointer()); + } + + @Override + public HRESULT GetNext(BSTRByReference name, ULONGByReference kind, PointerByReference value) { + return _invokeHR(VTIndices.GET_NEXT, getPointer(), name, kind, value); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/javaprovider/JavaProviderNative.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/javaprovider/JavaProviderNative.java new file mode 100644 index 0000000000..25f551e58e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/jna/javaprovider/JavaProviderNative.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.jna.javaprovider; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +public interface JavaProviderNative extends Library { + JavaProviderNative INSTANCE = Native.loadLibrary("javaprovider", JavaProviderNative.class); + + HRESULT createClient(Pointer client); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/manager/DbgManager2Impl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/manager/DbgManager2Impl.java new file mode 100644 index 0000000000..366f4ab85b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/manager/DbgManager2Impl.java @@ -0,0 +1,161 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.manager; + +import static ghidra.async.AsyncUtils.*; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.manager.DbgCause; +import agent.dbgeng.manager.DbgCause.Causes; +import agent.dbgeng.manager.DbgState; +import agent.dbgeng.manager.impl.*; +import agent.dbgmodel.dbgmodel.DbgModel; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.gadp.impl.DbgModelClientThreadExecutor; +import agent.dbgmodel.gadp.impl.WrappedDbgModel; +import agent.dbgmodel.jna.cmd.*; +import agent.dbgmodel.model.impl.DbgModel2TargetObjectImpl; +import ghidra.async.TypeSpec; +import ghidra.dbg.target.TargetObject; +import ghidra.util.Msg; + +public class DbgManager2Impl extends DbgManagerImpl { + + private DebugClient client; + private WrappedDbgModel dbgmodel; + + /** + * Instantiate a new manager + */ + public DbgManager2Impl() { + super(); + } + + public WrappedDbgModel getAccess() { + return dbgmodel; + } + + @Override + public DebugClient getClient() { + return client; + } + + @Override + public CompletableFuture start(String[] args) { + state.set(DbgState.STARTING, Causes.UNCLAIMED); + boolean create = true; + if (args.length == 0) { + engThread = new DbgModelClientThreadExecutor(() -> DbgModel.debugCreate()); + } + else { + String remoteOptions = String.join(" ", args); + engThread = + new DbgModelClientThreadExecutor(() -> DbgModel.debugConnect(remoteOptions)); + create = false; + } + engThread.setManager(this); + AtomicReference creat = new AtomicReference<>(create); + return sequence(TypeSpec.VOID).then(engThread, (seq) -> { + doExecute(creat.get()); + seq.exit(); + }).finish().exceptionally((exc) -> { + Msg.error(this, "start failed"); + return null; + }); + } + + @Override + protected void doExecute(Boolean create) { + dbgmodel = ((DbgModelClientThreadExecutor) engThread).getAccess(); + reentrantClient = dbgmodel.getClient(); + client = super.getClient(); + super.doExecute(create); + } + + public CompletableFuture> listElements(List path, + DbgModel2TargetObjectImpl targetObject) { + return execute(new DbgListElementsCommand(this, path, targetObject)); + } + + public CompletableFuture> listAttributes(List path, + DbgModel2TargetObjectImpl targetObject) { + return execute(new DbgListAttributesCommand(this, path, targetObject)); + } + + public CompletableFuture applyMethods(List path, + DbgModel2TargetObjectImpl targetObject) { + return execute(new DbgApplyMethodsCommand(this, path, targetObject)); + } + + public CompletableFuture> getRegisterMap(List path) { + return execute(new DbgGetRegisterMapCommand(this, path)); + } + + @Override + public DbgSessionImpl getCurrentSession() { + ModelObject currentSession = dbgmodel.getUtil().getCurrentSession(); + ModelObject keyValue = currentSession.getKeyValue("Id"); + Object val = keyValue.getValue(); + DebugSessionId sid = new DebugSessionId((Integer) val); + DbgSessionImpl session = sessions.get(sid); + if (session != null) { + curSession = session; + return session; + } + session = new DbgSessionImpl(this, sid); + addSession(session, DbgCause.Causes.UNCLAIMED); + curSession = session; + return session; + } + + @Override + public DbgProcessImpl getCurrentProcess() { + ModelObject currentProcess = dbgmodel.getUtil().getCurrentProcess(); + ModelObject keyValue = currentProcess.getKeyValue("Id"); + Long val = (Long) keyValue.getValue(); + DebugProcessId id = getSystemObjects().getCurrentProcessId(); + DbgProcessImpl process = processes.get(id); + if (process != null) { + curProcess = process; + return process; + } + process = new DbgProcessImpl(this, id, val); + addProcess(process, DbgCause.Causes.UNCLAIMED); + curProcess = process; + return process; + } + + @Override + public DbgThreadImpl getCurrentThread() { + ModelObject currentThread = dbgmodel.getUtil().getCurrentThread(); + ModelObject keyValue = currentThread.getKeyValue("Id"); + Long val = (Long) keyValue.getValue(); + DebugThreadId id = getSystemObjects().getCurrentThreadId(); + DbgThreadImpl thread = threads.get(id); + if (thread != null) { + return thread; + } + thread = new DbgThreadImpl(this, curProcess, id, val); + addThread(thread); + return thread; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2DefaultTargetModelRoot.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2DefaultTargetModelRoot.java new file mode 100644 index 0000000000..bcc0f3ddd6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2DefaultTargetModelRoot.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.model.impl; + +import ghidra.dbg.target.TargetAggregate; + +public class DbgModel2DefaultTargetModelRoot extends DbgModel2TargetObjectImpl + implements TargetAggregate { + + public DbgModel2DefaultTargetModelRoot(DbgModel2Impl model, String typeHint) { + super(model, null, null, typeHint); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2Impl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2Impl.java new file mode 100644 index 0000000000..02b2de01aa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2Impl.java @@ -0,0 +1,105 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.model.impl; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.AbstractDbgModel; +import agent.dbgeng.model.iface2.DbgModelTargetSession; +import agent.dbgmodel.manager.DbgManager2Impl; +import ghidra.dbg.target.TargetObject; +import ghidra.program.model.address.*; + +public class DbgModel2Impl extends AbstractDbgModel { + // TODO: Need some minimal memory modeling per architecture on the model/agent side. + // The model must convert to and from Ghidra's address space names + protected static final String SPACE_NAME = "ram"; + + // Don't make this static, so each model has a unique "GDB" space + protected final AddressSpace space = + new GenericAddressSpace(SPACE_NAME, 64, AddressSpace.TYPE_RAM, 0); + protected final AddressFactory addressFactory = + new DefaultAddressFactory(new AddressSpace[] { space }); + + protected final DbgManager2Impl dbg; + protected final DbgModel2TargetRootImpl root; + protected DbgModelTargetSession session; + + protected final CompletableFuture completedRoot; + + public DbgModel2Impl() { + this.dbg = new DbgManager2Impl(); + this.root = new DbgModel2TargetRootImpl(this); + this.completedRoot = CompletableFuture.completedFuture(root); + } + + @Override + public AddressSpace getAddressSpace(String name) { + if (!SPACE_NAME.equals(name)) { + return null; + } + return space; + } + + // TODO: Place make this a model method? + @Override + public AddressFactory getAddressFactory() { + return addressFactory; + } + + @Override + public CompletableFuture startDbgEng(String[] args) { + return dbg.start(args); + } + + @Override + public boolean isRunning() { + return dbg.isRunning(); + } + + @Override + public void terminate() throws IOException { + dbg.terminate(); + } + + @Override + public CompletableFuture fetchModelRoot() { + return completedRoot; + } + + @Override + public DbgManagerImpl getManager() { + return dbg; + } + + @Override + public CompletableFuture close() { + try { + terminate(); + return CompletableFuture.completedFuture(null); + } + catch (Throwable t) { + return CompletableFuture.failedFuture(t); + } + } + + @Override + public DbgModelTargetSession getSession() { + return session; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetAvailableContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetAvailableContainerImpl.java new file mode 100644 index 0000000000..cf284e189b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetAvailableContainerImpl.java @@ -0,0 +1,71 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.Pair; + +import agent.dbgeng.model.iface2.*; +import ghidra.dbg.target.TargetObject; +import ghidra.util.datastruct.WeakValueHashMap; + +public class DbgModel2TargetAvailableContainerImpl extends DbgModel2TargetObjectImpl + implements DbgModelTargetAvailableContainer { + + protected final Map attachablesById = + new WeakValueHashMap<>(); + + public DbgModel2TargetAvailableContainerImpl(DbgModelTargetObject obj) { + super(obj.getModel(), obj, "Available", "AvailableContainer"); + changeAttributes(List.of(), List.of(), Map.of( // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.SOLICITED // + ), "Initialized"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listAvailableProcesses().thenAccept(list -> { + List available; + synchronized (this) { + // NOTE: If more details added to entries, should clear attachablesById + available = + list.stream().map(this::getTargetAttachableEx).collect(Collectors.toList()); + } + setElements(available, Map.of(), "Refreshed"); + }); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + Map nmap = new HashMap<>(); + return addModelObjectAttributes(nmap); + } + + public synchronized DbgModelTargetAvailable getTargetAttachableEx(Pair pair) { + return attachablesById.computeIfAbsent(pair.getLeft(), + i -> new DbgModel2TargetAvailableImpl(this, pair.getLeft(), pair.getRight())); + } + + @Override + public synchronized DbgModelTargetAvailable getTargetAttachable(int pid) { + return attachablesById.computeIfAbsent(pid, + i -> new DbgModel2TargetAvailableImpl(this, pid)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetAvailableImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetAvailableImpl.java new file mode 100644 index 0000000000..b8fafb9196 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetAvailableImpl.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.dbgeng.model.iface2.DbgModelTargetAvailable; +import agent.dbgeng.model.iface2.DbgModelTargetAvailableContainer; +import ghidra.dbg.util.PathUtils; + +public class DbgModel2TargetAvailableImpl extends DbgModel2TargetObjectImpl + implements DbgModelTargetAvailable { + + protected static final String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid"; + // TODO: DESCRIPTION, TYPE, USER? + + protected static String indexAttachable(int pid) { + return Integer.toHexString(pid); + } + + protected static String keyAttachable(int pid) { + return PathUtils.makeKey(indexAttachable(pid)); + } + + protected final int pid; + private String name; + + public DbgModel2TargetAvailableImpl(DbgModelTargetAvailableContainer parent, int pid, + String name) { + super(parent.getModel(), parent, keyAttachable(pid), name); + this.pid = pid; + this.name = name; + + this.changeAttributes(List.of(), List.of(), Map.of(// + PID_ATTRIBUTE_NAME, pid, // + DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid) + " : " + name.trim() // + ), "Initialized"); + } + + public DbgModel2TargetAvailableImpl(DbgModelTargetAvailableContainer parent, int pid) { + super(parent.getModel(), parent, keyAttachable(pid), "Attachable"); + this.pid = pid; + + this.changeAttributes(List.of(), List.of(), Map.of(// + PID_ATTRIBUTE_NAME, pid, // + DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid) // + ), "Initialized"); + } + + @Override + public long getPid() { + return pid; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetObjectImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetObjectImpl.java new file mode 100644 index 0000000000..8f59e0b632 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetObjectImpl.java @@ -0,0 +1,375 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.dbgeng.manager.DbgEventsListener; +import agent.dbgeng.manager.DbgStateListener; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.model.AbstractDbgModel; +import agent.dbgeng.model.iface1.DbgModelSelectableObject; +import agent.dbgeng.model.iface2.*; +import agent.dbgeng.model.impl.DbgModelTargetMemoryContainerImpl; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ModelObjectKind; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.TypeKind; +import agent.dbgmodel.manager.DbgManager2Impl; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibility; +import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.dbg.util.PathUtils; +import ghidra.dbg.util.PathUtils.TargetObjectKeyComparator; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +public class DbgModel2TargetObjectImpl extends DefaultTargetObject + implements DbgModelTargetObject { + + protected final Map elementsByKey = new WeakValueHashMap<>(); + + protected DbgModelSelectableObject focus; + public TargetAccessibility accessibility = TargetAccessibility.ACCESSIBLE; + + private ModelObject modelObject = null; + protected Map intrinsics = new TreeMap<>(TargetObjectKeyComparator.ATTRIBUTE); + + protected String bptId; + protected static Map bptInfoMap = new HashMap<>(); + + protected String DBG_PROMPT = "(kd2)"; // Used by DbgModelTargetEnvironment + + protected static String indexObject(ModelObject obj) { + return obj.getSearchKey(); + } + + public static String keyObject(ModelObject obj) { + return PathUtils.makeKey(indexObject(obj)); + } + + protected static String getHintForObject(ModelObject obj) { + TypeKind typeKind = obj.getTypeKind(); + return typeKind == null ? "" : typeKind.name(); + } + + public DbgModel2TargetObjectImpl(AbstractDbgModel model, TargetObject parent, String name, + String typeHint) { + super(model, parent, name, typeHint); + } + + @Override + public DbgModel2Impl getModel() { + return (DbgModel2Impl) super.getModel(); + } + + public CompletableFuture> requestNativeElements() { + DbgManager2Impl manager2 = (DbgManager2Impl) getManager(); + List pathX = PathUtils.extend(List.of("Debugger"), path); + return manager2.listElements(pathX, this); + } + + @Override + public CompletableFuture> requestNativeAttributes() { + DbgManager2Impl manager2 = (DbgManager2Impl) getManager(); + List pathX = PathUtils.extend(List.of("Debugger"), path); + return manager2.listAttributes(pathX, this); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + List nlist = new ArrayList<>(); + return requestNativeElements().thenCompose(list -> { + for (TargetObject element : elements.values()) { + if (!list.contains(element)) { + if (element instanceof DbgStateListener) { + getManager().removeStateListener((DbgStateListener) element); + } + if (element instanceof DbgEventsListener) { + getManager().removeEventsListener((DbgEventsListener) element); + } + } + } + nlist.addAll(list); + int order = 0; + for (TargetObject targetObject : nlist) { + DbgModelTargetObject to = (DbgModelTargetObject) targetObject; + to.changeAttributes(List.of(), Map.of( // + ORDER_ATTRIBUTE_NAME, order++ // + ), "Initialized"); + } + return processModelObjectElements(nlist); + }).thenAccept(__ -> { + setElements(nlist, Map.of(), "Refreshed"); + }); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + Map nmap = new HashMap<>(); + return requestNativeAttributes().thenCompose(map -> { + if (map != null) { + Collection values = map.values(); + for (Object attribute : attributes.values()) { + if (!values.contains(attribute)) { + if (attribute instanceof DbgStateListener) { + getManager().removeStateListener((DbgStateListener) attribute); + } + if (attribute instanceof DbgEventsListener) { + getManager().removeEventsListener((DbgEventsListener) attribute); + } + } + } + nmap.putAll(map); + } + return addModelObjectAttributes(nmap); + }).thenAccept(__ -> { + setAttributes(List.of(), nmap, "Refreshed"); + }); + } + + protected CompletableFuture processModelObjectElements(List list) { + List> futures = + list.stream().map(to -> processElement(to)).collect(Collectors.toList()); + CompletableFuture allOf = + CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])); + return allOf; + } + + private CompletableFuture processElement(TargetObject targetObject) { + if (targetObject instanceof DbgModelTargetObject) { + DbgModelTargetObject proxy = (DbgModelTargetObject) targetObject; + DelegateDbgModel2TargetObject delegate = + DelegateDbgModel2TargetObject.getDelegate(proxy); + if (proxy instanceof TargetStackFrame || // + proxy instanceof TargetModule || // + proxy instanceof TargetBreakpointSpec) { + return delegate.requestAttributes(true); + } + } + return CompletableFuture.completedFuture(null); + } + + protected CompletableFuture addModelObjectAttributes(Map attrs) { + if (modelObject == null) { + return CompletableFuture.completedFuture(null); + } + String key = modelObject.getSearchKey(); + ModelObjectKind kind = modelObject.getKind(); + TypeKind tk = modelObject.getTypeKind(); + String value = modelObject.getValueString(); + + attrs.put(DISPLAY_ATTRIBUTE_NAME, key); + attrs.put(UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.UNSOLICITED); + if (kind != null) { + attrs.put(KIND_ATTRIBUTE_NAME, kind.toString()); + } + if (tk != null) { + attrs.put(TYPE_ATTRIBUTE_NAME, tk.toString()); + } + if (value != null && !value.equals("")) { + attrs.put(VALUE_ATTRIBUTE_NAME, value); + if (!kind.equals(ModelObjectKind.OBJECT_PROPERTY_ACCESSOR)) { + String oldval = (String) attributes.get(DISPLAY_ATTRIBUTE_NAME); + String newval = getName() + " : " + value; + attrs.put(DISPLAY_ATTRIBUTE_NAME, newval); + setModified(attrs, !newval.equals(oldval)); + } + if (tk == null) { + Object val = modelObject.getIntrinsicValue(); + if (val != null) { + attrs.put(TYPE_ATTRIBUTE_NAME, val.getClass().getSimpleName()); + } + } + } + if (this instanceof DelegateDbgModel2TargetObject) { + DelegateDbgModel2TargetObject delegate = (DelegateDbgModel2TargetObject) this; + TargetObject proxy = delegate.getProxy(); + if (proxy instanceof TargetExecutionStateful) { + TargetExecutionStateful stateful = (TargetExecutionStateful) proxy; + TargetExecutionState state = stateful.getExecutionState(); + attrs.put(TargetExecutionStateful.STATE_ATTRIBUTE_NAME, state); + } + if (proxy instanceof TargetInterpreter) { + attrs.put(TargetInterpreter.PROMPT_ATTRIBUTE_NAME, DBG_PROMPT); + } + if (proxy instanceof TargetBreakpointContainer) { + attrs.put(TargetBreakpointContainer.SUPPORTED_BREAK_KINDS_ATTRIBUTE_NAME, + TargetBreakpointKindSet.of(TargetBreakpointKind.values())); + } + if (proxy instanceof TargetBreakpointSpec) { + DbgModelTargetBreakpointSpec spec = (DbgModelTargetBreakpointSpec) proxy; + return spec.init(attrs); + } + if (proxy instanceof TargetEnvironment) { + attrs.put(TargetEnvironment.ARCH_ATTRIBUTE_NAME, "x86_64"); + attrs.put(TargetEnvironment.DEBUGGER_ATTRIBUTE_NAME, "dbgeng"); + attrs.put(TargetEnvironment.OS_ATTRIBUTE_NAME, "Windows"); + } + if (proxy instanceof TargetModule) { + DbgModelTargetModule module = (DbgModelTargetModule) proxy; + return module.init(attrs); + } + if (proxy instanceof TargetProcess) { + DbgModelTargetMemoryContainer memory; + if (attributes.containsKey("Memory")) { + memory = (DbgModelTargetMemoryContainer) attributes.get("Memory"); + } + else { + memory = new DbgModelTargetMemoryContainerImpl((DbgModelTargetProcess) proxy); + } + attrs.put(memory.getName(), memory); + } + if (proxy instanceof TargetThread) { + DbgModelTargetThread targetThread = (DbgModelTargetThread) proxy; + String executionType = + targetThread.getThread().getExecutingProcessorType().description; + attrs.put(TargetEnvironment.ARCH_ATTRIBUTE_NAME, executionType); + } + if (proxy instanceof TargetRegisterBank) { + attrs.put(TargetRegisterBank.DESCRIPTIONS_ATTRIBUTE_NAME, this); + } + if (proxy instanceof TargetStackFrame) { + DbgModelTargetStackFrame frame = (DbgModelTargetStackFrame) proxy; + return frame.init(attrs); + } + if (proxy instanceof DbgModelTargetTTD) { + DbgModelTargetTTD ttd = (DbgModelTargetTTD) proxy; + return ttd.init(attrs); + } + } + + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture fetchChild(final String key) { + if (key.startsWith("[") && key.endsWith("]")) { + String trimKey = key.substring(1, key.length() - 1); + if (elements.containsKey(trimKey)) { + return CompletableFuture.completedFuture(elements.get(trimKey)); + } + return requestElements(true).thenApply(__ -> getCachedElements().get(trimKey)); + } + if (attributes.containsKey(key)) { + return CompletableFuture.completedFuture(attributes.get(key)); + } + if (key.endsWith(")")) { + DbgManager2Impl manager2 = (DbgManager2Impl) getManager(); + List pathX = PathUtils.extend(List.of("Debugger"), path); + pathX = PathUtils.extend(pathX, key); + return manager2.applyMethods(pathX, this).thenApply(obj -> { + changeAttributes(List.of(), List.of(), Map.of( // + key, obj // + ), "Initialized"); + return obj; + }); + } + return requestAttributes(true).thenApply(__ -> getCachedAttribute(key)); + } + + //@Override + //public TargetAccessibility getAccessibility() { + // return accessibility; + //} + + public DbgModelSelectableObject getFocus() { + return focus; + } + + public Map getIntrinsics() { + return intrinsics; + } + + public void setModelObject(ModelObject modelObject) { + this.modelObject = modelObject; + Map attrs = new HashMap<>(); + addModelObjectAttributes(attrs).thenAccept(__ -> { + if (!attrs.isEmpty()) { + changeAttributes(List.of(), List.of(), attrs, "Refreshed"); + } + }).exceptionally(ex -> { + Msg.error(this, "Problem setting model object" + PathUtils.toString(getPath()) + ": ", + ex); + return null; + }); + } + + @Override + public void removeListener(TargetObjectListener l) { + listeners.clear(); + } + + @Override + public DbgModelTargetSession getParentSession() { + if (this instanceof DbgModelTargetSession) { + return (DbgModelTargetSession) this; + } + DbgModelTargetObject test = (DbgModelTargetObject) parent; + while (test != null && !(test.getProxy() instanceof DbgModelTargetSession)) { + test = (DbgModelTargetObject) test.getImplParent(); + } + return test == null ? null : (DbgModelTargetSession) test.getProxy(); + } + + @Override + public DbgModelTargetProcess getParentProcess() { + DbgModelTargetObject test = (DbgModelTargetObject) parent; + while (test != null && !(test.getProxy() instanceof TargetProcess)) { + test = (DbgModelTargetObject) test.getImplParent(); + } + return test == null ? null : (DbgModelTargetProcess) test.getProxy(); + } + + @Override + public DbgModelTargetThread getParentThread() { + DbgModelTargetObject test = (DbgModelTargetObject) parent; + while (test != null && !(test.getProxy() instanceof TargetThread)) { + test = (DbgModelTargetObject) test.getImplParent(); + } + return test == null ? null : (DbgModelTargetThread) test.getProxy(); + } + + @Override + public void setModified(Map attrs, boolean modified) { + if (modified) { + attrs.put(MODIFIED_ATTRIBUTE_NAME, modified); + listeners.fire.displayChanged(this, getDisplay()); + } + } + + @Override + public void setModified(boolean modified) { + if (modified) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, modified // + ), "Refreshed"); + listeners.fire.displayChanged(this, getDisplay()); + } + } + + @Override + public void resetModified() { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, false // + ), "Refreshed"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetRootImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetRootImpl.java new file mode 100644 index 0000000000..2f8024a479 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetRootImpl.java @@ -0,0 +1,483 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.manager.reason.*; +import agent.dbgeng.model.iface1.DbgModelSelectableObject; +import agent.dbgeng.model.iface1.DbgModelTargetExecutionStateful; +import agent.dbgeng.model.iface2.*; +import agent.dbgeng.model.impl.DbgModelTargetConnectorContainerImpl; +import agent.dbgmodel.manager.DbgManager2Impl; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointListener; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.dbg.util.PathUtils; +import ghidra.util.Msg; + +public class DbgModel2TargetRootImpl extends DbgModel2DefaultTargetModelRoot + implements DbgModelTargetRoot { + + protected final DbgModel2Impl impl; + + protected final DbgModel2TargetAvailableContainerImpl available; + protected final DbgModelTargetConnectorContainerImpl connectors; + protected final DbgModel2TargetSystemMarkerImpl systemMarker; + + public DbgModel2TargetRootImpl(DbgModel2Impl impl) { + super(impl, "Debugger"); + this.impl = impl; + + this.available = new DbgModel2TargetAvailableContainerImpl(this); + this.connectors = new DbgModelTargetConnectorContainerImpl(this); + this.systemMarker = new DbgModel2TargetSystemMarkerImpl(this); + + DbgModelTargetConnector defaultConnector = connectors.getDefaultConnector(); + changeAttributes(List.of(), List.of( // + available, // + connectors, // + systemMarker // + ), Map.of( // + DISPLAY_ATTRIBUTE_NAME, "Debugger", // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, defaultConnector.getParameters() // + // ARCH_ATTRIBUTE_NAME, "x86_64", // + // DEBUGGER_ATTRIBUTE_NAME, "dbgeng", // + // OS_ATTRIBUTE_NAME, "Windows", // + ), "Initialized"); + impl.getManager().addEventsListener(this); + } + + @Override + public boolean setFocus(DbgModelSelectableObject sel) { + boolean doFire; + synchronized (this) { + doFire = !Objects.equals(this.focus, sel); + this.focus = sel; + } + if (doFire) { + changeAttributes(List.of(), List.of(), Map.of( // + TargetFocusScope.FOCUS_ATTRIBUTE_NAME, focus // + ), "Focus changed"); + intrinsics.put(TargetFocusScope.FOCUS_ATTRIBUTE_NAME, focus); + DbgModelTargetSession session = focus.getParentSession(); + session.select(); + listeners.fire(TargetFocusScopeListener.class).focusChanged(this, sel); + } + return doFire; + } + + @Override + public void setDefaultConnector(DbgModelTargetConnector defaultConnector) { + changeAttributes(List.of(), List.of(), + Map.of(TargetMethod.PARAMETERS_ATTRIBUTE_NAME, defaultConnector.getParameters()), + "Default connector changed"); + } + + @Override + public void sessionSelected(DbgSession session, DbgCause cause) { + objectSelected(session); + } + + @Override + public void processSelected(DbgProcess process, DbgCause cause) { + objectSelected(process); + } + + @Override + public void threadSelected(DbgThread thread, DbgStackFrame frame, DbgCause cause) { + objectSelected(thread); + if (frame != null) { + objectSelected(frame); + } + } + + public void objectSelected(Object object) { + List objPath = findObject(object); + getModel().fetchModelValue(objPath, true).thenAccept(obj -> { + if (obj instanceof DbgModelSelectableObject) { + setFocus((DbgModelSelectableObject) obj); + } + }).exceptionally(ex -> { + Msg.error("Could not set focus on selected object: " + PathUtils.toString(objPath), ex); + return null; + }); + } + + @Override + public void sessionAdded(DbgSession session, DbgCause cause) { + changeAttributes(List.of(), List.of( // + new DbgModel2TargetSystemMarkerImpl(this) // + ), Map.of(), "System"); + getObject(session); + } + + @Override + public void processAdded(DbgProcess proc, DbgCause cause) { + stateChanged(proc, DbgState.STARTING, "ProcessAdded"); + getObject(proc).thenAccept(obj -> { + DbgModelTargetProcess process = (DbgModelTargetProcess) obj; + if (process == null) { + return; + } + getListeners().fire(TargetEventScopeListener.class) + .event( + this, null, TargetEventType.PROCESS_CREATED, "Process " + proc.getId() + + " started " + "notepad.exe" + " pid=" + proc.getPid(), + List.of(process)); + }); + } + + @Override + public void threadCreated(DbgThread thread, DbgCause cause) { + stateChanged(thread, DbgState.STARTING, "ThreadCreated"); + getObject(thread).thenAccept(obj -> { + DbgModelTargetThread targetThread = (DbgModelTargetThread) obj; + if (targetThread == null) { + return; + } + getListeners().fire(TargetEventScopeListener.class) + .event(this, targetThread, TargetEventType.THREAD_CREATED, + "Thread " + thread.getId() + " started", List.of(targetThread)); + }); + } + + @Override + public void moduleLoaded(DbgProcess proc, String name, DbgCause cause) { + getObject(proc, List.of("Modules"), name).thenAccept(obj -> { + DbgModelTargetModule mod = (DbgModelTargetModule) obj; + if (mod == null) { + return; + } + getListeners().fire(TargetEventScopeListener.class) + .event(this, null, TargetEventType.MODULE_LOADED, "Library " + name + " loaded", + List.of(mod)); + }); + } + + @Override + public void moduleUnloaded(DbgProcess proc, String name, DbgCause cause) { + getObject(proc, List.of("Modules"), name).thenAccept(obj -> { + DbgModelTargetModule mod = (DbgModelTargetModule) obj; + if (mod == null) { + return; + } + getListeners().fire(TargetEventScopeListener.class) + .event(this, null, TargetEventType.MODULE_UNLOADED, + "Library " + name + " unloaded", List.of(mod)); + }); + } + + private CompletableFuture getObject(Object object) { + List objPath = findObject(object); + if (objPath == null) { + return CompletableFuture.completedFuture(null); + } + // NB: fetchModelObject uses overriden DbgModel2TargetObjectImpl::fetchChild + // which forces refresh for empty containers + return AsyncUtils.sequence(TypeSpec.cls(DbgModelTargetObject.class)).then(seq -> { + getModel().fetchModelObject(objPath).handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((pobj, seq) -> { + DbgModelTargetObject pimpl = (DbgModelTargetObject) pobj; + seq.exit(pimpl); + }).finish(); + } + + private CompletableFuture getObject(Object object, List ext, + String name) { + List objPath = findObject(object); + if (objPath == null) { + return CompletableFuture.completedFuture(null); + } + List xpath = new ArrayList<>(); + xpath.addAll(objPath); + xpath.addAll(ext); + return AsyncUtils.sequence(TypeSpec.cls(DbgModelTargetObject.class)).then(seq -> { + getModel().fetchModelObject(xpath).handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((pobj, seq) -> { + if (pobj == null) { + seq.exit(); + return; + } + DbgModelTargetObject proxy = (DbgModelTargetObject) pobj; + DelegateDbgModel2TargetObject delegate = + DelegateDbgModel2TargetObject.getDelegate(proxy); + delegate.requestElements(true).thenAccept(__ -> { + Map cachedElements = delegate.getCachedElements(); + for (TargetObject val : cachedElements.values()) { + DbgModelTargetObject obj = (DbgModelTargetObject) val; + if (obj.getDisplay().contains(name)) { + seq.exit(obj); + } + } + seq.exit((DbgModelTargetObject) null); + }); + }).finish(); + } + + @Override + public void sessionRemoved(DebugSessionId sessionId, DbgCause cause) { + getObject(sessionId); + } + + @Override + public void processRemoved(DebugProcessId processId, DbgCause cause) { + getObject(processId).thenAccept(obj -> { + DbgModelTargetProcess process = obj.as(DbgModelTargetProcess.class); + if (process == null) { + return; + } + DbgProcess proc = process.getProcess(); + getListeners().fire(TargetEventScopeListener.class) + .event(this, null, TargetEventType.PROCESS_EXITED, + "Process " + proc.getId() + " exited code=" + proc.getExitCode(), + List.of(process)); + }); + } + + @Override + public void threadExited(DebugThreadId threadId, DbgProcess process, DbgCause cause) { + getObject(threadId).thenAccept(obj -> { + DbgModelTargetThread targetThread = obj.as(DbgModelTargetThread.class); + if (targetThread == null) { + return; + } + getListeners().fire(TargetEventScopeListener.class) + .event(this, targetThread, TargetEventType.THREAD_EXITED, + "Thread " + threadId + " exited", List.of(targetThread)); + }); + } + + @Override + public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause, + DbgReason reason) { + stateChanged(thread, state, reason.desc()).thenAccept(obj -> { + DbgModelTargetThread targetThread = (DbgModelTargetThread) obj; + if (targetThread == null) { + return; + } + DbgProcess process = thread.getProcess(); + changeAttributes(List.of(), List.of(), Map.of( // + TargetEventScope.EVENT_PROCESS_ATTRIBUTE_NAME, Long.toHexString(process.getPid()), // + TargetEventScope.EVENT_THREAD_ATTRIBUTE_NAME, Long.toHexString(thread.getTid()) // + ), reason.desc()); + intrinsics.put(TargetEventScope.EVENT_PROCESS_ATTRIBUTE_NAME, + Long.toHexString(process.getPid())); + intrinsics.put(TargetEventScope.EVENT_THREAD_ATTRIBUTE_NAME, + Long.toHexString(thread.getTid())); + TargetEventType eventType = getEventType(state, cause, reason); + getListeners().fire(TargetEventScopeListener.class) + .event(this, targetThread, eventType, + "Thread " + thread.getId() + " state changed", List.of(targetThread)); + }); + } + + private CompletableFuture stateChanged(Object object, DbgState state, + String reason) { + List objPath = findObject(object); + return AsyncUtils.sequence(TypeSpec.cls(DbgModelTargetObject.class)).then(seq -> { + getModel().fetchModelValue(objPath).handle(seq::next); + }, TypeSpec.cls(Object.class)).then((obj, seq) -> { + if (obj instanceof DbgModelTargetExecutionStateful) { + DbgModelTargetExecutionStateful stateful = + (DbgModelTargetExecutionStateful) obj; + TargetExecutionState execState = stateful.convertState(state); + stateful.setExecutionState(execState, reason); + } + seq.exit((DbgModelTargetObject) obj); + }).finish(); + } + + @Override + public void breakpointCreated(DbgBreakpointInfo info, DbgCause cause) { + int id = info.getDebugBreakpoint().getId(); + bptInfoMap.put(id, info); + getObject(info.getProc(), List.of("Debug", "Breakpoints"), Integer.toHexString(id)); + /* + getObject(info).thenAccept(obj -> { + DbgModelTargetBreakpointSpec bpt = (DbgModelTargetBreakpointSpec) obj; + if (bpt == null) { + return; + } + bpt.setBreakpointInfo(info); + bpt.setEnabled(true, "Created"); + }); + */ + } + + @Override + public void breakpointModified(DbgBreakpointInfo newInfo, DbgBreakpointInfo oldInfo, + DbgCause cause) { + int id = newInfo.getDebugBreakpoint().getId(); + bptInfoMap.put(id, newInfo); + getObject(newInfo.getProc(), List.of("Debug", "Breakpoints"), Integer.toHexString(id)); + } + + @Override + public void breakpointDeleted(DbgBreakpointInfo info, DbgCause cause) { + int id = info.getDebugBreakpoint().getId(); + bptInfoMap.remove(id); + getObject(info.getProc(), List.of("Debug", "Breakpoints"), Integer.toHexString(id)); + } + + @Override + public void breakpointHit(DbgBreakpointInfo info, DbgCause cause) { + int id = info.getDebugBreakpoint().getId(); + getObject(info.getProc(), List.of("Debug", "Breakpoints"), Integer.toHexString(id)) + .thenAccept(obj -> { + DbgModelTargetBreakpointSpec bpt = (DbgModelTargetBreakpointSpec) obj; + if (bpt == null) { + Msg.error(this, "Stopped for breakpoint unknown to the agent: " + + info.getNumber() + " (pc=" + info.getLocation() + ")"); + return; + } + + listeners.fire(TargetBreakpointListener.class) + .breakpointHit((TargetBreakpointContainer) bpt.getParent(), + getParentProcess(), null, bpt, bpt); + bpt.breakpointHit(); + }); + } + + /* + @Override + public void consoleOutput(String output, int mask) { + Channel chan = TargetConsole.Channel.STDOUT; + if (((mask & DebugOutputFlags.DEBUG_OUTPUT_ERROR.getValue()) // + == DebugOutputFlags.DEBUG_OUTPUT_ERROR.getValue()) || // + ((mask & DebugOutputFlags.DEBUG_OUTPUT_WARNING.getValue()) // + == DebugOutputFlags.DEBUG_OUTPUT_WARNING.getValue())) { + chan = TargetConsole.Channel.STDERR; + } + final Channel channel = chan; + getObject("cursession").thenAccept(session -> { + listeners.fire(TargetInterpreterListener.class).consoleOutput(session, channel, output); + }); + } + */ + + private List findObject(Object obj) { + DebugSystemObjects so = getManager().getSystemObjects(); + List objpath = new ArrayList<>(); + DebugSessionId sid = so.getCurrentSystemId(); + String skey = sid.id < 0 ? PathUtils.makeKey("0x0") + : PathUtils.makeKey("0x" + Integer.toHexString(sid.id)); + if (obj instanceof DbgSession) { + DbgSession session = (DbgSession) obj; + skey = PathUtils.makeKey("0x" + Long.toHexString(session.getId().id)); + } + if (obj instanceof DbgSession || obj instanceof String) { + objpath = List.of("Sessions", skey); + return objpath; + } + int pid = so.getCurrentProcessSystemId(); + String pkey = PathUtils.makeKey("0x" + Integer.toHexString(pid)); + if (obj instanceof DbgProcess) { + DbgProcess process = (DbgProcess) obj; + pkey = PathUtils.makeKey("0x" + Long.toHexString(process.getPid())); + } + if (obj instanceof DbgProcess || obj instanceof DebugProcessId) { + objpath = List.of("Sessions", skey, "Processes", pkey); + return objpath; + } + int tid = so.getCurrentThreadSystemId(); + String tkey = PathUtils.makeKey("0x" + Integer.toHexString(tid)); + if (obj instanceof DbgThread) { + DbgThread thread = (DbgThread) obj; + DbgProcess process = thread.getProcess(); + tkey = PathUtils.makeKey("0x" + Long.toHexString(thread.getTid())); + pkey = PathUtils.makeKey("0x" + Long.toHexString(process.getPid())); + } + if (obj instanceof DbgStackFrame) { + DbgStackFrame frame = (DbgStackFrame) obj; + DbgThread thread = frame.getThread(); + DbgProcess process = thread.getProcess(); + int level = frame.getLevel(); + String fkey = "[0x" + Integer.toHexString(level) + "]"; + tkey = PathUtils.makeKey("0x" + Long.toHexString(thread.getTid())); + pkey = PathUtils.makeKey("0x" + Long.toHexString(process.getPid())); + objpath = List.of("Sessions", skey, "Processes", pkey, "Threads", tkey, "Stack", + "Frames", fkey); + } + if (obj instanceof DbgThread || obj instanceof DebugThreadId) { + objpath = List.of("Sessions", skey, "Processes", pkey, "Threads", tkey); + } + return objpath; + } + + private TargetEventType getEventType(DbgState state, DbgCause cause, DbgReason reason) { + switch (state) { + case RUNNING: + return TargetEventType.RUNNING; + case STOPPED: + case EXIT: + if (reason instanceof DbgEndSteppingRangeReason) { + return TargetEventType.STEP_COMPLETED; + } + if (reason instanceof DbgSignalReceivedReason) { + return TargetEventType.SIGNAL; + } + if (reason instanceof DbgExitedReason) { + return TargetEventType.EXCEPTION; + } + if (reason instanceof DbgExitNormallyReason) { + return TargetEventType.THREAD_EXITED; + } + return TargetEventType.STOPPED; + default: + break; + } + return null; + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + DbgManager2Impl manager2 = (DbgManager2Impl) getManager(); + List pathX = PathUtils.extend(List.of("Debugger"), path); + intrinsics.put(available.getName(), available); + intrinsics.put(connectors.getName(), connectors); + intrinsics.put(systemMarker.getName(), systemMarker); + return manager2.listAttributes(pathX, this).thenAccept(map -> { + if (map == null) { + return; + } + setAttributes(List.of(), map, "Refreshed"); + }); + } + + //@Override + public void refresh() { + // TODO ??? + System.err.println("root:refresh"); + } + + @Override + public TargetAccessibility getAccessibility() { + return accessibility; + } + + @Override + public void setAccessibility(TargetAccessibility accessibility) { + this.accessibility = accessibility; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetSystemMarkerImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetSystemMarkerImpl.java new file mode 100644 index 0000000000..e3a02320d9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetSystemMarkerImpl.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.model.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.model.iface1.DbgModelTargetInterruptible; +import agent.dbgeng.model.iface2.DbgModelTargetObject; + +public class DbgModel2TargetSystemMarkerImpl extends DbgModel2TargetObjectImpl + implements DbgModelTargetInterruptible { + + // NB: this is an invisible marker whose only purpose if to enable an + // interrupt when connecting in kernel-mode to a running target + public DbgModel2TargetSystemMarkerImpl(DbgModelTargetObject obj) { + super(obj.getModel(), obj, "_system", "SystemMarker"); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + Map nmap = new HashMap<>(); + return addModelObjectAttributes(nmap); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DelegateDbgModel2TargetObject.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DelegateDbgModel2TargetObject.java new file mode 100644 index 0000000000..90e9a971ef --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DelegateDbgModel2TargetObject.java @@ -0,0 +1,376 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.model.impl; + +import java.lang.invoke.MethodHandles; +import java.lang.ref.Cleaner; +import java.lang.ref.Cleaner.Cleanable; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.model.iface1.*; +import agent.dbgeng.model.iface2.*; +import agent.dbgmodel.dbgmodel.main.ModelObject; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.ModelObjectKind; +import ghidra.async.AsyncUtils; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointAction; +import ghidra.dbg.util.PathUtils; +import ghidra.util.datastruct.ListenerSet; +import utilities.util.ProxyUtilities; + +public class DelegateDbgModel2TargetObject extends DbgModel2TargetObjectImpl implements // + DbgModelTargetAccessConditioned, // + DbgModelTargetExecutionStateful, // + DbgModelTargetBptHelper { + // Probably don-t need any of the handler-map or annotation stuff + + protected final DbgStateListener accessListener = this::checkExited; + + protected static String indexObject(ModelObject obj) { + return obj.getSearchKey(); + } + + public static String keyObject(ModelObject obj) { + return PathUtils.makeKey(indexObject(obj)); + } + + protected static final Cleaner CLEANER = Cleaner.create(); + + // For resource management, I highly recommend keeping the ProxyState stuff. + // It's contents will look different + protected static class ProxyState implements Runnable { + protected final DbgModel2Impl model; + protected final ModelObject modelObject; + + public ProxyState(DbgModel2Impl model, ModelObject modelObject) { + this.model = model; + this.modelObject = modelObject; + } + + @Override + public void run() { + modelObject.dereference(); // Or whatever COM thing to free it here + } + } + + protected static Class lookupWrapperType(String type, + String parentName) { + switch (type) { + case "Available": + return DbgModelTargetAvailableContainer.class; + case "Sessions": + return DbgModelTargetSessionContainer.class; + case "Processes": + return DbgModelTargetProcessContainer.class; + case "Threads": + return DbgModelTargetThreadContainer.class; + case "Modules": + return DbgModelTargetModuleContainer.class; + case "Frames": + return DbgModelTargetStack.class; + case "Registers": + return DbgModelTargetRegisterContainer.class; + case "Attributes": + return DbgModelTargetSessionAttributes.class; + case "Breakpoints": + return DbgModelTargetBreakpointContainer.class; + case "cursession": + return DbgModelTargetSession.class; + case "curprocess": + return DbgModelTargetProcess.class; + case "curthread": + return DbgModelTargetThread.class; + case "curframe": + return DbgModelTargetStackFrame.class; + case "User": + return DbgModelTargetRegisterBank.class; + case "TTD": + return DbgModelTargetTTD.class; + } + if (parentName != null) { + switch (parentName) { + case "Available": + return DbgModelTargetAvailable.class; + case "Sessions": + return DbgModelTargetSession.class; + case "Processes": + return DbgModelTargetProcess.class; + case "Threads": + return DbgModelTargetThread.class; + case "Modules": + return DbgModelTargetModule.class; + case "Frames": + return DbgModelTargetStackFrame.class; + case "Breakpoints": + return DbgModelTargetBreakpointSpec.class; + //case "Registers": + // return DbgModelTargetRegisterBank.class; + case "FloatingPoint": + case "Kernel": + case "SIMD": + case "VFP": + case "User": + return DbgModelTargetRegister.class; + } + } + return null; + } + + public static DbgModelTargetObject makeProxy(DbgModel2Impl model, DbgModelTargetObject parent, + String key, ModelObject object) { + List> mixins = new ArrayList<>(); + String lkey = key; + String pname = parent.getName(); + if (object.getKind().equals(ModelObjectKind.OBJECT_METHOD) || lkey.contains(")")) { + mixins.add(DbgModelTargetMethod.class); + // NB: We're passing the parent's mixin model to the method on the assumption + // the init methods will need to know that the method's children have various + // properties. + lkey = pname; + pname = ""; + } + Class mixin = lookupWrapperType(lkey, pname); + if (mixin != null) { + mixins.add(mixin); + } + return new DelegateDbgModel2TargetObject(model, parent, key, object, mixins).proxy; + } + + private static Map map = new HashMap<>(); + + public static DelegateDbgModel2TargetObject getDelegate(DbgModelTargetObject proxy) { + return map.get(proxy); + } + + protected static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + + // NOTE: The Cleanable stuff is the replacement for overriding Object.finalize(), which + // is now deprecated. + protected final ProxyState state; + protected final Cleanable cleanable; + + private final DbgModelTargetObject proxy; + + private boolean breakpointEnabled; + private final ListenerSet breakpointActions = + new ListenerSet<>(TargetBreakpointAction.class) { + // Use strong references on actions + protected Map createMap() { + return Collections.synchronizedMap(new LinkedHashMap<>()); + }; + }; + + // Extending DefaultTargetObject may spare you from listeners, elements, and attributes + //protected final ListenerSet listeners = + // new ListenerSet<>(TargetObjectListener.class); + + // any other fields you need to support your impl + + public DelegateDbgModel2TargetObject(DbgModel2Impl model, DbgModelTargetObject parent, + String key, ModelObject modelObject, List> mixins) { + super(model, parent.getProxy(), key, getHintForObject(modelObject)); + this.state = new ProxyState(model, modelObject); + this.cleanable = CLEANER.register(this, state); + + getManager().addStateListener(accessListener); + + this.proxy = + ProxyUtilities.composeOnDelegate(DbgModelTargetObject.class, this, mixins, LOOKUP); + map.put(proxy, this); + if (proxy instanceof DbgEventsListener) { + model.getManager().addEventsListener((DbgEventsListener) proxy); + } + setModelObject(modelObject); + } + + @Override + public > T as(Class iface) { + return DebuggerObjectModel.requireIface(iface, proxy, getPath()); + } + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public CompletableFuture fetch() { + return (CompletableFuture) CompletableFuture.completedFuture(proxy); + } + + @Override + public TargetObject getProxy() { + return proxy; + } + + @SuppressWarnings("unchecked") + @Override + public CompletableFuture fetchParent() { + TargetObjectRef p = getParent(); + if (p == null) { + return AsyncUtils.nil(); + } + return (CompletableFuture) p.fetch(); + } + + protected static String getHintForObject(ModelObject obj) { + ModelObjectKind kind = obj.getKind(); + String ret = kind == null ? "" : kind.name(); + if (kind.equals(ModelObjectKind.OBJECT_INTRINSIC)) { + ret += " " + obj.getValueString(); + } + return ret; + } + + protected void checkExited(DbgState state, DbgCause cause) { + TargetExecutionState exec = TargetExecutionState.INACTIVE; + switch (state) { + case NOT_STARTED: { + exec = TargetExecutionState.INACTIVE; + break; + } + case STARTING: { + exec = TargetExecutionState.ALIVE; + break; + } + case RUNNING: { + exec = TargetExecutionState.RUNNING; + resetModified(); + onRunning(); + break; + } + case STOPPED: { + exec = TargetExecutionState.STOPPED; + onStopped(); + break; + } + case EXIT: { + exec = TargetExecutionState.TERMINATED; + onExit(); + break; + } + } + if (proxy instanceof TargetExecutionStateful) { + setExecutionState(exec, "Refreshed"); + } + } + + private void invalidate() { + if (proxy instanceof DbgModelTargetMemoryContainer || // + proxy instanceof DbgModelTargetBreakpointContainer || // + proxy instanceof DbgModelTargetRegisterContainer || // + proxy instanceof DbgModelTargetRegisterBank || // + proxy instanceof DbgModelTargetStackFrame || // + proxy instanceof DbgModelTargetStack || // + proxy instanceof DbgModelTargetTTD) { + listeners.fire.invalidateCacheRequested(proxy); + return; + } + } + + private void update() { + if (proxy instanceof DbgModelTargetProcessContainer || // + proxy instanceof DbgModelTargetThreadContainer || // + proxy instanceof DbgModelTargetModuleContainer || // + proxy instanceof DbgModelTargetBreakpointContainer || // + proxy instanceof DbgModelTargetRegisterContainer || // + proxy instanceof DbgModelTargetRegisterBank || // + proxy instanceof DbgModelTargetStack || // + proxy instanceof DbgModelTargetTTD) { + requestElements(true); + requestAttributes(true); + return; + } + if (proxy instanceof DbgModelTargetRegister || proxy instanceof DbgModelTargetStackFrame) { + DbgThread thread = proxy.getParentThread().getThread(); + if (thread.equals(getManager().getEventThread())) { + requestAttributes(true); + } + return; + } + } + + public void onRunning() { + invalidate(); + setAccessibility(TargetAccessibility.INACCESSIBLE); + } + + public void onStopped() { + setAccessibility(TargetAccessibility.ACCESSIBLE); + update(); + } + + public void onExit() { + setAccessibility(TargetAccessibility.ACCESSIBLE); + } + + @Override + public TargetAccessibility getAccessibility() { + return accessibility; + } + + @Override + public void setAccessibility(TargetAccessibility accessibility) { + synchronized (attributes) { + if (this.accessibility == accessibility) { + return; + } + this.accessibility = accessibility; + } + if (proxy instanceof TargetAccessConditioned) { + changeAttributes(List.of(), List.of(), Map.of( // + TargetAccessConditioned.ACCESSIBLE_ATTRIBUTE_NAME, + accessibility == TargetAccessibility.ACCESSIBLE // + ), "Accessibility changed"); + } + } + + // Methods required for DbgModelTargetBreakpointSpec mixin + + @Override + public DbgBreakpointInfo getBreakpointInfo() { + return bptInfoMap.get(Integer.decode(bptId)); + } + + @Override + public void setBreakpointId(String id) { + this.bptId = id; + } + + @Override + public void setBreakpointInfo(DbgBreakpointInfo info) { + TargetObject id = (TargetObject) this.getCachedAttribute("Id"); + String idstr = id.getCachedAttribute(VALUE_ATTRIBUTE_NAME).toString(); + bptInfoMap.put(Integer.decode(idstr), info); + } + + @Override + public boolean isBreakpointEnabled() { + return breakpointEnabled; + } + + @Override + public void setBreakpointEnabled(boolean enabled) { + update(); + this.breakpointEnabled = enabled; + } + + public ListenerSet getActions() { + return breakpointActions; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/sctldbg/cpp/sctldbg.cpp b/Ghidra/Debug/Debugger-agent-dbgmodel/src/sctldbg/cpp/sctldbg.cpp new file mode 100644 index 0000000000..b01c2055d8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/sctldbg/cpp/sctldbg.cpp @@ -0,0 +1,316 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#define INITGUID +#include +#include +#include + +#include + +JavaVM* jvm = NULL; +JNIEnv* env = NULL; + +char JDK_JVM_DLL_PATH[] = "\\jre\\bin\\server\\jvm.dll"; +char JRE_JVM_DLL_PATH[] = "\\bin\\server\\jvm.dll"; + +char MAIN_CLASS[] = "sctldbgmodel/sctl/DbgModelSctlServer"; + +char CP_PREFIX[] = "-Djava.class.path="; + +typedef jint (_cdecl *CreateJavaVMFunc)(JavaVM**, void**, void*); + + +#define CHECK_RC(v, f, x) do { \ + HRESULT ___hr = (x); \ + if (___hr < 0) { \ + fprintf(stderr, "FAILED on line %d: HRESULT=%08x\n", __LINE__, ___hr); \ + goto f; \ + } else if (___hr == S_OK) { \ + v = 1; \ + } else { \ + v = 0; \ + } \ +} while (0) + + +#if 0 +class MyEventCallbacks : public DebugBaseEventCallbacks { +public: + STDMETHOD_(ULONG, AddRef)(THIS) { + InterlockedIncrement(&m_ulRefCount); + return m_ulRefCount; + } + + STDMETHOD_(ULONG, Release)(THIS) { + ULONG ulRefCount = InterlockedDecrement(&m_ulRefCount); + if (m_ulRefCount == 0) { + delete this; + } + return ulRefCount; + } + + STDMETHOD(GetInterestMask)(_Out_ PULONG Mask) { + *Mask = DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_CREATE_THREAD; + return S_OK; + } + + STDMETHOD(CreateProcess)( + THIS_ + _In_ ULONG64 ImageFileHandle, + _In_ ULONG64 Handle, + _In_ ULONG64 BaseOffset, + _In_ ULONG ModuleSize, + _In_ PCSTR ModuleName, + _In_ PCSTR ImageName, + _In_ ULONG CheckSum, + _In_ ULONG TimeDateStamp, + _In_ ULONG64 InitialThreadHandle, + _In_ ULONG64 ThreadDataOffset, + _In_ ULONG64 StartOffset + ) { + UNREFERENCED_PARAMETER(ImageFileHandle); + UNREFERENCED_PARAMETER(Handle); + UNREFERENCED_PARAMETER(BaseOffset); + UNREFERENCED_PARAMETER(ModuleSize); + UNREFERENCED_PARAMETER(ModuleName); + UNREFERENCED_PARAMETER(ImageName); + UNREFERENCED_PARAMETER(CheckSum); + UNREFERENCED_PARAMETER(TimeDateStamp); + UNREFERENCED_PARAMETER(InitialThreadHandle); + UNREFERENCED_PARAMETER(ThreadDataOffset); + UNREFERENCED_PARAMETER(StartOffset); + return DEBUG_STATUS_BREAK; + } + + STDMETHOD(CreateThread)( + THIS_ + _In_ ULONG64 Handle, + _In_ ULONG64 DataOffset, + _In_ ULONG64 StartOffset + ) { + UNREFERENCED_PARAMETER(Handle); + UNREFERENCED_PARAMETER(DataOffset); + UNREFERENCED_PARAMETER(StartOffset); + return DEBUG_STATUS_BREAK; + } +private: + ULONG m_ulRefCount = 0; +}; + +int main_exp00(int argc, char** argv) { + PDEBUG_CLIENT5 pClient5 = NULL; + PDEBUG_CONTROL4 pControl4 = NULL; + PDEBUG_SYMBOLS3 pSymbols3 = NULL; + int ok = 0; + + CHECK_RC(ok, EXIT, DebugCreate(IID_IDebugClient5, (PVOID*) &pClient5)); + CHECK_RC(ok, EXIT, pClient5->QueryInterface(IID_IDebugControl4, (PVOID*) &pControl4)); + CHECK_RC(ok, EXIT, pClient5->QueryInterface(IID_IDebugSymbols3, (PVOID*) &pSymbols3)); + + pClient5->SetEventCallbacks(new MyEventCallbacks()); + + CHECK_RC(ok, EXIT, pControl4->Execute(DEBUG_OUTCTL_ALL_CLIENTS, ".create notepad", DEBUG_EXECUTE_ECHO)); + CHECK_RC(ok, EXIT, pControl4->WaitForEvent(0, INFINITE)); + CHECK_RC(ok, EXIT, pControl4->Execute(DEBUG_OUTCTL_ALL_CLIENTS, "g", DEBUG_EXECUTE_ECHO)); + CHECK_RC(ok, EXIT, pControl4->WaitForEvent(0, INFINITE)); + + ULONG64 ul64MatchHandle = 0; + CHECK_RC(ok, EXIT, pSymbols3->StartSymbolMatch("*", &ul64MatchHandle)); + while (true) { + char aBuffer[1024] = { 0 }; + ULONG64 ul64Offset = 0; + CHECK_RC(ok, FINISH, pSymbols3->GetNextSymbolMatch(ul64MatchHandle, aBuffer, sizeof(aBuffer), NULL, &ul64Offset)); + printf("%016x: %s\n", ul64Offset, aBuffer); + } +FINISH: + + fprintf(stderr, "SUCCESS\n"); +EXIT: + pClient5->SetEventCallbacks(NULL); + pControl4->Release(); + pClient5->Release(); + return 0; +} + +int main_exp01(int argc, char** argv) { + PDEBUG_CLIENT5 pClient5 = NULL; + int ok = 0; + + CHECK_RC(ok, EXIT, DebugCreate(IID_IDebugClient5, (PVOID*) &pClient5)); + + CHECK_RC(ok, EXIT, pClient5->StartProcessServerWide(DEBUG_CLASS_USER_WINDOWS, L"tcp:port=11200", NULL)); + CHECK_RC(ok, EXIT, pClient5->WaitForProcessServerEnd(INFINITE)); +EXIT: + if (pClient5 != NULL) { + pClient5->Release(); + } + return 0; +} +#endif + +int main_sctldbg(int argc, char** argv) { + if (argc < 1) { + fprintf(stderr, "Something is terribly wrong: argc == 0\n"); + } + char* env_java_home = getenv("JAVA_HOME"); + if (env_java_home == NULL) { + fprintf(stderr, "JAVA_HOME is not set\n"); + fflush(stderr); + return -1; + } + char* java_home = strdup(env_java_home); + size_t home_len = strlen(java_home); + if (java_home[home_len - 1] == '\\') { + java_home[home_len - 1] = '\0'; + } + size_t full_len = home_len + sizeof(JDK_JVM_DLL_PATH); + char* full_path = new char[full_len]; + HMODULE jvmDll = NULL; + // Try the JRE path first; + strcpy_s(full_path, full_len, java_home); + strcat_s(full_path, full_len, JRE_JVM_DLL_PATH); + fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path); + fflush(stderr); + jvmDll = LoadLibraryA(full_path); + if (jvmDll == NULL) { + // OK, then try the JDK path + strcpy_s(full_path, full_len, java_home); + strcat_s(full_path, full_len, JDK_JVM_DLL_PATH); + fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path); + fflush(stderr); + jvmDll = LoadLibraryA(full_path); + } + + free(full_path); + free(java_home); + + if (jvmDll == NULL) { + fprintf(stderr, "Could not find the jvm.dll\n"); + fflush(stderr); + return -1; + } + fprintf(stderr, "Found it!\n"); + fflush(stderr); + +#define USE_EXE_AS_JAR +#ifdef USE_EXE_AS_JAR + DWORD fullpath_len = GetFullPathNameA(argv[0], 0, NULL, NULL); + char* fullpath = new char[fullpath_len]; + GetFullPathNameA(argv[0], fullpath_len, fullpath, NULL); + size_t cp_opt_len = sizeof(CP_PREFIX) + strlen(fullpath); + char* cp_opt = new char[cp_opt_len]; + strcpy_s(cp_opt, cp_opt_len, CP_PREFIX); + strcat_s(cp_opt, cp_opt_len, fullpath); + fflush(stderr); +#endif + + JavaVMOption options[2]; + JavaVMInitArgs vm_args = { 0 }; + vm_args.version = JNI_VERSION_1_8; + vm_args.nOptions = sizeof(options)/sizeof(options[0]); + vm_args.options = options; + options[0].optionString = "-Xrs"; +#ifdef USE_EXE_AS_JAR + fprintf(stderr, "Classpath: %s\n", cp_opt); + options[1].optionString = cp_opt; +#else + options[1].optionString = "-Djava.class.path=sctldbgmodel.jar"; +#endif + //options[2].optionString = "-verbose:class"; + vm_args.ignoreUnrecognized = false; + CreateJavaVMFunc create_jvm = NULL; + //create_jvm = JNI_CreateJavaVM; + create_jvm = (CreateJavaVMFunc) GetProcAddress(jvmDll, "JNI_CreateJavaVM"); + jint jni_result = create_jvm(&jvm, (void**)&env, &vm_args); + +#ifdef USE_EXE_AS_JAR + free(cp_opt); +#endif + + if (jni_result != JNI_OK) { + jvm = NULL; + fprintf(stderr, "Could not initialize JVM: %d: ", jni_result); + switch (jni_result) { + case JNI_ERR: + fprintf(stderr, "unknown error"); + break; + case JNI_EDETACHED: + fprintf(stderr, "thread detached from the VM"); + break; + case JNI_EVERSION: + fprintf(stderr, "JNI version error"); + break; + case JNI_ENOMEM: + fprintf(stderr, "not enough memory"); + break; + case JNI_EEXIST: + fprintf(stderr, "VM already created"); + break; + case JNI_EINVAL: + fprintf(stderr, "invalid arguments"); + break; + } + fprintf(stderr, "\n"); + fflush(stderr); + return -1; + } + + jclass mainCls = env->FindClass(MAIN_CLASS); + if (mainCls == NULL) { + fprintf(stderr, "Could not find main class: %s\n", MAIN_CLASS); + jvm->DestroyJavaVM(); + return -1; + } + + jmethodID mainMeth = env->GetStaticMethodID(mainCls, "main", "([Ljava/lang/String;)V"); + if (mainMeth == NULL) { + fprintf(stderr, "No main(String[] args) method in main class\n"); + jvm->DestroyJavaVM(); + return -1; + } + + jclass stringCls = env->FindClass("java/lang/String"); + + jobjectArray jargs = env->NewObjectArray(argc - 1, stringCls, NULL); + for (int i = 1; i < argc; i++) { + jstring a = env->NewStringUTF(argv[i]); + if (a == NULL) { + fprintf(stderr, "Could not create Java string for arguments.\n"); + jvm->DestroyJavaVM(); + return -1; + } + env->SetObjectArrayElement(jargs, i - 1, a); + } + + env->CallStaticVoidMethod(mainCls, mainMeth, (jvalue*) jargs); + + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + jvm->DestroyJavaVM(); + + return 0; +} + +int main(int argc, char** argv) { + main_sctldbg(argc, argv); +} + diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/test/java/agent/dbgmodel/DummyProc.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/test/java/agent/dbgmodel/DummyProc.java new file mode 100644 index 0000000000..a7f411b83d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/test/java/agent/dbgmodel/DummyProc.java @@ -0,0 +1,51 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel; + +import static org.junit.Assume.assumeTrue; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinNT.HANDLE; + +public class DummyProc implements AutoCloseable { + final Process process; + public final int pid; + + public DummyProc(String... args) throws IOException, NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException { + process = new ProcessBuilder(args).start(); + + Class cls = process.getClass(); + assumeTrue(cls.getName().equals("java.lang.ProcessImpl")); + Field handleFld = cls.getDeclaredField("handle"); + handleFld.setAccessible(true); + long handle = handleFld.getLong(process); + pid = Kernel32.INSTANCE.GetProcessId(new HANDLE(new Pointer(handle))); + } + + @Override + public void close() throws Exception { + if (!process.destroyForcibly().waitFor(1000, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Could not terminate process " + pid); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/test/java/agent/dbgmodel/dbgmodel/DbgModelTest.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/test/java/agent/dbgmodel/dbgmodel/DbgModelTest.java new file mode 100644 index 0000000000..04a042982b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/test/java/agent/dbgmodel/dbgmodel/DbgModelTest.java @@ -0,0 +1,1309 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgmodel.dbgmodel; + +import static org.junit.Assert.*; + +import java.io.*; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import org.junit.Before; +import org.junit.Test; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.COM.COMException; +import com.sun.jna.platform.win32.COM.Unknown; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugBreakpoint.BreakType; +import agent.dbgeng.dbgeng.DebugClient.*; +import agent.dbgeng.dbgeng.DebugDataSpaces.*; +import agent.dbgeng.dbgeng.DebugModule.DebugModuleName; +import agent.dbgeng.dbgeng.DebugRegisters.DebugRegisterDescription; +import agent.dbgeng.dbgeng.DebugRegisters.DebugRegisterSource; +import agent.dbgeng.dbgeng.DebugValue.DebugInt64Value; +import agent.dbgeng.dbgeng.util.DebugEventCallbacksAdapter; +import agent.dbgmodel.dbgmodel.bridge.HostDataModelAccess; +import agent.dbgmodel.dbgmodel.datamodel.DataModelManager1; +import agent.dbgmodel.dbgmodel.datamodel.script.*; +import agent.dbgmodel.dbgmodel.debughost.*; +import agent.dbgmodel.dbgmodel.main.*; +import agent.dbgmodel.gadp.impl.WrappedDbgModel; +import agent.dbgmodel.impl.dbgmodel.bridge.HDMAUtil; +import agent.dbgmodel.impl.dbgmodel.debughost.DebugHostModuleImpl1; +import agent.dbgmodel.impl.dbgmodel.main.ModelPropertyAccessorInternal; +import agent.dbgmodel.jna.dbgmodel.DbgModelNative.*; +import agent.dbgmodel.jna.dbgmodel.UnknownWithUtils; +import ghidra.comm.util.BitmaskSet; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; + +public class DbgModelTest extends AbstractGhidraHeadlessIntegrationTest { + protected static HostDataModelAccess cachedAccess = null; + protected static DebugClient cachedClient = null; + + protected HostDataModelAccess doDebugCreate() { + System.out.println("Creating a client"); + return DbgModel.debugCreate(); + } + + protected HostDataModelAccess doDebugConnect() { + System.out.println("Connecting to a client"); + String options = "tcp:Port=54321"; + return DbgModel.debugConnect(options); + } + + protected void debugCreate() { + //if (cachedClient == null) { + cachedAccess = doDebugCreate(); + cachedClient = cachedAccess.getClient(); + //} + } + + protected void debugConnect() { + cachedAccess = doDebugConnect(); + cachedClient = cachedAccess.getClient(); + } + + protected HostDataModelAccess access; + protected DebugClient client; + protected DebugControl control; + + @Before + public void setUp() { + DbgEngTest.assumeDbgengDLLLoadable(); + debugCreate(); + //debugConnect(); + access = cachedAccess; + client = cachedClient; + control = client.getControl(); + } + + @Test + public void testServer() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + control.execute(".server tcp:port=54321"); + + int count = 0; + try { + while (true) { + count++; + } + } + catch (Exception e) { + System.err.println(e); + } + } + } + + @Test + public void testOpenTrace() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + // NB: This does not work! TTDReplay must live in TTD\TTReplay.dll wherever + // dbgeng.dll lives + //client.getControl().execute(".load c:\\Software\\windbg\\amd64\\ttd\\TTDReplay.dll"); + + client.getControl().execute(".load TTDReplay.dll"); + client.getControl().execute(".load TTDAnalyze.dll"); + client.openDumpFileWide("notepad01.run"); + } + } + + @Test + public void testOpenTraceWithoutProcess() { + final CompletableFuture procInfo = new CompletableFuture<>(); + final CompletableFuture threadInfo = new CompletableFuture<>(); + final CompletableFuture procExit = new CompletableFuture<>(); + + StringBuilder outputCapture = null; + client.setEventCallbacks(new NoisyDebugEventCallbacksAdapter(DebugStatus.NO_CHANGE) { + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + super.createProcess(debugProcessInfo); + procInfo.complete(debugProcessInfo); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + super.createThread(debugThreadInfo); + threadInfo.complete(debugThreadInfo); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + super.exitProcess(exitCode); + procExit.complete(exitCode); + return DebugStatus.BREAK; + } + }); + client.setOutputCallbacks(new DebugOutputCallbacks() { + @Override + public void output(int mask, String text) { + System.out.print(text); + if (outputCapture != null) { + outputCapture.append(text); + } + } + }); + + client.openDumpFileWide("notepad01.run"); + control.waitForEvent(); + control.execute("g"); + control.waitForEvent(); + DebugProcessInfo pi = procInfo.getNow(null); + DebugThreadInfo ti = threadInfo.getNow(null); + DebugSystemObjects so = client.getSystemObjects(); + int currentProcessSystemId = so.getCurrentProcessSystemId(); + DebugProcessId currentProcessId = so.getCurrentProcessId(); + DebugThreadId currentThreadId = so.getCurrentThreadId(); + DebugProcessId eventProcess = so.getEventProcess(); + DebugThreadId eventThread = so.getEventThread(); + List processes = so.getProcesses(); + List threads = so.getThreads(); + System.err.println(threads); + } + + @Test + public void testInterfaces() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + DataModelManager1 manager = util.getManager(); + assertNotNull("manager not null", manager); + DebugHost host = util.getHost(); + assertNotNull("host not null", host); + //KeyStore defaultMetadata = host.getDefaultMetadata(); + //UnknownEx hostDefinedInterface = host.getHostDefinedInterface(); + + ModelObject rootNamespace = util.getRootNamespace(); + assertNotNull("rootNamespace not null", rootNamespace); + enumerate(rootNamespace, " "); + //enumerateR(rootNamespace, " "); + System.out.println("END"); + } + } + + @Test + public void testHammerEnumerate() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + DataModelManager1 manager = util.getManager(); + assertNotNull("manager not null", manager); + DebugHost host = util.getHost(); + assertNotNull("host not null", host); + + ModelObject rootNamespace = util.getRootNamespace(); + assertNotNull("rootNamespace not null", rootNamespace); + for (int i = 0; i < 10; i++) { + enumerate(rootNamespace, " "); + System.gc(); + } + System.out.println("END"); + } + UnknownWithUtils.ANALYZER.checkLeaks(); + } + + HashSet seen = new HashSet(); + + private void enumerate(ModelObject obj, String tab) { + String key; + KeyEnumerator enumerateKeys = obj.enumerateKeyValues(); + do { + key = enumerateKeys.getNext(); + ModelObject value = enumerateKeys.getValue(); + if (value == null || key == null || key.equals("Registers")) { + continue; + } + //if (!value.getKind().equals(ModelObjectKind.OBJECT_METHOD)) { + String desc = tab + key + ":" + value.getKind(); + if (value.getKind().equals(ModelObjectKind.OBJECT_INTRINSIC)) { + desc += ":" + value.getIntrinsicValue(); + } + if (!seen.contains(key)) { + System.out.println(desc); + seen.add(key); + enumerate(value, tab + " "); + } + //} + } + while (key != null); + + List children = obj.getElements(); + ListIterator iter = children.listIterator(); + if (iter.hasNext()) { + ModelObject child = iter.next(); + System.err.println(tab + child.toString()); + enumerate(child, tab + " "); + } + + if (obj.getKind().equals(ModelObjectKind.OBJECT_TARGET_OBJECT)) { + RawEnumerator enumerateRaw = + obj.enumerateRawValues(SymbolKind.SYMBOL_FIELD.ordinal(), 0); + while ((key = enumerateRaw.getNext()) != null) { + ModelObject value = enumerateRaw.getValue(); + String desc = tab + key + ":" + value.getKind(); + if (value.getKind().equals(ModelObjectKind.OBJECT_INTRINSIC)) { + desc += ":" + value.getIntrinsicValue(); + } + if (!seen.contains(key.toString())) { + System.out.println(desc); + seen.add(key.toString()); + enumerate(value, tab + " "); + } + } + } + } + + @Test + public void testGetChild() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + ModelObject currentProcess = util.getCurrentProcess(); + String ctlid = util.getCtlId(currentProcess); + ModelObject process = util.getProcess(util.getCurrentSession(), ctlid); + assertTrue(ctlid.equals(util.getCtlId(process))); + } + } + + @Test + public void testEnv() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + //control.execute(".server tcp:port=54321"); + + HDMAUtil util = new HDMAUtil(access); + //DataModelManager1 manager = util.getManager(); + + ModelObject currentProcess = util.getCurrentProcess(); + ModelObject env = currentProcess.getKeyValue("Environment"); + ModelObject eb = env.getKeyValue("EnvironmentBlock"); + System.out.println(eb.getKind()); + System.out.println(eb.toString()); + + DebugHostType1 targetInfo = eb.getTargetInfo(); + //DebugHostContext context = targetInfo.getContext(); + System.out.println(targetInfo.getSymbolKind()); + System.out.println(targetInfo.getName()); + //DebugHostType1 type = targetInfo.getType(); + DebugHostModule1 containingModule = targetInfo.getContainingModule(); + System.out.println(containingModule.getName()); + DebugHostType1 typeByName = containingModule.findTypeByName("_PEB"); + System.out.println(typeByName.getName()); + + //System.out.println(targetInfo.getOffset()); + System.out.println(targetInfo.getTypeKind()); + System.out.println(targetInfo.getSize()); + + System.out.println(Integer.toHexString(targetInfo.getHashCode())); + + System.out.println(targetInfo.getPointerKind()); + + DebugHostSymbolEnumerator enumerator = + targetInfo.enumerateChildren(SymbolKind.SYMBOL, null); + DebugHostSymbol1 next; + while ((next = enumerator.getNext()) != null) { + System.out.println(next.getName()); + } + } + } + + @Test + public void testEnvEx() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + //control.execute(".server tcp:port=54321"); + + HDMAUtil util = new HDMAUtil(access); + DebugHost host = util.getHost(); + //DebugHostContext currentContext = host.getCurrentContext(); + //DataModelManager1 manager = util.getManager(); + + ModelObject currentProcess = util.getCurrentProcess(); + ModelObject env = currentProcess.getKeyValue("Environment"); + ModelObject eb = env.getKeyValue("EnvironmentBlock"); + + client.getControl().execute("dt nt!_PEB"); + client.getControl() + .execute( + "dx Debugger.State.DebuggerVariables.curprocess.Environment.EnvironmentBlock"); + System.err.println(eb.hashCode()); + System.err.println(eb.getLocation().Offset); + DebugHostType2 targetInfo = (DebugHostType2) eb.getTargetInfo(); + DebugHostSymbolEnumerator enumerator = + targetInfo.enumerateChildren(SymbolKind.SYMBOL, null); + DebugHostSymbol1 next; + while ((next = enumerator.getNext()) != null) { + System.out.println(next.getName()); + } + System.err.println(targetInfo.getTypeKind()); + + System.err.println(eb.getKeyValueMap().size()); + System.err.println(eb.getElements().size()); + RawEnumerator enumeratorR = eb.enumerateRawValues(SymbolKind.SYMBOL.ordinal(), 0); + String nextR; + // SYMBOL, SYMBOL_CONSTANT, SYMBOL_DATA, SYMBOL_FIELD return the same values + // SYMBOL_BASE_CLASS, SYMBOL_PUBLIC, SYMBOL_TYPE return nothing + // SYMBOL_FUNCTION, SYMBOL_MODULE throw an error + while ((nextR = enumeratorR.getNext()) != null) { + System.out.println(nextR); + System.out.println(enumeratorR.getKind()); + } + } + } + + /* + @Test + public void testPrintln() { + CompletableFuture cb = new CompletableFuture<>(); + client.setOutputCallbacks(new DebugOutputCallbacks() { + @Override + public void output(int mask, String text) { + System.out.print(text); + cb.complete(text); + } + }); + control.outln("Hello, World!"); + String back = cb.getNow(null); + // NOTE: I'd like to be precise wrt/ new lines, but it seems to vary with version. + assertEquals("Hello, World!", back.trim()); + } + */ + + @Test + public void testGetProcessSystemIds() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + ModelObject currentSession = util.getCurrentSession(); + String ctlid = util.getCtlId(currentSession); + List procs = util.getRunningProcesses(ctlid); + System.out.println("Total: " + procs.size()); + procs.sort(null); + for (ModelObject p : procs) { + System.out.println("ID: " + util.getCtlId(p)); + } + } + } + + @Test + public void testGetProcesses() { + DebugSystemObjects so = access.getClient().getSystemObjects(); + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + System.out.println(so.getNumberProcesses()); + + HDMAUtil util = new HDMAUtil(access); + ModelObject currentProcess = util.getCurrentProcess(); + String ctlid = util.getCtlId(currentProcess); + System.out.println("ID: " + ctlid); + List processes = so.getProcesses(); + for (DebugProcessId id : processes) { + System.out.println("ID: " + id); + DebugProcessId pid = so.getProcessIdBySystemId(Integer.parseUnsignedInt(ctlid, 16)); + System.out.println("ID: " + pid); + } + } + } + + @Test + public void testGetProcessDescriptions() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + ModelObject currentSession = util.getCurrentSession(); + String ctlid = util.getCtlId(currentSession); + List procs = util.getRunningProcesses(ctlid); + System.out.println("Total: " + procs.size()); + procs.sort(null); + for (ModelObject p : procs) { + try { + System.out.println(p.toString()); + } + catch (COMException e) { + System.out.println( + "Error with PID " + util.getCtlId(p) + ": " + e.getMessage()); + } + } + } + } + + @Test + public void testGetRegistersNew() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + ModelObject currentThread = util.getCurrentThread(); + ModelObject registers = currentThread.getKeyValue("Registers").getKeyValue("User"); + Map map = registers.getKeyValueMap(); + for (String key : map.keySet()) { + ModelObject mo = map.get(key); + Object value = mo.getValue(); + System.out.println(key + ":" + Long.toHexString(Long.parseLong(value.toString()))); + } + } + } + + @Test + public void testGetAllRegisters() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + WrappedDbgModel dbgmodel = new WrappedDbgModel(access); + Set descs = dbgmodel.getAllRegisterDescriptions(); + for (DebugRegisterDescription desc : descs) { + System.out.println(desc.index + ":" + desc.name); + } + } + } + + @Test + public void testGetRegisters() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + List out = maker.execCapture("r"); + String expected = out.stream().filter(s -> s.startsWith("rax")).findAny().get(); + + WrappedDbgModel dbgmodel = new WrappedDbgModel(access); + DebugRegisters regs = dbgmodel.getRegisters(); + List indices = new ArrayList<>(); + int raxIdx = regs.getIndexByName("rax"); + int rbxIdx = regs.getIndexByName("rbx"); + int rcxIdx = regs.getIndexByName("rcx"); + indices.add(raxIdx); + indices.add(rbxIdx); + indices.add(rcxIdx); + Map values = + regs.getValues(DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE, indices); + + String actual = String.format("rax=%016x rbx=%016x rcx=%016x", + ((DebugInt64Value) values.get(raxIdx)).longValue(), + ((DebugInt64Value) values.get(rbxIdx)).longValue(), + ((DebugInt64Value) values.get(rcxIdx)).longValue()); + System.out.println(actual); + assertEquals(expected, actual); + } + } + + @Test + public void testSetCurrentThread() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + ModelObject currentProcess = util.getCurrentProcess(); + ModelObject currentThread = util.getCurrentThread(); + System.out.println(currentThread); + String ctlid = util.getCtlId(currentThread); + VARIANT v = new VARIANT(ctlid); + currentProcess.switchTo(util.getManager(), v); + currentThread = util.getCurrentThread(); + ; + System.out.println(currentThread); + } + } + + @Test + public void testGetElements() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + List children = + util.getElements(List.of("Debugger", "Sessions[0]", "Processes")); + for (ModelObject obj : children) { + System.err.println(obj.getSearchKey()); + } + } + } + + @Test + public void testGetAttributes() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + Map map = util.getAttributes(List.of("Debugger", "Sessions")); + for (String key : map.keySet()) { + System.err.println(key); + } + } + } + + @Test + public void testCall() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + DataModelManager1 manager = util.getManager(); + Pointer[] args = new Pointer[0]; + //VARIANT.ByReference vbr = new VARIANT.ByReference(v); + //ModelObject mo = manager.createIntrinsicObject(ModelObjectKind.OBJECT_INTRINSIC, vbr); + //args[0] = mo.getPointer(); + ModelObject sessions = util.getSessionOf(null); + ModelMethod f = sessions.getMethod("Last"); + ModelObject ret = f.call(sessions, 0, args); + System.err.println("=====>" + ret.getSearchKey()); + } + } + + @Test + public void testCallWithParameter() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + DebugHost host = util.getHost(); + DebugHostEvaluator2 eval = host.asEvaluator(); + Pointer[] args = new Pointer[1]; + ModelObject process = util.getCurrentProcess(); + ModelObject threads = process.getKeyValue("Threads"); + DebugHostContext context = host.getCurrentContext(); + ModelObject mo = + eval.evaluateExtendedExpression(context, new WString("c => c.Id"), threads); + args[0] = mo.getPointer(); + ModelMethod f = threads.getMethod("OrderByDescending"); + ModelObject ret = f.call(threads, 1, args); + List children = ret.getElements(); + for (ModelObject child : children) { + System.err.println("=====>" + child.getSearchKey()); + } + } + } + + @Test + public void testCallWithParametersEx() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + DebugHost host = util.getHost(); + DebugHostEvaluator2 eval = host.asEvaluator(); + ModelObject process = util.getCurrentProcess(); + ModelObject modules = process.getKeyValue("Modules"); + DebugHostContext context = host.getCurrentContext(); + ModelObject mo = eval.evaluateExtendedExpression(context, + new WString("OrderByDescending(c => c.BaseAddress)"), modules); + List children = mo.getElements(); + for (ModelObject child : children) { + ModelObject value = child.getKeyValue("BaseAddress"); + System.err.println("=====>" + child.getSearchKey() + ":" + value); + } + } + } + +/* + @Test + public void testSetSingleRegister() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugRegisters regs = client.getRegisters(); + regs.setValueByName("rax", new DebugInt64Value(0x0102030405060708L)); + + List out = maker.execCapture("r"); + String actual = + out.stream().filter(s -> s.startsWith("rax")).findAny().get().split("\\s+")[0]; + assertEquals("rax=0102030405060708", actual); + } + } + + @Test + public void testSetRegisters() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugRegisters regs = client.getRegisters(); + // Purposefully choosing non-linked variant. + // Want to know that order does not make a difference. + Map values = new HashMap<>(); + values.put(regs.getIndexByName("rax"), new DebugInt64Value(0x0102030405060708L)); + values.put(regs.getIndexByName("rbx"), new DebugInt64Value(0x1122334455667788L)); + values.put(regs.getIndexByName("rcx"), new DebugInt64Value(0x8877665544332211L)); + regs.setValues(DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE, values); + + List out = maker.execCapture("r"); + String actual = out.stream().filter(s -> s.startsWith("rax")).findAny().get(); + assertEquals("rax=0102030405060708 rbx=1122334455667788 rcx=8877665544332211", actual); + } + } + + @Test + public void testQueryVirtual() { + // Also, an experiment to figure out how it works + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + List collected1 = new ArrayList<>(); + try { + long last = 0; + long offset = 0; + do { + System.out.print(Long.toHexString(offset) + ": "); + DebugMemoryBasicInformation info = client.getDataSpaces().queryVirtual(offset); + System.out.println(info); + collected1.add(info); + last = offset; + offset += info.regionSize; + } + while (Long.compareUnsigned(last, offset) < 0); + } + catch (COMException e) { + if (!e.getMessage().contains("HRESULT: 80004002")) { + throw e; + } + } + + List collected2 = new ArrayList<>(); + for (DebugMemoryBasicInformation info : client.getDataSpaces().iterateVirtual(0)) { + collected2.add(info); + } + + assertTrue(collected1.size() > 0); + assertEquals(collected1, collected2); + } + } +*/ + + @Test + public void testModules() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + DebugHost host = util.getHost(); + DebugHostSymbols symbols = host.asSymbols(); + DebugHostSymbolEnumerator enumerator = + symbols.enumerateModules(util.getCurrentContext()); + DebugHostSymbol1 next; + while ((next = enumerator.getNext()) != null) { + DebugHostModule1 module = next.asModule(); + System.out.println(" Ctxt: " + module.getContext()); + System.out.println( + " Kind: " + SymbolKind.values()[module.getSymbolKind().ordinal()]); + System.out.println(" Load: " + module.getName().toString()); + DebugHostModule1 containingModule = module.getContainingModule(); + System.out.println(" CMod: " + containingModule); + + System.out.println(" Img: " + module.getImageName(true).toString()); + LOCATION base = module.getBaseLocation(); + System.out.println(" Base: " + Long.toHexString(base.Offset.longValue())); + module.getVersion(); + DebugHostModuleImpl1 impl = (DebugHostModuleImpl1) module; + System.out.println(" FVer: " + Long.toHexString(impl.getFileVersion())); + System.out.println( + " Pvar: " + Long.toHexString(impl.getProductVersion())); + } + } + } + + @Test + public void testStack() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + HDMAUtil util = new HDMAUtil(access); + ModelObject currentStack = util.getCurrentStack(); + ModelObject frames = currentStack.getKeyValue("Frames"); + List children = frames.getElements(); + for (ModelObject child : children) { + System.out.println(child); + Map map = child.getKeyValueMap(); + for (String key : map.keySet()) { + ModelObject value = map.get(key); + System.out.println(key + ":" + value); + if (value.getKind().equals(ModelObjectKind.OBJECT_PROPERTY_ACCESSOR)) { + Unknown v = (Unknown) value.getIntrinsicValue(); + System.out.println(v); + ModelPropertyAccessorInternal ifc = + ModelPropertyAccessorInternal.tryPreferredInterfaces(v::QueryInterface); + System.out.println(ifc); + ModelObject result = ifc.getValue("All", null); + System.out.println(result); + } + } + } + } + } + + @Test + public void testReadMemory() throws FileNotFoundException, IOException { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + int len = 256; + + HDMAUtil util = new HDMAUtil(access); + DebugHostContext currentContext = util.getCurrentContext(); + DebugHost host = util.getHost(); + DebugHostSymbols symbols = host.asSymbols(); + DebugHostModule1 module = symbols.findModuleByName(currentContext, "notepad"); + LOCATION base = module.getBaseLocation(); + System.out.println("Base: " + Long.toHexString(base.Offset.longValue())); + + DebugHostMemory1 memory = host.asMemory(); + + ByteBuffer data = ByteBuffer.allocate(len); + memory.readBytes(currentContext, base, data, len); + System.out.println(NumericUtilities.convertBytesToString(data.array())); + + // TODO: Avoid hardcoding path to notepad + try (FileInputStream fis = new FileInputStream("C:\\Windows\\notepad.exe")) { + byte[] fromFile = new byte[len]; + fis.read(fromFile); + System.out.println(NumericUtilities.convertBytesToString(fromFile)); + assertArrayEquals(fromFile, data.array()); + } + + //data.clear(); + //data.putInt(0x12345678); + //client.getDataSpaces().readVirtual(notepadModule.getBase(), data, data.remaining()); + //data.flip(); + + //assertEquals(0x12345678, data.getInt()); + } + } + + @Test + public void testScriptInterface() throws FileNotFoundException, IOException { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + client.getControl() + .execute( + ".load c:\\Software\\windbg\\amd64\\winext\\JSProvider.dll"); + client.getControl().execute(".load c:\\Software\\windbg\\amd64\\ttd\\TTDReplayCPU.dll"); + client.getControl().execute(".load c:\\Software\\windbg\\amd64\\ttd\\TTDAnalyze.dll"); + client.getControl().execute(".load c:\\Software\\windbg\\amd64\\ttd\\TtdExt.dll"); + client.getControl().execute("!tt 1:0"); + + HDMAUtil util = new HDMAUtil(access); + DebugHostContext currentContext = util.getCurrentContext(); + DebugHost host = util.getHost(); + DebugHostScriptHost scriptHost = host.asScriptHost(); + DataModelManager1 manager = util.getManager(); + DataModelScriptManager scriptManager = manager.asScriptManager(); + //DataModelScriptProvider jsProvider = + // scriptManager.findProviderForScriptType("JavaScript"); + DataModelScriptProviderEnumerator enumerator = + scriptManager.enumeratorScriptProviders(); + DataModelScriptProvider next; + while ((next = enumerator.getNext()) != null) { + System.out.println(next.getName()); + DataModelScriptTemplateEnumerator enumerator2 = next.enumerateTemplates(); + DataModelScriptTemplate nextTemplate; + while ((nextTemplate = enumerator2.getNext()) != null) { + System.out.println(nextTemplate.getName()); + System.out.println(nextTemplate.getDescription()); + } + } + } + } + + @Test + public void testBreakpoints() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugBreakpoint bpt = control.addBreakpoint(BreakType.CODE); + System.out.println("Breakpoint id: " + bpt.getId()); + System.out.println("Flags: " + bpt.getFlags()); + DebugBreakpoint bpt2 = control.getBreakpointById(bpt.getId()); + assertEquals(bpt, bpt2); + + HDMAUtil util = new HDMAUtil(access); + ModelObject currentProcess = util.getCurrentProcess(); + + ModelObject bpts = currentProcess.getKeyValue("Debug").getKeyValue("Breakpoints"); + List children = bpts.getElements(); + for (ModelObject child : children) { + //List gc = child.getChildren(); + Map pairs = child.getKeyValueMap(); + for (String key : pairs.keySet()) { + System.out.println(key); + } + } + + } + } + + @Test + public void testSymbols() { + try (ProcMaker maker = + new ProcMaker("c:\\Users\\dbmilla\\Desktop\\ConsoleApplication1.exe")) { + maker.start(); + + DebugSymbols ds = client.getSymbols(); + System.out.println(ds.getSymbolPath()); + ds.setSymbolPath("srv*c:\\Symbols*https://msdl.microsoft.com/downloads/symbols"); + System.out.println(ds.getSymbolOptions()); + ds.setSymbolOptions(0x80000046); + + WrappedDbgModel dbgmodel = new WrappedDbgModel(access); + ModelObject symbolSettings = dbgmodel.getUtil().getSettings().getKeyValue("Symbols"); + ModelObject sympath = symbolSettings.getKeyValue("Sympath"); + sympath.getIntrinsicValue(); + List modules2 = dbgmodel.getDebugHostModules(); + for (DebugHostModule1 module : modules2) { + System.out.println(module.getName().toString()); + System.out.println(module.getSymbolKind()); + + List symbol0 = + client.getSymbols().getSymbolIdsByName(""); + System.out.println(symbol0.size()); + + try { + DebugHostSymbolEnumerator enumerator = + module.enumerateChildren(SymbolKind.SYMBOL_PUBLIC, null); + if (enumerator != null) { + DebugHostSymbol1 next; + int count = 0; + while ((next = enumerator.getNext()) != null) { + + System.out.println(next.getName() + ":" + next.getSymbolKind()); + if (next.getSymbolKind().equals(SymbolKind.SYMBOL_PUBLIC)) { + DebugHostPublic pub = next.asPublic(); + try { + System.out.println(pub.getLocationKind()); + System.out.println(pub.getLocation().Offset); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + count++; + } + System.out.println(count); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } + + Set symbols = new LinkedHashSet<>(); + Set modules = new LinkedHashSet<>(); + for (DebugSymbolName sym : client.getSymbols().iterateSymbolMatches("*")) { + String[] parts = sym.name.split("!"); + symbols.add(sym); + modules.add(parts[0]); + } + System.out.println("Total Symbols: " + symbols.size()); + System.out.println("Total Modules (by symbol name): " + modules.size()); + + // These make assumptions that could be broken later. + // It used to expect at least 10 modules (devised when testing on Win7). Now it's 5! + assertTrue("Fewer than 1000 symbols: " + symbols.size(), symbols.size() > 1000); + assertTrue("Fewer than 3 modules: " + modules.size(), modules.size() > 3); + } + } + + //@Test(expected = COMException.class) + public void testModuleOutOfBounds() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + DebugModule umod = client.getSymbols() + .getModuleByIndex( + client.getSymbols().getNumberLoadedModules() + 1); + System.out.println(umod.getBase()); + } + } + + @Test + public void testQueryVirtualWithModule() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + for (DebugMemoryBasicInformation info : client.getDataSpaces().iterateVirtual(0)) { + if (info.state != PageState.FREE) { + DebugModule mod = null; + String name = "[NONE]"; + try { + mod = client.getSymbols().getModuleByOffset(info.baseAddress, 0); + name = mod.getName(DebugModuleName.IMAGE); + } + catch (COMException e) { + name = "[ERR:" + e + "]"; + } + System.out.println(String.format("%016x", info.baseAddress) + ":" + + Long.toHexString(info.regionSize) + ":" + info.state + " from " + name + + " " + info.type + info.protect); + } + } + } + } + + @Test + public void testSymbolInfo() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + int count = 0; + for (DebugSymbolId symid : client.getSymbols().getSymbolIdsByName("ntdll!*")) { + //System.out.println(symid); + DebugSymbolEntry syment = client.getSymbols().getSymbolEntry(symid); + if (syment.typeId != 0) { + System.out.println(" " + syment); + } + count++; + } + + assertTrue(count > 10); + } + } + + //@Test + public void testWriteMemory() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + // TODO: How to write to protected memory? + // Debugger should be able to modify program code. + DebugMemoryBasicInformation writable = null; + space: for (DebugMemoryBasicInformation info : client.getDataSpaces() + .iterateVirtual( + 0)) { + for (PageProtection prot : info.protect) { + if (prot.isWrite()) { + writable = info; + break space; + } + } + } + if (writable == null) { + throw new AssertionError("No writable pages?"); + } + System.out.println("writable: " + writable); + ByteBuffer toWrite = ByteBuffer.allocate(10); + toWrite.putInt(0x12345678); + toWrite.putInt(0x89abcdef); + toWrite.putShort((short) 0x5555); + toWrite.flip(); + client.getDataSpaces().writeVirtual(writable.baseAddress, toWrite, toWrite.remaining()); + + ByteBuffer toRead = ByteBuffer.allocate(10); + client.getDataSpaces().readVirtual(writable.baseAddress, toRead, toRead.remaining()); + + assertArrayEquals(toWrite.array(), toRead.array()); + } + } + + /* + @Test + public void testFreezeUnfreeze() { + try (ProcMaker maker = new ProcMaker("notepad")) { + maker.start(); + + // Trying to see if any events will help me track frozen threads + System.out.println("****Freezing"); + control.execute("~0 f"); + System.out.println("****Unfreezing"); + control.execute("~0 u"); + System.out.println("****Done"); + // Well, that result stinks. + // There is no event to tell me about frozenness + } + } + */ + + /* + @Test + @Ignore("I can't find a reliable means to detect the last thread. " + + "There's supposed to be an initial break, but it is rarely reported. " + + "I thought about toolhelp, but that presumes local live debugging.") + public void testMultiThreadAttach() throws Exception { + // I need to see how to attach to multi-threaded processes. There must be some event + // or condition to indicate when all threads have been discovered. + String specimen = + Application.getOSFile("sctldbgeng", "expCreateThreadSpin.exe").getCanonicalPath(); + client.setOutputCallbacks(new DebugOutputCallbacks() { + @Override + public void output(int mask, String text) { + System.out.print(text); + System.out.flush(); + } + }); + client.setEventCallbacks(new DebugEventCallbacksAdapter() { + @Override + public DebugStatus breakpoint(DebugBreakpoint bp) { + control.outln("*** Breakpoint: " + bp); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { + control.outln("*** Exception: " + exception + "," + firstChance); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + control.outln("*** CreateThread: " + debugThreadInfo); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + control.outln("*** CreateProcess: " + debugProcessInfo); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitThread(int exitCode) { + control.outln("*** ExitThread: code=" + exitCode + ", " + + client.getSystemObjects().getEventThread()); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + control.outln("*** ExitProcess: code=" + exitCode + ", " + + client.getSystemObjects().getEventProcess()); + System.out.println("Threads: " + client.getSystemObjects().getThreads()); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus changeEngineState(BitmaskSet flags, + long argument) { + if (flags.contains(ChangeEngineState.EXECUTION_STATUS)) { + control.outln("*** ExecutionStatus: " + control.getExecutionStatus()); + } + return DebugStatus.NO_CHANGE; + } + }); + try (DummyProc proc = new DummyProc(specimen)) { + System.out.println("Started " + specimen + " with PID=" + proc.pid); + Thread.sleep(1000); + System.out.println("Attaching..."); + client.attachProcess(client.getLocalServer(), proc.pid, BitmaskSet.of()); + if (true) { + for (int i = 0; i < 10; i++) { + System.out.println("WAIT " + i + "..."); + control.waitForEvent(100); + System.out.println("STATUS: " + control.getExecutionStatus()); + System.out.println("DONE " + i); + // control.execute("~*"); + } + } + } + finally { + client.setEventCallbacks(null); + } + } + */ + + public static abstract class NoisyDebugEventCallbacksAdapter + extends DebugEventCallbacksAdapter { + final DebugStatus defaultStatus; + + public NoisyDebugEventCallbacksAdapter(DebugStatus defaultStatus) { + this.defaultStatus = defaultStatus; + } + + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + Msg.info(this, "createProcess: " + debugProcessInfo); + return defaultStatus; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + Msg.info(this, "createThread: " + debugThreadInfo); + return defaultStatus; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + Msg.info(this, "exitProcess: " + Integer.toHexString(exitCode)); + return defaultStatus; + } + + @Override + public DebugStatus breakpoint(DebugBreakpoint bp) { + Msg.info(this, "breakpoint: " + bp); + return defaultStatus; + } + + @Override + public DebugStatus changeDebuggeeState(BitmaskSet flags, + long argument) { + Msg.info(this, "changeDebuggeeState: " + flags + ", " + argument); + return defaultStatus; + } + + @Override + public DebugStatus changeEngineState(BitmaskSet flags, long argument) { + Msg.info(this, "changeEngineState: " + flags + ", " + argument); + return defaultStatus; + } + + @Override + public DebugStatus changeSymbolState(BitmaskSet flags, long argument) { + Msg.info(this, "changeSymbolState: " + flags + ", " + argument); + return defaultStatus; + } + + @Override + public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { + Msg.info(this, "exception: " + exception + ", " + firstChance); + return defaultStatus; + } + + @Override + public DebugStatus exitThread(int exitCode) { + Msg.info(this, "exitThread: " + Integer.toHexString(exitCode)); + return defaultStatus; + } + + @Override + public DebugStatus loadModule(DebugModuleInfo debugModuleInfo) { + Msg.info(this, "loadModule: " + debugModuleInfo); + return defaultStatus; + } + + @Override + public DebugStatus sessionStatus(SessionStatus status) { + Msg.info(this, "sessionStatus: " + status); + return defaultStatus; + } + + @Override + public DebugStatus systemError(int error, int level) { + Msg.info(this, "systemError: " + error + ", " + level); + return defaultStatus; + } + + @Override + public DebugStatus unloadModule(String imageBaseName, long baseOffset) { + Msg.info(this, "unloadModule: " + imageBaseName + ", " + baseOffset); + return defaultStatus; + } + } + + protected class ProcMaker implements AutoCloseable { + public ProcMaker(String cmdLine) { + this.cmdLine = cmdLine; + } + + final String cmdLine; + + final CompletableFuture procInfo = new CompletableFuture<>(); + final CompletableFuture threadInfo = new CompletableFuture<>(); + final CompletableFuture procExit = new CompletableFuture<>(); + + StringBuilder outputCapture = null; + + public void start() { + client.setEventCallbacks(new NoisyDebugEventCallbacksAdapter(DebugStatus.NO_CHANGE) { + @Override + public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { + super.createProcess(debugProcessInfo); + procInfo.complete(debugProcessInfo); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { + super.createThread(debugThreadInfo); + threadInfo.complete(debugThreadInfo); + return DebugStatus.BREAK; + } + + @Override + public DebugStatus exitProcess(int exitCode) { + super.exitProcess(exitCode); + procExit.complete(exitCode); + return DebugStatus.BREAK; + } + }); + client.setOutputCallbacks(new DebugOutputCallbacks() { + @Override + public void output(int mask, String text) { + System.out.print(text); + if (outputCapture != null) { + outputCapture.append(text); + } + } + }); + + Msg.debug(this, "Starting " + cmdLine + " with client " + client); + control.execute(".create " + cmdLine); + control.waitForEvent(); + DebugProcessInfo pi = procInfo.getNow(null); + assertNotNull(pi); + control.execute("g"); + control.waitForEvent(); + DebugThreadInfo ti = threadInfo.getNow(null); + assertNotNull(ti); + } + + public void kill() { + Msg.debug(this, "Killing " + cmdLine); + control.execute(".kill"); + control.waitForEvent(); + Integer exitCode = procExit.getNow(null); + client.setOutputCallbacks(null); + assertNotNull(exitCode); + } + + public List execCapture(String command) { + try { + outputCapture = new StringBuilder(); + control.execute(command); + return Arrays.asList(outputCapture.toString().split("\n")); + } + finally { + outputCapture = null; + } + } + + @Override + public void close() { + if (procInfo.isDone() && !procExit.isDone()) { + kill(); + } + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/Module.manifest b/Ghidra/Debug/Debugger-agent-gdb/Module.manifest new file mode 100644 index 0000000000..d7346b185e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/Module.manifest @@ -0,0 +1 @@ +MODULE FILE LICENSE: lib/jython-standalone-2.7.1.jar Apache License 2.0 diff --git a/Ghidra/Debug/Debugger-agent-gdb/build.gradle b/Ghidra/Debug/Debugger-agent-gdb/build.gradle new file mode 100644 index 0000000000..406f75a553 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/build.gradle @@ -0,0 +1,87 @@ +apply from: "$rootProject.projectDir/gradle/javaProject.gradle" +apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle" +apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" +apply from: "$rootProject.projectDir/gradle/nativeProject.gradle" +apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" + +apply plugin: 'eclipse' +eclipse.project.name = 'Debug Debugger-agent-gdb' + +dependencies { + compile project(':Framework-AsyncComm') + compile project(':Framework-Debugging') + compile project(':Debugger-gadp') + compile 'org.python:jython-standalone:2.7.1' + + testCompile project(path: ':Debugger-gadp', configuration: 'testArtifacts') +} + +def boolean filterJar(File jarfile) { + if (jarfile.name.contains("gradle-api")) { + return false + } else if (jarfile.name.contains("groovy-all")) { + return false + } else if (jarfile.name.contains("gradle-installation-beacon")) { + return false + } + return true +} + +jar { + manifest { + attributes['Main-Class'] = 'agent.gdb.gadp.GdbGadpServer' + } +} + +task configureNodepJar { + doLast { + configurations.runtime.files.forEach { + if (filterJar(it)) { + nodepJar.from(zipTree(it)) + } + } + } +} + +task nodepJar(type: Jar) { + inputs.file(file(jar.archivePath)) + dependsOn(configureNodepJar) + dependsOn(jar) + + appendix = 'nodep' + manifest { + attributes['Main-Class'] = 'agent.gdb.gadp.GdbGadpServer' + } + + from(zipTree(jar.archivePath)) +} + +task executableJar { + ext.execsh = file("src/main/sh/execjar.sh") + ext.jarfile = file(nodepJar.archivePath) + ext.outjar = file("${buildDir}/bin/gadp-agent-gdb") + dependsOn(nodepJar) + inputs.file(execsh) + inputs.file(jarfile) + outputs.file(outjar) + doLast { + outjar.parentFile.mkdirs() + outjar.withOutputStream { output -> + execsh.withInputStream { input -> + output << input + } + jarfile.withInputStream { input -> + output << input + } + } + exec { + commandLine("chmod", "+x", outjar) + } + } +} + +test { + if ("linux64".equals(getCurrentPlatformName())) { + dependsOn(":Framework-Debugging:testSpecimenLinux64") + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/certification.manifest b/Ghidra/Debug/Debugger-agent-gdb/certification.manifest new file mode 100644 index 0000000000..303a2099eb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/certification.manifest @@ -0,0 +1,6 @@ +##VERSION: 2.0 +##MODULE IP: Jython License +Module.manifest||GHIDRA||||END| +build.gradle||GHIDRA||||END| +src/main/resources/session.py||GHIDRA||||END| +src/main/sh/execjar.sh||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbInJvmDebuggerModelFactory.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbInJvmDebuggerModelFactory.java new file mode 100644 index 0000000000..2c690a30e3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbInJvmDebuggerModelFactory.java @@ -0,0 +1,76 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb; + +import java.util.concurrent.CompletableFuture; + +import agent.gdb.gadp.GdbLocalDebuggerModelFactory; +import agent.gdb.manager.GdbManager; +import agent.gdb.model.impl.GdbModelImpl; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.LocalDebuggerModelFactory; +import ghidra.dbg.util.ConfigurableFactory.FactoryDescription; +import ghidra.util.classfinder.ExtensionPointProperties; + +/** + * Note this is in the testing source because it's not meant to be shipped in the release.... That + * may change if it proves stable, though, no? + */ +@FactoryDescription( // + brief = "IN-VM GNU gdb local debugger", // + htmlDetails = "Launch a GDB session in this same JVM" // +) +@ExtensionPointProperties(priority = 80) +public class GdbInJvmDebuggerModelFactory implements LocalDebuggerModelFactory { + + private String gdbCmd = GdbManager.DEFAULT_GDB_CMD; + @FactoryOption("GDB launch command") + public final Property gdbCommandOption = + Property.fromAccessors(String.class, this::getGdbCommand, this::setGdbCommand); + + private boolean existing = false; + @FactoryOption("Use existing session via new-ui") + public final Property useExistingOption = + Property.fromAccessors(boolean.class, this::isUseExisting, this::setUseExisting); + + @Override + public CompletableFuture build() { + GdbModelImpl model = new GdbModelImpl(); + return model.startGDB(gdbCmd, new String[] {}).thenApply(__ -> model); + } + + @Override + public boolean isCompatible() { + return GdbLocalDebuggerModelFactory.checkGdbPresent(gdbCmd); + } + + public String getGdbCommand() { + return gdbCmd; + } + + public void setGdbCommand(String gdbCmd) { + this.gdbCmd = gdbCmd; + } + + public boolean isUseExisting() { + return existing; + } + + public void setUseExisting(boolean existing) { + this.existing = existing; + gdbCommandOption.setEnabled(!existing); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/FdInputStream.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/FdInputStream.java new file mode 100644 index 0000000000..df359b469f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/FdInputStream.java @@ -0,0 +1,80 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.ffi.linux; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +import jnr.posix.POSIX; +import jnr.posix.POSIXFactory; + +/** + * An input stream that wraps a native POSIX file descriptor + * + * WARNING: This class makes use of jnr-ffi to invoke native functions. An invalid file descriptor + * is generally detected, but an incorrect, but valid file descriptor may cause undefined behavior. + */ +public class FdInputStream extends InputStream { + private static final POSIX LIB_POSIX = POSIXFactory.getNativePOSIX(); + + private final int fd; + private boolean closed = false; + + /** + * Wrap the given file descriptor in an {@link InputStream} + * + * @param fd the file descriptor + */ + FdInputStream(int fd) { + this.fd = fd; + } + + @Override + public synchronized int read() throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + byte[] buf = new byte[1]; + if (0 == read(buf)) { + return -1; + } + return buf[0] & 0x0FF; + } + + @Override + public synchronized int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + @Override + public synchronized int read(byte[] b, int off, int len) throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + ByteBuffer buf = ByteBuffer.wrap(b, off, len); + int ret = LIB_POSIX.read(fd, buf, len); + if (ret < 0) { + throw new IOException(LIB_POSIX.strerror(LIB_POSIX.errno())); + } + return ret; + } + + @Override + public synchronized void close() throws IOException { + closed = true; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/FdOutputStream.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/FdOutputStream.java new file mode 100644 index 0000000000..2b35ec0b40 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/FdOutputStream.java @@ -0,0 +1,78 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.ffi.linux; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +import jnr.posix.POSIX; +import jnr.posix.POSIXFactory; + +/** + * An output stream that wraps a native POSIX file descriptor + * + * WARNING: This class makes use of jnr-ffi to invoke native functions. An invalid file descriptor + * is generally detected, but an incorrect, but valid file descriptor may cause undefined behavior. + */ +public class FdOutputStream extends OutputStream { + private static final POSIX LIB_POSIX = POSIXFactory.getNativePOSIX(); + + private final int fd; + private boolean closed = false; + + /** + * Wrap the given file descriptor in an {@link OutputStream} + * + * @param fd the file descriptor + */ + FdOutputStream(int fd) { + this.fd = fd; + } + + @Override + public synchronized void write(int b) throws IOException { + write(new byte[] { (byte) b }); + } + + @Override + public synchronized void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + @Override + public synchronized void write(byte[] b, int off, int len) throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + ByteBuffer buf = ByteBuffer.wrap(b, off, len); + + int total = 0; + do { + int ret = LIB_POSIX.write(fd, buf, len - total); + if (ret < 0) { + throw new IOException(LIB_POSIX.strerror(LIB_POSIX.errno())); + } + total += ret; + } + while (total < len); + } + + @Override + public synchronized void close() throws IOException { + closed = true; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/Pty.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/Pty.java new file mode 100644 index 0000000000..32cf1d146b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/Pty.java @@ -0,0 +1,153 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.ffi.linux; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import ghidra.util.Msg; +import jnr.ffi.Pointer; +import jnr.ffi.byref.IntByReference; +import jnr.posix.POSIX; +import jnr.posix.POSIXFactory; + +/** + * A pseudo-terminal + * + * A pseudo-terminal is essentially a two way pipe where one end acts as the master, and the other + * acts as the slave. The process opening the pseudo-terminal is given a handle to both ends. The + * slave end is generally given to a subprocess, possibly designating the pty as the controlling tty + * of a new session. This scheme is how, for example, an SSH daemon starts a new login shell. The + * shell is given the slave end, and the master end is presented to the SSH client. + * + * This is more powerful than controlling a process via standard in and standard out. 1) Some + * programs detect whether or not stdin/out/err refer to the controlling tty. For example, a program + * should avoid prompting for passwords unless stdin is the controlling tty. Using a pty can provide + * a controlling tty that is not necessarily controlled by a user. 2) Terminals have other + * properties and can, e.g., send signals to the foreground process group (job) by sending special + * characters. Normal characters are passed to the slave, but special characters may be interpreted + * by the terminal's line discipline. A rather common case is to send Ctrl-C (character + * 003). Using stdin, the subprocess simply reads 003. With a properly-configured pty and session, + * the subprocess is interrupted (sent SIGINT) instead. + * + * This class opens a pseudo-terminal and presents both ends as individual handles. The master end + * simply provides an input and output stream. These are typical byte-oriented streams, except that + * the data passes through the pty, subject to interpretation by the OS kernel. On Linux, this means + * the pty will apply the configured line discipline. Consult the host OS documentation for special + * character sequences. + * + * The slave end also provides the input and output streams, but it is uncommon to use them from the + * same process. More likely, subprocess is launched in a new session, configuring the slave as the + * controlling terminal. Thus, the slave handle provides methods for obtaining the slave pty file + * name and/or spawning a new session. Once spawned, the master end is used to control the session. + * + * Example: + * + *

+ * Pty pty = Pty.openpty();
+ * pty.getSlave().session("bash");
+ * 
+ * PrintWriter writer = new PrintWriter(pty.getMaster().getOutputStream());
+ * writer.println("echo test");
+ * BufferedReader reader =
+ * 	new BufferedReader(new InputStreamReader(pty.getMaster().getInputStream()));
+ * System.out.println(reader.readLine());
+ * System.out.println(reader.readLine());
+ * 
+ * pty.close();
+ * 
+ */ +public class Pty implements AutoCloseable { + private static final POSIX LIB_POSIX = POSIXFactory.getNativePOSIX(); + + private final int amaster; + private final int aslave; + private final String name; + private boolean closed = false; + + /** + * Open a new pseudo-terminal + * + * Implementation note: On Linux, this invokes the native {@code openpty()} function. See the + * Linux manual for details. + * + * @return new new Pty + * @throws IOException if openpty fails + */ + public static Pty openpty() throws IOException { + // TODO: Support termp and winp? + IntByReference m = new IntByReference(); + IntByReference s = new IntByReference(); + Pointer n = Pointer.wrap(jnr.ffi.Runtime.getSystemRuntime(), ByteBuffer.allocate(1024)); + if (Util.INSTANCE.openpty(m, s, n, null, null) < 0) { + int errno = LIB_POSIX.errno(); + throw new IOException(errno + ": " + LIB_POSIX.strerror(errno)); + } + return new Pty(m.intValue(), s.intValue(), n.getString(0)); + } + + Pty(int amaster, int aslave, String name) { + Msg.debug(this, "New Pty: " + name + " at (" + amaster + "," + aslave + ")"); + this.amaster = amaster; + this.aslave = aslave; + this.name = name; + } + + /** + * Get a handle to the master side of the pty + * + * @return the master handle + */ + public PtyMaster getMaster() { + return new PtyMaster(amaster); + } + + /** + * Get a handle to the slave side of the pty + * + * @return the slave handle + */ + public PtySlave getSlave() { + return new PtySlave(aslave, name); + } + + /** + * Closes both ends of the pty + * + * This only closes this process's handles to the pty. For the master end, this should be the + * only process with a handle. The slave end may be opened by any number of other processes. + * More than likely, however, those processes will terminate once the master end is closed, + * since reads or writes on the slave will produce EOF or an error. + * + * @throws IOException if an I/O error occurs + */ + @Override + public synchronized void close() throws IOException { + if (closed) { + return; + } + int result; + result = LIB_POSIX.close(aslave); + if (result < 0) { + throw new IOException(LIB_POSIX.strerror(LIB_POSIX.errno())); + } + result = LIB_POSIX.close(amaster); + if (result < 0) { + throw new IOException(LIB_POSIX.strerror(LIB_POSIX.errno())); + } + closed = true; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtyEndpoint.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtyEndpoint.java new file mode 100644 index 0000000000..4ec078b624 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtyEndpoint.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.ffi.linux; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * A base class for either end of a pseudo-terminal + * + * This provides the input and output streams + */ +public class PtyEndpoint { + private final int fd; + + PtyEndpoint(int fd) { + this.fd = fd; + } + + /** + * Get the output stream for this end of the pty + * + * Writes to this stream arrive on the input stream for the opposite end, subject to the + * terminal's line discipline. + * + * @return the output stream + */ + public OutputStream getOutputStream() { + return new FdOutputStream(fd); + } + + /** + * Get the input stream for this end of the pty + * + * Writes to the output stream of the opposite end arrive here, subject to the terminal's line + * discipline. + * + * @return the input stream + */ + public InputStream getInputStream() { + return new FdInputStream(fd); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtyMaster.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtyMaster.java new file mode 100644 index 0000000000..3914936c34 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtyMaster.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.ffi.linux; + +/** + * The master end of a pseudo-terminal + */ +public class PtyMaster extends PtyEndpoint { + PtyMaster(int fd) { + super(fd); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtySessionLeader.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtySessionLeader.java new file mode 100644 index 0000000000..f52c8f373c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtySessionLeader.java @@ -0,0 +1,93 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.ffi.linux; + +import java.util.List; +import java.util.concurrent.Callable; + +import jnr.posix.POSIX; +import jnr.posix.POSIXFactory; + +public class PtySessionLeader { + private static final POSIX LIB_POSIX = POSIXFactory.getNativePOSIX(); + private static final int O_RDWR = 2; // TODO: Find this in libs + + public static void main(String[] args) throws Exception { + PtySessionLeader master = new PtySessionLeader(); + master.parseArgs(args); + master.run(); + } + + protected String ptyPath; + protected List subArgs; + + protected void parseArgs(String[] args) { + ptyPath = args[0]; + subArgs = List.of(args).subList(1, args.length); + } + + protected T checkErr(Callable r) throws Exception { + LIB_POSIX.errno(0); + T result = r.call(); + int errno = LIB_POSIX.errno(); + if (errno != 0) { + throw new RuntimeException("errno=" + errno + ": " + LIB_POSIX.strerror(errno)); + } + return result; + } + + protected void run() throws Exception { + /** This tells Linux to make this process the leader of a new session. */ + checkErr(() -> LIB_POSIX.setsid()); + + /** + * Open the TTY. On Linux, the first TTY opened since becoming a session leader becomes the + * session's controlling TTY. Other platforms, e.g., BSD may require an explicit IOCTL. + */ + int fd = checkErr(() -> LIB_POSIX.open(ptyPath, O_RDWR, 0)); + + /** Copy stderr to a backup descriptor, in case something goes wrong. */ + int bk = fd + 1; + checkErr(() -> LIB_POSIX.dup2(2, bk)); + + /** + * Copy the TTY fd over all standard streams. This effectively redirects the leader's + * standard streams to the TTY. + */ + checkErr(() -> LIB_POSIX.dup2(fd, 0)); + checkErr(() -> LIB_POSIX.dup2(fd, 1)); + checkErr(() -> LIB_POSIX.dup2(fd, 2)); + + /** + * At this point, we are the session leader and the named TTY is the controlling PTY. Now, + * exec the specified image with arguments as the session leader. Recall, this replaces the + * image of this process. + */ + try { + checkErr(() -> LIB_POSIX.execv(subArgs.get(0), subArgs.toArray(new String[0]))); + } + catch (Throwable t) { + try { + checkErr(() -> LIB_POSIX.dup2(bk, 2)); + } + catch (Throwable t2) { + // Catastrophic + System.exit(-1); + } + throw t; + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtySlave.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtySlave.java new file mode 100644 index 0000000000..2172404e67 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/PtySlave.java @@ -0,0 +1,145 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.ffi.linux; + +import java.io.*; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.file.Paths; +import java.util.*; + +/** + * The slave end of a pseudo-terminal + */ +public class PtySlave extends PtyEndpoint { + private final File file; + + PtySlave(int fd, String name) { + super(fd); + this.file = new File(name); + } + + /** + * Get the file referring to this pseudo-terminal + * + * @return the file + */ + public File getFile() { + return file; + } + + /** + * Spawn a subprocess in a new session whose controlling tty is this pseudo-terminal + * + * Implementation note: This uses {@link ProcessBuilder} to launch the subprocess. See its + * documentation for more details of the parameters of this method. + * + * Deep implementation note: This actually launches a Python script, which sets up the session + * and then executes the requested program. The requested program image replaces the Python + * interpreter so that the returned process is indeed a handle to the requested program, not a + * Python interpreter. Ordinarily, this does not matter, but it may be useful to know when + * debugging. Furthermore, if special characters are sent on the master before Python has + * executed the requested program, they may be received by the Python interpreter. For example, + * Ctrl-C might be received by Python by mistake if sent immediately upon spawning a new + * session. Users should send a simple command, e.g., "echo", to confirm that the requested + * program is active before sending special characters. + * + * @param args the image path and arguments + * @param env the environment + * @return a handle to the subprocess + * @throws IOException + */ + public Process session(String[] args, Map env) throws IOException { + return sessionUsingJavaLeader(args, env); + } + + protected Process sessionUsingJavaLeader(String[] args, Map env) + throws IOException { + final List argsList = new ArrayList<>(); + argsList.add("java"); + argsList.add("-cp"); + argsList.add(System.getProperty("java.class.path")); + argsList.add(PtySessionLeader.class.getCanonicalName()); + + argsList.add(file.getAbsolutePath()); + argsList.addAll(Arrays.asList(args)); + ProcessBuilder builder = new ProcessBuilder(argsList); + if (env != null) { + builder.environment().putAll(env); + } + builder.inheritIO(); + + return builder.start(); + } + + protected Process sessionUsingPythonLeader(String[] args, Map env) + throws IOException { + final List argsList = new ArrayList<>(); + argsList.add("python"); + argsList.add("-m"); + argsList.add("session"); + + argsList.add(file.getAbsolutePath()); + argsList.addAll(Arrays.asList(args)); + ProcessBuilder builder = new ProcessBuilder(argsList); + if (env != null) { + builder.environment().putAll(env); + } + String sourceLoc = getSourceLocationForResource("session.py").getAbsolutePath(); + //System.err.println("PYTHONPATH=" + sourceLoc); + builder.environment().put("PYTHONPATH", sourceLoc); + builder.inheritIO(); + + return builder.start(); + } + + public static File getSourceLocationForResource(String name) { + // TODO: Refactor this with SystemUtilities.getSourceLocationForClass() + URL url = PtySlave.class.getClassLoader().getResource(name); + String urlFile = url.getFile(); + try { + urlFile = URLDecoder.decode(urlFile, "UTF-8"); + } + catch (UnsupportedEncodingException e) { + // can't happen, since we know the encoding is correct + throw new AssertionError(e); + } + + if ("file".equals(url.getProtocol())) { + int packageLevel = Paths.get(name).getNameCount(); + File file = new File(urlFile); + for (int i = 0; i < packageLevel; i++) { + file = file.getParentFile(); + } + return file; + } + + if ("jar".equals(url.getProtocol())) { + // Running from Jar file + String jarPath = urlFile; + if (!jarPath.startsWith("file:")) { + return null; + } + + // strip off the 'file:' prefix and the jar path suffix after the + // '!' + jarPath = jarPath.substring(5, jarPath.indexOf('!')); + return new File(jarPath); + } + + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/Util.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/Util.java new file mode 100644 index 0000000000..f19e799ebf --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/ffi/linux/Util.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.ffi.linux; + +import jnr.ffi.LibraryLoader; +import jnr.ffi.Pointer; +import jnr.ffi.annotations.Out; +import jnr.ffi.byref.IntByReference; + +/** + * The interface for linking to {@code openpty} via jnr-ffi + */ +public interface Util { + Util INSTANCE = LibraryLoader.create(Util.class).load("util"); + + /** + * See the Linux manual pages + * + * @param amaster (purposefully undocumented here) + * @param aslave (purposefully undocumented here) + * @param name (purposefully undocumented here) + * @param termp (purposefully undocumented here) + * @param winp (purposefully undocumented here) + * @return (purposefully undocumented here) + */ + int openpty(@Out IntByReference amaster, @Out IntByReference aslave, @Out Pointer name, + @Out Pointer termp, @Out Pointer winp); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbGadpServer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbGadpServer.java new file mode 100644 index 0000000000..b1a9454c31 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbGadpServer.java @@ -0,0 +1,205 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.gadp; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import agent.gdb.gadp.impl.GdbGadpServerImpl; +import ghidra.dbg.agent.AgentWindow; +import ghidra.util.Msg; + +public interface GdbGadpServer extends AutoCloseable { + public static void main(String[] args) throws Exception { + new Runner().run(args); + } + + public static GdbGadpServer newInstance(SocketAddress addr) throws IOException { + return new GdbGadpServerImpl(addr); + } + + public class Runner { + private String gdbCmd = "gdb"; + private List gdbArgs = new ArrayList<>(); + private InetSocketAddress bindTo; + + public void run(String args[]) + throws IOException, InterruptedException, ExecutionException { + parseArguments(args); + + try (GdbGadpServer server = newInstance(bindTo)) { + server.startGDB(gdbCmd, gdbArgs.toArray(new String[] {})).exceptionally(e -> { + Msg.error(this, "Error starting GDB/GADP", e); + System.exit(-1); + return null; + }); + new AgentWindow("GDB Agent for Ghidra", server.getLocalAddress()); + server.consoleLoop(); + } + System.exit(0); + } + + private void parseArguments(String[] args) { + String iface = "localhost"; + int port = 12345; + // NOTE: Maybe we should import commons-cli (Apache 2.0) or Argparse4j (MIT).... + Iterator ait = Arrays.asList(args).iterator(); + while (ait.hasNext()) { + String a = ait.next(); + if ("--gadp-args".equals(a)) { + break; + } + else if ("-h".equals(a) || "--help".equals(a)) { + printUsage(); + System.exit(0); + } + else { + gdbArgs.add(a); + } + } + while (ait.hasNext()) { + String a = ait.next(); + if ("-p".equals(a) || "--port".equals(a)) { + if (!ait.hasNext()) { + System.err.println("Expected PORT"); + printUsage(); + System.exit(-1); + } + String portStr = ait.next(); + try { + port = Integer.parseInt(portStr); + } + catch (NumberFormatException e) { + System.err.println("Integer required. Got " + portStr); + printUsage(); + System.exit(-1); + } + } + else if ("-H".equals(a) || "--host".equals(a)) { + if (!ait.hasNext()) { + System.err.println("Expected HOST/ADDR"); + printUsage(); + System.exit(-1); + } + iface = ait.next(); + } + else if ("-g".equals(a) || "--gdb-cmd".equals(a)) { + if (!ait.hasNext()) { + System.err.println("Expected CMD"); + printUsage(); + System.exit(-1); + } + gdbCmd = ait.next(); + } + else if ("-x".equals(a) || "--existing".equals(a)) { + gdbCmd = null; + } + else { + System.err.println("Unknown option: " + a); + printUsage(); + System.exit(-1); + } + } + bindTo = new InetSocketAddress(iface, port); + } + + private void printUsage() { + System.out.println("This is the GADP wrapper for GDB. Usage:"); + System.out.println(); + System.out.println( + " gadpgdb [GDB options] [--gadp-args [-H HOST/ADDR] [-p PORT] [-g CMD] [-x]]"); + System.out.println(); + System.out.println("Options:"); + System.out.println(); + System.out.println("Use gdb -h for suitable [GDB options]"); + System.out.println(); + System.out.println( + " --host/-H The address of the interface on which to listen. Default is localhost"); + System.out.println( + " --port/-p The TCP port on which to listen. Default is 12345. 0 for automatic."); + System.out.println( + " --gdb-cmd/-g The command to launch gdb. Default is 'gdb'"); + System.out.println( + " --existing/-x Do not launch gdb. Instead just open a pty"); + System.out.println(); + System.out.println( + "Starts a GDB-based GADP server \"agent\". In general, it can be invoked in"); + System.out.println( + "the same manner as standard gdb. Arguments to control the GADP server and"); + System.out.println( + "GDB invocation are given after the --gadp-args flag. Once the server has"); + System.out.println( + "started, it will print the interface IP and port. The -g and -x flags are"); + System.out.println( + "mutually exclusive. The one appearing last get preference. The -x flags"); + System.out.println( + "causes the agent to refrain from launching its own gdb process. Instead,"); + System.out.println( + "it prints the file name of a private terminate (pty) where it expects a"); + System.out.println( + "GDB/MI v2 interpreter from an existing gdb process. Use the new-ui command"); + System.out.println( + "(available since GDB version 7.12) to join the agent to the existing"); + System.out.println("session:"); + System.out.println(); + System.out.println("(gdb) new-ui mi2 /dev/ptyXX"); + } + } + + /** + * Start the GDB session + * + * @param gdbCmd the command to execute GDB + * @param args arguments to pass to GDB, except for -i + * @return a future that completes when GDB is ready + */ + CompletableFuture startGDB(String gdbCmd, String[] args); + + /** + * Get the local address to which the SCTL server is bound. + * + * @return the local socket address + */ + SocketAddress getLocalAddress(); + + /** + * Starts the GDB manager's console loop + * + * @throws IOException if an I/O error occurs + */ + public void consoleLoop() throws IOException; + + /** + * Close all SCTL connections and ports, and terminate the GDB session + * + * @throws IOException if an I/O error occurs + */ + public void terminate() throws IOException; + + /** + * Calls {@link #terminate()} + * + * @throws IOException if an I/O error occurs + */ + @Override + default void close() throws IOException { + terminate(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbLocalDebuggerModelFactory.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbLocalDebuggerModelFactory.java new file mode 100644 index 0000000000..851027f239 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbLocalDebuggerModelFactory.java @@ -0,0 +1,107 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.gadp; + +import java.io.IOException; +import java.lang.ProcessBuilder.Redirect; +import java.util.List; + +import agent.gdb.manager.GdbManager; +import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory; +import ghidra.dbg.util.ConfigurableFactory.FactoryDescription; +import ghidra.util.classfinder.ExtensionPointProperties; + +@FactoryDescription( // + brief = "GNU gdb local agent via GADP/TCP", // + htmlDetails = "Launch a new agent using GDB. This may start a new session or join an existing one." // +) +@ExtensionPointProperties(priority = 100) +public class GdbLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory { + public static boolean checkGdbPresent(String gdbCmd) { + try { + ProcessBuilder builder = new ProcessBuilder(gdbCmd, "--version"); + builder.redirectError(Redirect.INHERIT); + builder.redirectOutput(Redirect.INHERIT); + @SuppressWarnings("unused") + Process gdb = builder.start(); + // TODO: Once supported versions are decided, check the version. + return true; + } + catch (IOException e) { + return false; + } + } + + protected Boolean isSuitable; + + private String gdbCmd = GdbManager.DEFAULT_GDB_CMD; + @FactoryOption("GDB launch command") + public final Property gdbCommandOption = + Property.fromAccessors(String.class, this::getGdbCommand, this::setGdbCommand); + + private boolean existing = false; + @FactoryOption("Use existing session via new-ui") + public final Property useExistingOption = + Property.fromAccessors(boolean.class, this::isUseExisting, this::setUseExisting); + + // TODO: A factory which connects to GDB via SSH. Would need to refactor manager. + + @Override + public boolean isCompatible() { + // TODO: Could potentially support GDB on Windows, but the pty thing would need porting. + if (isSuitable != null) { + return isSuitable; + } + return isSuitable = checkGdbPresent(gdbCmd); + } + + public String getGdbCommand() { + return gdbCmd; + } + + public void setGdbCommand(String gdbCmd) { + this.gdbCmd = gdbCmd; + } + + public boolean isUseExisting() { + return existing; + } + + public void setUseExisting(boolean existing) { + this.existing = existing; + gdbCommandOption.setEnabled(!existing); + } + + @Override + protected String getThreadName() { + return "Local gdb Agent stdout"; + } + + @Override + protected void completeCommandLine(List cmd) { + cmd.add(GdbGadpServer.class.getCanonicalName()); + // TODO: Option for additional GDB command-line parameters + cmd.add("--gadp-args"); + cmd.addAll(List.of("-H", host)); + cmd.addAll(List.of("-p", Integer.toString(port))); // Available ephemeral port + if (!existing) { + cmd.addAll(List.of("-g", gdbCmd)); + } + else { + cmd.add("-x"); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/impl/GdbGadpServerImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/impl/GdbGadpServerImpl.java new file mode 100644 index 0000000000..b94a66b391 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/impl/GdbGadpServerImpl.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.gadp.impl; + +import java.io.IOException; +import java.net.SocketAddress; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.gadp.GdbGadpServer; +import agent.gdb.model.impl.GdbModelImpl; +import ghidra.dbg.gadp.server.AbstractGadpServer; + +public class GdbGadpServerImpl implements GdbGadpServer { + public class GadpSide extends AbstractGadpServer { + public GadpSide(GdbModelImpl model, SocketAddress addr) throws IOException { + super(model, addr); + } + } + + protected final GdbModelImpl model; + protected final GadpSide server; + + public GdbGadpServerImpl(SocketAddress addr) throws IOException { + super(); + this.model = new GdbModelImpl(); + this.server = new GadpSide(model, addr); + } + + @Override + public CompletableFuture startGDB(String gdbCmd, String[] args) { + return model.startGDB(gdbCmd, args).thenCompose(__ -> server.launchAsyncService()); + } + + @Override + public SocketAddress getLocalAddress() { + return server.getLocalAddress(); + } + + @Override + public void consoleLoop() throws IOException { + model.consoleLoop(); + } + + @Override + public void terminate() throws IOException { + model.terminate(); + server.terminate(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbCause.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbCause.java new file mode 100644 index 0000000000..1ea8e75f36 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbCause.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import agent.gdb.manager.impl.GdbPendingCommand; + +/** + * Identifies the cause of an event emitted by GDB + * + * This is not a concept native to GDB. Rather, it is a means to distinguish events that result from + * commands issued by the {@link GdbManager} from those issued by the user or some other means. For + * example, a call to {@link GdbManager#addInferior()} will emit a + * {@link GdbEventsListener#inferiorAdded(GdbInferior, GdbCause)} event, identifying the + * {@link GdbPendingCommand} as the cause. However, a call to {@link GdbManager#console(String)} + * issuing an "add-inferior" command will emit the same event, but the cause will be + * {@link GdbCause.Causes#UNCLAIMED}. + */ +public interface GdbCause { + public enum Causes implements GdbCause { + UNCLAIMED; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbConsoleOutputListener.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbConsoleOutputListener.java new file mode 100644 index 0000000000..8a8d9692a8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbConsoleOutputListener.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import agent.gdb.manager.GdbManager.Channel; + +public interface GdbConsoleOutputListener { + /** + * GDB outputted some text + * + * @param channel indicates stderr or stdout + * @param out the output + */ + void output(Channel channel, String out); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbEventsListener.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbEventsListener.java new file mode 100644 index 0000000000..b0c9b3bce1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbEventsListener.java @@ -0,0 +1,179 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.util.Collection; + +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import agent.gdb.manager.reason.GdbReason; + +/** + * A listener for events related to objects known to the manager + */ +public interface GdbEventsListener { + + /** + * An inferior has been added to the session + * + * @param inferior a handle to the new inferior + * @param cause the cause of this event + */ + void inferiorAdded(GdbInferior inferior, GdbCause cause); + + /** + * An inferior has been removed from the session + * + * @param inferiorId the ID of the now-defunct inferior + * @param cause the cause of this event + */ + void inferiorRemoved(int inferiorId, GdbCause cause); + + /** + * A different inferior has been selected (gained focus) + * + * @param inferior a handle to the selected inferior + * @param cause the cause of this event + */ + void inferiorSelected(GdbInferior inferior, GdbCause cause); + + /** + * Execution has been started in an inferior + * + * @param inferior a handle to the now-executing inferior + * @param cause the cause of this event + */ + void inferiorStarted(GdbInferior inferior, GdbCause cause); + + /** + * Execution has terminated in an inferior + * + * @param inferior a handle to the now-stopped inferior + * @param cause the cause of this event + */ + void inferiorExited(GdbInferior inferior, GdbCause cause); + + /** + * An inferior's state has changed + * + * Note this event is also parceled out to each affected thread via + * {@link #threadStateChanged(GdbThread, GdbState, GdbCause, GdbReason)} immediately after this + * callback. + * + * @param inf the inferior which is running + * @param threads the threads of the inferior whose states have changed + * @param state the state to which the inferior changed + * @param thread if applicable, the thread which caused the change to {@link GdbState#STOPPED} + * @param cause the cause of this event + * @param reason the reason for the state change + */ + void inferiorStateChanged(GdbInferior inf, Collection threads, GdbState state, + GdbThread thread, GdbCause cause, GdbReason reason); + + /** + * A thread has been created + * + * Use {@link GdbThread#getInferior()} to get a handle to the inferior in which the thread was + * created. + * + * @param thread a handle to the new thread + * @param cause the cause of this event + */ + void threadCreated(GdbThread thread, GdbCause cause); + + /** + * A thread's state has changed, e.g., {@link GdbState#RUNNING} to {@link GdbState#STOPPED} + * + * @param thread a handle to the thread whose state has changed + * @param state the state to which the thread changed + * @param cause the cause of this event + * @param reason the reason for the state change + */ + void threadStateChanged(GdbThread thread, GdbState state, GdbCause cause, GdbReason reason); + + /** + * A thread has exited + * + * @param threadId the ID of the now-defuct thread + * @param inferior a handle to the inferior to which the thread belonged + * @param cause the cause of this event + */ + void threadExited(int threadId, GdbInferior inferior, GdbCause cause); + + /** + * A different thread has been selected (gained focus) + * + * @param thread a handle to the selected thread + * @param frame a handle to the current frame + * @param cause the cause of this event + */ + void threadSelected(GdbThread thread, GdbStackFrame frame, GdbCause cause); + + /** + * A library has been loaded by an inferior + * + * @param inferior a handle to the inferior which loaded the library + * @param name the name of the library on the target + * @param cause the cause of this event + */ + void libraryLoaded(GdbInferior inferior, String name, GdbCause cause); + + /** + * A library has been unloaded from an inferior + * + * @param inferior a handle to the inferior which unloaded the library + * @param name the name of the library on the target + * @param cause the cause of this event + */ + void libraryUnloaded(GdbInferior inferior, String name, GdbCause cause); + + /** + * A breakpoint has been created in the session + * + * @param info information about the new breakpoint + * @param cause the cause of this event + */ + void breakpointCreated(GdbBreakpointInfo info, GdbCause cause); + + /** + * A breakpoint in the session has been modified + * + * @param newInfo new information about the modified breakpoint + * @param oldInfo old information about the modified breakpoint + * @param cause the cause of this event + */ + void breakpointModified(GdbBreakpointInfo newInfo, GdbBreakpointInfo oldInfo, GdbCause cause); + + /** + * A breakpoint has been deleted from the session + * + * @param info information about the now-deleted breakpoint + * @param cause the cause of this event + */ + void breakpointDeleted(GdbBreakpointInfo info, GdbCause cause); + + /** + * TODO: This is not yet implemented + * + * It is not clear whether GDB detects when a target writes into its own memory, or if this + * event is emitted when GDB changes the target's memory, or both. + * + * @param inferior the inferior whose memory changed + * @param addr the address of the change + * @param len the length, with the address, bounding the region of change + * @param cause the cause of this event + */ + void memoryChanged(GdbInferior inferior, long addr, int len, GdbCause cause); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbEventsListenerAdapter.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbEventsListenerAdapter.java new file mode 100644 index 0000000000..79d2c20cf7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbEventsListenerAdapter.java @@ -0,0 +1,95 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.util.Collection; + +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import agent.gdb.manager.reason.GdbReason; + +/** + * An adapter for {@link GdbEventsListener} + * + * This provides an empty default implementation of each method. + */ +public interface GdbEventsListenerAdapter extends GdbEventsListener { + @Override + default void inferiorAdded(GdbInferior inferior, GdbCause cause) { + } + + @Override + default void inferiorRemoved(int inferiorId, GdbCause cause) { + } + + @Override + default void inferiorSelected(GdbInferior inferior, GdbCause cause) { + } + + @Override + default void inferiorStarted(GdbInferior inferior, GdbCause cause) { + } + + @Override + default void inferiorExited(GdbInferior inferior, GdbCause cause) { + } + + @Override + default void inferiorStateChanged(GdbInferior inf, Collection threads, + GdbState state, GdbThread thread, GdbCause cause, GdbReason reason) { + } + + @Override + default void threadCreated(GdbThread thread, GdbCause cause) { + } + + @Override + default void threadStateChanged(GdbThread thread, GdbState state, GdbCause cause, + GdbReason reason) { + } + + @Override + default void threadExited(int threadId, GdbInferior inferior, GdbCause cause) { + } + + @Override + default void threadSelected(GdbThread thread, GdbStackFrame frame, GdbCause cause) { + } + + @Override + default void libraryLoaded(GdbInferior inferior, String name, GdbCause cause) { + } + + @Override + default void libraryUnloaded(GdbInferior inferior, String name, GdbCause cause) { + } + + @Override + default void breakpointCreated(GdbBreakpointInfo info, GdbCause cause) { + } + + @Override + default void breakpointModified(GdbBreakpointInfo newInfo, GdbBreakpointInfo oldInfo, + GdbCause cause) { + } + + @Override + default void breakpointDeleted(GdbBreakpointInfo info, GdbCause cause) { + } + + @Override + default void memoryChanged(GdbInferior inferior, long addr, int len, GdbCause cause) { + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbInferior.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbInferior.java new file mode 100644 index 0000000000..43559017aa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbInferior.java @@ -0,0 +1,306 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.math.BigInteger; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.GdbManager.ExecSuffix; +import agent.gdb.manager.impl.GdbMemoryMapping; + +/** + * A handle to a GDB inferior + * + * Each inferior controlled by GDB is numbered and usually corresponds to a target process. Methods + * that return a {@link CompletableFuture} send a command to GDB via its GDB/MI interpreter. Each + * method issuing a command will first change focus to this inferior. The returned future completes + * when GDB has finished executing the command. + */ +public interface GdbInferior extends GdbMemoryOperations { + + /** + * Get the GDB-assigned inferior number + * + * @return the number + */ + int getId(); + + /** + * If started, get the OS-assigned ID of the process + * + * @return the process ID + */ + Long getPid(); + + /** + * If exited (implying a previous start), get the process exit code + * + * This may be slightly system-dependent, as the exit code may specify either the status of a + * normal exit, or the cause of an abnormal exit. + * + * @return the exit code + */ + Long getExitCode(); + + /** + * Get the executable path + * + * TODO: I presume path on the target system + * + * @return the executable + */ + String getExecutable(); + + /** + * Get a thread belonging to this inferior + * + * GDB (at least recent versions) numbers its threads using a global counter. The thread ID is + * this number, not the OS-assigned TID. + * + * @param tid the GDB-assigned thread ID + * @return a handle to the thread, if it exists + */ + GdbThread getThread(int tid); + + /** + * Enumerate the threads known to the manager to belong to this inferior + * + * This does not send any commands to GDB. Rather it simply returns a read-only handle to the + * manager's internal map for tracking threads and inferiors. + * + * @return a map of GDB-assigned thread IDs to thread handles + */ + Map getKnownThreads(); + + /** + * List GDB's threads in this inferior (thread group) + * + * This is equivalent to the CLI command: {@code info threads}. + * + * @return a future that completes with a map of global thread IDs to thread handles + */ + CompletableFuture> listThreads(); + + /** + * Enumerate the modules known to the manager to belong to this inferior + * + * This does not send any commands to GDB. Rather it simply returns a read-only handle to the + * manager's internal map for tracking modules. + * + * @return a map of GDB-assigned names to module handles + */ + Map getKnownModules(); + + /** + * List GDB's modules in this inferior (process, thread group) + * + * This is equivalent to the CLI command: {@code maintenance info sections ALLOBJ}. This command + * is more thorough than {@code info shared} as it contains the executable module, shared + * libraries, system-supplied objects, and enumerates all sections thereof, not just + * {@code .text}. + * + * @return a future that completes with a map of module names to module handles + */ + CompletableFuture> listModules(); + + /** + * Enumerate the memory mappings known to the manager to belong to this inferior's process + * + * @return a map of start addresses to mapped memory regions + */ + Map getKnownMappings(); + + /** + * List the memory mappings of this inferior's process + * + * @return a future that completes with a map of start addresses to mapped memory regions + */ + CompletableFuture> listMappings(); + + /** + * Change CLI focus to this inferior + * + * This is equivalent to the CLI command: {@code inferior [THIS_ID]}. + * + * GDB's CLI has the concept of focus. That is, commands issued must be applied to some + * "current" inferior. This method changes GDB's current inferior so that subsequent commands + * will apply to this inferior. Commands issued from this handle are always executed with this + * inferior in focus, so it is rare to invoke his method directly. + * + * @return a future that completes when GDB has executed the command + */ + CompletableFuture select(); + + /** + * Specify a binary image for execution and debug symbols + * + * This is equivalent to the CLI command: {@code file [FILE]}. + * + * @param file the path to the binary image + * @return a future that completes when GDB has executed the command + */ + CompletableFuture fileExecAndSymbols(String file); + + /** + * Begin execution + * + * This is equivalent to the CLI command: {@code run}. Note this will not stop at + * {@code main}. The caller should first set breakpoints if an immediate stop is desired. If the + * same behavior as {@code start} is desired, consider using {@link #console(String)} or + * {@link #consoleCapture(String)}. + * + * This command completes as soon as the inferior is running. If a stop is expected at a + * breakpoint, then the caller should listen for that event before issuing additional commands. + * Alternatively, the caller may interrupt the inferior. The manager has only been tested on GDB + * in all-stop mode; GDB cannot process commands while an inferior is running. + * + * @return a future that completes with a handle to the first thread of the running inferior + */ + CompletableFuture run(); + + /** + * Attach to a running process + * + * This is equivalent to the CLI command: {@code attach [PID]}. + * + * @param pid the OS-assigned process ID of the target process + * @return a future that completes with a set of handles to all threads of the attached inferior + */ + CompletableFuture> attach(long pid); + + /** + * Execute an arbitrary CLI command, printing output to the CLI console + * + * Note: to ensure a certain thread has focus for a console command, see + * {@link GdbThread#console(String)}. + * + * @param command the command to execute + * @return a future that completes when GDB has executed the command + */ + CompletableFuture console(String command); + + /** + * Execute an arbitrary CLI command, capturing its console output + * + * The output will not be printed to the CLI console. To ensure a certain thread has focus for a + * console command, see {@link GdbThread#consoleCapture(String)}. + * + * @param command the command to execute + * @return a future that completes with the captured output when GDB has executed the command + */ + CompletableFuture consoleCapture(String command); + + /** + * Continue execution + * + * This is equivalent to the CLI command: {@code continue}. + * + * @return a future that completes once the inferior is running + */ + CompletableFuture cont(); + + /** + * Step execution + * + * This is equivalent to the CLI command: {@code step}. + * + * @param suffix specifies how far to step, or on what conditions stepping ends. + * + * @return a future that completes once the inferior has stepped + */ + CompletableFuture step(ExecSuffix suffix); + + /** + * Evaluate an expression + * + * This evaluates an expression in the same way that the CLI commands {@code print}, + * {@code output}, and {@code call} would. + * + * @param expression the expression to evaluate + * @return a future that completes with the string representation of the value + */ + CompletableFuture evaluate(String expression); + + /** + * Set the controlling TTY for future executions + * + * This is equivalent to the CLI command: {@code set inferior-tty [TTY]}. It does not affect the + * currently running process, if any. This is useful, e.g., to separate target output from GDB's + * output. If, e.g., a program outputs lines which look like GDB/MI records, the manager will + * interpret them possibly leading to strange behavior. Using this command can redirect the + * output to an alternative console to avoid any mis-interpretation. + * + * @param tty the controlling TTY for future executions + * @return a future that completes when GDB has executed the command + */ + CompletableFuture setTty(String tty); + + /** + * Get the value of an internal GDB variable + * + * This is equivalent to the CLI command: {@code show [VAR_NAME]}. + * + * @param varName the name of the GDB variable + * @return a future that completes with the string representation of the value + */ + CompletableFuture getVar(String varName); + + /** + * Set the value of an internal GDB variable + * + * This is equivalent to the CLI command: {@code set [VAR_NAME]=[VAL]}. + * + * @param varName the name of the GDB variable + * @param val the value to assign + * @return a future that completes when GDB has executed the command + */ + CompletableFuture setVar(String varName, String val); + + /** + * Detach from the process + * + * This is equivalent to the CLI command {@code detach}. + * + * @return a future that completes when GDB has executed the command + */ + CompletableFuture detach(); + + /** + * Kill the process + * + * This is equivalent to the CLI command {@code kill}. + * + * @return a future that completes when GDB has executed the command + */ + CompletableFuture kill(); + + /** + * Remove this inferior from the session + * + * @return a future that completes when GDB has executed the command + */ + CompletableFuture remove(); + + /** + * Get the "Descriptor" column, usually a process id + * + * @return the descriptor + */ + String getDescriptor(); + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbInferiorThreadGroup.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbInferiorThreadGroup.java new file mode 100644 index 0000000000..189768405e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbInferiorThreadGroup.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +public class GdbInferiorThreadGroup { + private final int iid; + private final String type; + private final Long pid; + private final Long exitCode; + private final String executable; + // TODO: cores + + public GdbInferiorThreadGroup(int iid, String type, Long pid, Long exitCode, + String executable) { + this.iid = iid; + this.type = type; + this.pid = pid; + this.exitCode = exitCode; + this.executable = executable; + } + + public int getInferiorId() { + return iid; + } + + public String getType() { + return type; + } + + public Long getPid() { + return pid; + } + + public Long getExitCode() { + return exitCode; + } + + public String getExecutable() { + return executable; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbLibraryId.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbLibraryId.java new file mode 100644 index 0000000000..d45b59dfa8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbLibraryId.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +/** + * An opaque handle identifying a library + * + * GDB leaves a lot of variability in how libraries are uniquely identified within an inferior. This + * interface provides a means for an implementor to define it arbitrarily. It seems GDB actually + * uses the path of the library, but one should not depend on it. + */ +public interface GdbLibraryId { + // An opaque handle +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbManager.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbManager.java new file mode 100644 index 0000000000..6c8526f8d6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbManager.java @@ -0,0 +1,473 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import agent.gdb.ffi.linux.Pty; +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import agent.gdb.manager.breakpoint.GdbBreakpointInsertions; +import agent.gdb.manager.impl.GdbManagerImpl; + +/** + * The controlling side of a GDB session, using GDB/MI, usually via a pseudo-terminal + * + * This facilitates the implementation of GDB front ends. This piece communicates with the GDB back + * end, providing a more object-oriented programmatic interface. The methods returning + * {@link CompletableFuture} all send commands to GDB, and the future completes when the command has + * been executed. + */ +public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions { + public static final String DEFAULT_GDB_CMD = "/usr/bin/gdb"; + + /** + * Possible values for {@link GdbThread#step(ExecSuffix)} + */ + public enum ExecSuffix { + /** Equivalent to {@code finish} in the CLI */ + FINISH("finish"), + /** Equivalent to {@code next} in the CLI */ + NEXT("next"), + /** Equivalent to {@code nexti} in the CLI */ + NEXT_INSTRUCTION("next-instruction"), + /** Equivalent to {@code return} in the CLI */ + RETURN("return"), + /** Equivalent to {@code step} in the CLI */ + STEP("step"), + /** Equivalent to {@code stepi} in the CLI */ + STEP_INSTRUCTION("step-instruction"), + /** Equivalent to {@code until} in the CLI */ + UNTIL("until"), + ; + + final String str; + + ExecSuffix(String str) { + this.str = str; + } + + @Override + public String toString() { + return str; + } + } + + /** + * Just a vanilla demo of the manager + * + * This presents the usual GDB CLI, using GDB/MI on the back end. The manager is keeps track of + * events; however, in this vanilla front end, nothing consumes them. This also provides a quick + * test to ensure the console loop operates correctly, or at least closely enough to actual GDB. + * + * @param args additional arguments to pass to GDB. Passing -i will cause problems. + * @throws InterruptedException + * @throws ExecutionException + * @throws IOException + */ + public static void main(String[] args) + throws InterruptedException, ExecutionException, IOException { + try (GdbManager mgr = newInstance()) { + mgr.start(DEFAULT_GDB_CMD, args); + mgr.runRC().get(); + mgr.consoleLoop(); + } + } + + public enum Channel { + STDOUT, STDERR; + } + + /** + * Get a new manager instance, without starting GDB + * + * @return the manager + */ + public static GdbManager newInstance() { + // TODO: Add parameter and test both? + // TODO: Eventually the 'true' variant will be deprecated + return new GdbManagerImpl(); + } + + /** + * Launch GDB + * + * @return a future which completes when GDB is ready to accept commands + * @throws IOException if GDB cannot be started + */ + default void start() throws IOException { + start(DEFAULT_GDB_CMD); + } + + /** + * Launch GDB, providing a custom path to GDB + * + * @param gdbCmd the path to the GDB executable + * @param args additional arguments to pass. Passing -i will cause problems. + * @return a future which completes when GDB is ready to accept commands + * @throws IOException if GDB cannot be started + */ + void start(String gdbCmd, String... args) throws IOException; + + /** + * Wait for a prompt and run any rc (initial configuration) commands + * + * @return a future which completes when rc has finished + */ + CompletableFuture runRC(); + + /** + * Terminates GDB + */ + @Override + default void close() { + terminate(); + } + + /** + * Execute a console loop in this thread + * + * Note this does not follow the asynchronous pattern. + * + * @throws IOException if an I/O error occurs + */ + void consoleLoop() throws IOException; + + /** + * Terminate GDB + */ + void terminate(); + + /** + * Check if GDB is alive + * + * Note this is not about the state of inferiors in GDB. If the GDB controlling process is + * alive, GDB is alive. + * + * @return true if GDB is alive, false otherwise + */ + boolean isAlive(); + + /** + * Add a listener for GDB's state + * + * @see #getState() + * @param listener the listener to add + */ + void addStateListener(GdbStateListener listener); + + /** + * Remove a listener for GDB's state + * + * @see #getState() + * @param listener the listener to remove + */ + void removeStateListener(GdbStateListener listener); + + /** + * Add a listener for events on inferiors + * + * @param listener the listener to add + */ + void addEventsListener(GdbEventsListener listener); + + /** + * Remove a listener for events on inferiors + * + * @param listener the listener to remove + */ + void removeEventsListener(GdbEventsListener listener); + + /** + * Add a listener for target output + * + * Note: depending on the target, its output may not be communicated via this listener. Local + * targets, e.g., tend to just print output to GDB's controlling TTY. See + * {@link GdbInferior#setTty(String)} for a means to more reliably interact with a target's + * input and output. See also {@link Pty} for a means to easily acquire a new TTY from Java. + * + * @param listener the listener to add + */ + void addTargetOutputListener(GdbTargetOutputListener listener); + + /** + * Remove a listener for target output + * + * @see #addTargetOutputListener(GdbTargetOutputListener) + * @param listener + */ + void removeTargetOutputListener(GdbTargetOutputListener listener); + + /** + * Add a listener for console output + * + * @param listener the listener to add + */ + void addConsoleOutputListener(GdbConsoleOutputListener listener); + + /** + * Remove a listener for console output + * + * @param listener + */ + void removeConsoleOutputListener(GdbConsoleOutputListener listener); + + /** + * Get a thread by its GDB-assigned ID + * + * GDB numbers its threads using a global counter. These IDs are unrelated to the OS-assigned + * TID. This method can retrieve a thread by its ID no matter which inferior it belongs to. + * + * @param tid the GDB-asigned thread ID + * @return a handle to the thread, if it exists + */ + GdbThread getThread(int tid); + + /** + * Get an inferior by its GDB-assigned ID + * + * GDB numbers inferiors incrementally. All inferiors and created and destroyed by the user. See + * {@link #addInferior()}. + * + * @param iid the inferior ID + * @return a handle to the inferior, if it exists + */ + GdbInferior getInferior(int iid); + + /** + * Get the inferior which currently has focus + * + * @see GdbInferior#select() + * @return a handle to the inferior with focus + */ + GdbInferior currentInferior(); + + /** + * Get all inferiors known to the manager + * + * This does not ask GDB to list its inferiors. Rather it returns a read-only view of the + * manager's understanding of the current inferiors based on its tracking of GDB events. + * + * @return a map of inferior IDs to corresponding inferior handles + */ + Map getKnownInferiors(); + + /** + * Get all threads known to the manager + * + * This does not ask GDB to lists its known threads. Rather it returns a read-only view of the + * manager's understanding of the current threads based on its tracking of GDB events. + * + * @return a map of GDB-assigned thread IDs to corresponding thread handles + */ + Map getKnownThreads(); + + /** + * Get all breakpoints known to the manager + * + * This does not ask GDB to list its breakpoints. Rather it returns a read-only view of the + * manager's understanding of the current breakpoints based on its tracking of GDB events. + * + * @return a map of GDB-assigned breakpoint IDs to corresponding breakpoint information + */ + Map getKnownBreakpoints(); + + /** + * Send an interrupt to GDB regardless of other queued commands + * + * This may be useful if the manager's command queue is stalled because an inferior is running. + * + * @throws IOException if an I/O error occurs + * @throws InterruptedException + */ + void sendInterruptNow() throws IOException; + + /** + * Get the state of the GDB session + * + * In all-stop mode, if any thread is running, GDB is said to be in the running state and is + * unable to process commands. Otherwise, if all threads are stopped, then GDB is said to be in + * the stopped state and can accept and process commands. This manager has not been tested in + * non-stop mode. + * + * @return the state + */ + GdbState getState(); + + /** + * Wait for GDB to enter the given state + * + * @param forState the state to wait for + * @return a future which completes when GDB enters the given state + */ + CompletableFuture waitForState(GdbState forState); + + /** + * Wait for GDB to present a prompt + * + * This waits for a prompt from GDB unless the last line printed is already a prompt. This is + * generally not necessary following normal commands, but may be necessary after interrupting a + * running inferior, or after waiting for an inferior to reach a stopped state. + * + * @return a future which completes when GDB presents a prompt + */ + CompletableFuture waitForPrompt(); + + /** + * A dummy command which claims as cause a stopped event and waits for the next prompt + * + * This is used to squelch normal processing of a stopped event until the next prompt + * + * @return a future which completes when the "command" has finished execution + */ + CompletableFuture claimStopped(); + + /** + * Add an inferior + * + * This is equivalent to the CLI command: {@code add-inferior}. + * + * @return a future which completes with the handle to the new inferior + */ + CompletableFuture addInferior(); + + /** + * Remove an inferior + * + * This is equivalent to the CLI command: {@code remove-inferior}. + * + * Note that unlike the CLI, it is possible to remove the current inferior, in which case, the + * lowest-id inferior is selected. Like the CLI, it is not possible to remove an active inferior + * or the last inferior. + * + * @param inferior the inferior to remove + * @return a future which completes then GDB has executed the command + */ + CompletableFuture removeInferior(GdbInferior inferior); + + /** + * Execute an arbitrary CLI command, printing output to the CLI console + * + * Note: to ensure a certain thread or inferior has focus for a console command, see + * {@link GdbThread#console(String)} and {@link GdbInferior#console(String)}. + * + * @param command the command to execute + * @return a future that completes when GDB has executed the command + */ + CompletableFuture console(String command); + + /** + * Execute an arbitrary CLI command, capturing its console output + * + * The output will not be printed to the CLI console. To ensure a certain thread or inferior has + * focus for a console command, see {@link GdbThread#consoleCapture(String)} and + * {@link GdbInferior#consoleCapture(String)}. + * + * @param command the command to execute + * @return a future that completes with the captured output when GDB has executed the command + */ + CompletableFuture consoleCapture(String command); + + /** + * Interrupt the GDB session + * + * This is equivalent to typing Ctrl-C in the CLI. This typically results in the target being + * interrupted, either because GDB and the target have the same controlling TTY, or because GDB + * will "forward" the interrupt to the target. + * + * For whatever reason, interrupting the session does not always reliably interrupt the target. + * The manager will send Ctrl-C to the pseudo-terminal up to three times, waiting about 10ms + * between each, until GDB issues a stopped event and presents a new prompt. + * + * @return a future that completes when GDB has entered the stopped state + */ + CompletableFuture interrupt(); + + /** + * List GDB's inferiors + * + * This is equivalent to the CLI command: {@code inferiors}. + * + * @return a future that completes with a map of inferior IDs to inferior handles + */ + CompletableFuture> listInferiors(); + + /** + * List information for all breakpoints + * + * This is equivalent to the CLI command {@code info break}. + * + * @return a future that completes with a list of information for all breakpoints + */ + CompletableFuture> listBreakpoints(); + + /** + * Disable the given breakpoints + * + * This is equivalent to the CLI command {@code disable breakpoint [NUMBER]}. + * + * @param numbers the GDB-assigned breakpoint numbers + * @return a future that completes when GDB has executed the command + */ + CompletableFuture disableBreakpoints(long... numbers); + + /** + * Enable the given breakpoints + * + * This is equivalent to the CLI command {@code enable breakpoint [NUMBER]}. + * + * @param numbers the GDB-assigned breakpoint numbers + * @return a future that completes when GDB has executed the command + */ + CompletableFuture enableBreakpoints(long... numbers); + + /** + * Delete a breakpoint + * + * This is equivalent to the CLI command {@code delete breakpoint [NUMBER]}. + * + * @param numbers the GDB-assigned breakpoint numbers + * @return a future that completes when GDB has executed the command + */ + CompletableFuture deleteBreakpoints(long... numbers); + + /** + * List the available processes on target + * + * @return a future that completes with a list of PIDs + */ + CompletableFuture> listAvailableProcesses(); + + /** + * Gather information about the host OS + * + * This is equivalent to the CLI command: {@code info os [TYPE]}. + * + * @param type the type of OS information to gather + * @return a future which completes with a table of information + */ + CompletableFuture infoOs(String type); + + /** + * Get the name of the mi2 pty for this GDB session + * + * @return the filename + */ + String getMi2PtyName(); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbMemoryOperations.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbMemoryOperations.java new file mode 100644 index 0000000000..e173938ef5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbMemoryOperations.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.nio.ByteBuffer; +import java.util.concurrent.CompletableFuture; + +import com.google.common.collect.RangeSet; + +public interface GdbMemoryOperations { + + /** + * Read memory + * + * @param addr the address to begin reading at + * @param buf the buffer to read into + * @param len the length of data to read + * @return a future which completes giving the ranges successfully read + */ + CompletableFuture> readMemory(long addr, ByteBuffer buf, int len); + + /** + * Read memory + * + * The length is determined by the available space in the destination buffer + * + * @param addr the address to begin reading at + * @param buf the buffer to read into + * @return a future which completes giving the ranges successfully read + */ + default CompletableFuture> readMemory(long addr, ByteBuffer buf) { + return readMemory(addr, buf, buf.remaining()); + } + + /** + * Write memory + * + * @param addr the address to begin writing at + * @param buf the buffer to copy from + * @param len the length of data to write + * @return a future that completes when the write succeeds in its entirety + */ + CompletableFuture writeMemory(long addr, ByteBuffer buf, int len); + + /** + * Write memory + * + * The length is determined by the available data in the source buffer + * + * @param addr the address to begin writing at + * @param buf the buffer to copy from + * @return a future that completes when the write succeeds in its entirety + */ + default CompletableFuture writeMemory(long addr, ByteBuffer buf) { + return writeMemory(addr, buf, buf.remaining()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbModule.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbModule.java new file mode 100644 index 0000000000..c574b01fb0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbModule.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.impl.GdbMinimalSymbol; + +public interface GdbModule { + String getName(); + + CompletableFuture computeBase(); + + CompletableFuture computeMax(); + + Long getKnownBase(); + + Long getKnownMax(); + + CompletableFuture> listSections(); + + Map getKnownSections(); + + CompletableFuture> listMinimalSymbols(); + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbModuleSection.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbModuleSection.java new file mode 100644 index 0000000000..190d0c8339 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbModuleSection.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.util.List; + +public interface GdbModuleSection { + String getName(); + + long getVmaStart(); + + long getVmaEnd(); + + long getFileOffset(); + + List getAttributes(); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbProcessThreadGroup.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbProcessThreadGroup.java new file mode 100644 index 0000000000..f85db8fde5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbProcessThreadGroup.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +/** + * A description of an available process + * + * NOTE: This list of available processes can change at any time. The pid may not be valid or + * describe the same process. + */ +public class GdbProcessThreadGroup { + private final int pid; + private final String description; + + public GdbProcessThreadGroup(int pid, String description) { + this.pid = pid; + this.description = description; + } + + /** + * Get the process ID + * + * @return the PID + */ + public int getPid() { + return pid; + } + + /** + * Get a description of the process, usually the command line + * + * @return the description + */ + public String getDescription() { + return description; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbRegister.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbRegister.java new file mode 100644 index 0000000000..695d716fd8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbRegister.java @@ -0,0 +1,77 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +/** + * A handle/descriptor to a GDB register + * + * This contains a register's name and number + */ +public class GdbRegister implements Comparable { + private final String name; + private final int number; + private final int size; + + /** + * Construct a new register descriptor + * + * @param name the register's name + * @param number the GDB-assigned register number + * @param the size in bytes + */ + public GdbRegister(String name, int number, int size) { + this.name = name; + this.number = number; + this.size = size; + } + + /** + * Get the register's name + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Get the register's GDB-assigned number + * + * @return the number + */ + public int getNumber() { + return number; + } + + /** + * Get the register's size in bytes + * + * @return the size + */ + public int getSize() { + return size; + } + + @Override + public int compareTo(GdbRegister that) { + return this.number - that.number; + } + + @Override + public String toString() { + return "<" + getClass().getSimpleName() + " " + name + " (" + number + ")>"; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbRegisterSet.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbRegisterSet.java new file mode 100644 index 0000000000..afed95f62a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbRegisterSet.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.util.*; + +/** + * A collection of registers indexed by name and number + * + * Iteration is in order of register number + */ +public class GdbRegisterSet extends AbstractSet { + private final Map byName = new HashMap<>(); + private final Map byNumber = new TreeMap<>(); + + /** + * Construct a set from the given collection + * + * Note that regs need not be presented in any particular order; however, there must be at most + * one register per number. Otherwise, there may be undefined behavior. + * + * @param regs the registers to index + */ + public GdbRegisterSet(Collection regs) { + for (GdbRegister r : regs) { + byName.put(r.getName(), r); + byNumber.put(r.getNumber(), r); + } + } + + /** + * Get a register by name + * + * @param name the name + * @return the register + */ + public GdbRegister get(String name) { + return byName.get(name); + } + + /** + * Get a register by number + * + * @param number the number + * @return the register + */ + public GdbRegister get(int number) { + return byNumber.get(number); + } + + @Override + public Iterator iterator() { + return byNumber.values().iterator(); + } + + @Override + public int size() { + return byNumber.size(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStackFrame.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStackFrame.java new file mode 100644 index 0000000000..12936c8cd1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStackFrame.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.math.BigInteger; +import java.util.concurrent.CompletableFuture; + +public interface GdbStackFrame extends GdbStackFrameOperations { + // TODO: from, args? + + /** + * Get the address of the program counter + * + * @return the program counter + */ + BigInteger getAddress(); + + /** + * Get the name of the function where the program counter is + * + * @return the function name + */ + String getFunction(); + + /** + * Get the level of the frame + * + * Frame 0 is the actual machine state, and each increment unwinds the stack 1 frame. + * + * @return the frame level + */ + int getLevel(); + + /** + * Make this frame the current frame + * + * @return a future that completes when the frame is the current frame + */ + CompletableFuture select(); + + /** + * Get the thread for this frame + * + * @return the thread + */ + GdbThread getThread(); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStackFrameOperations.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStackFrameOperations.java new file mode 100644 index 0000000000..54f2b37c41 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStackFrameOperations.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.math.BigInteger; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +public interface GdbStackFrameOperations { + /** + * Evaluate an expression + * + * This evaluates an expression in the same way that the CLI commands {@code print}, + * {@code output}, and {@code call} would. + * + * @param expression the expression to evaluate + * @return a future that completes with the string representation of the value + */ + CompletableFuture evaluate(String expression); + + /** + * Read the values of a given set of registers + * + * @param regs the set of registers + * @return a future that completes with a map of register descriptors to value + */ + CompletableFuture> readRegisters(Set regs); + + /** + * Write the values of a given set of registers + * + * @param regVals a map of register descriptors to value + * @return a future that completes when the registers have been written + */ + CompletableFuture writeRegisters(Map regVals); + + /** + * Execute an arbitrary CLI command, printing output to the CLI console + * + * @param command the command to execute + * @return a future that completes when GDB has executed the command + */ + CompletableFuture console(String command); + + /** + * Execute an arbitrary CLI command, capturing its console output + * + * The output will not be printed to the CLI console. + * + * @param command the command to execute + * @return a future that completes with the captured output when GDB has executed the command + */ + CompletableFuture consoleCapture(String command); + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbState.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbState.java new file mode 100644 index 0000000000..afeb6571b9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbState.java @@ -0,0 +1,79 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +/** + * Describes the running state of GDB + * + * The manager is initialized in the {@link #NOT_STARTED} state. When {@link GdbManager#start()} is + * called, it enters the {@link #STARTING} state immediately upon successfully launching the GDB + * session. Once GDB issues its first prompt, it enters the {@link #STOPPED} state. The state then + * switches between {@link #RUNNING} and {@link #STOPPED} according to the execution state of its + * inferior(s). When the GDB session exits, it enters the {@link #EXIT} state. + * + * This is also used to describe the state of threads and inferiors. Only {@link #STOPPED}, + * {@link #RUNNING}, and {@link #EXIT} apply to inferiors. Only {@link #STOPPED} and + * {@link #RUNNING} apply to threads. + */ +public enum GdbState { + /** + * GDB is not alive, because it has not be started + */ + NOT_STARTED { + @Override + public boolean isAlive() { + return false; + } + }, + /** + * GDB is alive, but has not issued its first prompt, yet + */ + STARTING { + @Override + public boolean isAlive() { + return true; + } + }, + /** + * GDB, the inferior, or the thread, is stopped + */ + STOPPED { + @Override + public boolean isAlive() { + return true; + } + }, + /** + * GDB, the inferior, or the thread, is running + */ + RUNNING { + @Override + public boolean isAlive() { + return true; + } + }, + /** + * GDB or the inferior has exited + */ + EXIT { + @Override + public boolean isAlive() { + return false; + } + }; + + public abstract boolean isAlive(); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStateListener.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStateListener.java new file mode 100644 index 0000000000..657543aa7e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbStateListener.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import ghidra.util.TriConsumer; + +/** + * A listener for changes in GDB's state + */ +public interface GdbStateListener extends TriConsumer { + /** + * The state has changed because of the given cause + * + * @param state the new state + * @param cause the reason for the change + */ + void stateChanged(GdbState state, GdbCause cause); + + @Override + default void accept(GdbState oldSt, GdbState newSt, GdbCause u) { + stateChanged(newSt, u); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbTable.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbTable.java new file mode 100644 index 0000000000..e191339739 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbTable.java @@ -0,0 +1,320 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.util.*; +import java.util.Map.Entry; + +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import ghidra.util.Msg; + +/** + * A parsed table output from a GDB/MI command + * + * GDB provides many equivalent GDB/MI commands for CLI commands that output formatted tables of + * information. The GDB/MI format can be a little obtuse to traverse, but at least it is relatively + * well structured. This object provides two views of the table: as rows and columns. Generally, a + * table has column headings and entries. Viewed as rows, it is a collection of entries where each + * entry is a map of column head to cell value. Viewed as columns, it is a map of column head to + * column where each column is a collection of cells. + */ +public class GdbTable { + /* + * Piece for parsing the table data + */ + private static class Column { + final int num; + final String name; + final String head; + + Column(int num, String name, String head) { + this.num = num; + this.name = name; + this.head = head; + } + + Column(int num, GdbMiFieldList map) { + this(num, map.getString("col_name"), map.getString("colhdr")); + } + } + + /* + * Implementation of the row-oriented view + * + * view is a list of maps: TableRowView + * + * each item is a map: TableRowCellMap + * + * each map has an entry set, a set of head-cell pairs: TableRowCellSet + * + * each entry set must be iterable, an iterator of head-cell pairs: TableRowCellIterator + * + * each iterator traverses the head-cell pairs: TableRowCellEntry + * + * The other methods are implemented by the abstract base classes provided by Java's + * collections. + */ + + private class TableRowCellEntry implements Entry { + @Override + public String getKey() { + return columns[colIndex].head; + } + + @Override + public String getValue() { + return cells[rowIndex * colCount + colIndex]; + } + + @Override + public String setValue(String value) { + throw new UnsupportedOperationException(); + } + } + + private class TableRowCellIterator implements Iterator> { + @Override + public boolean hasNext() { + return colIndex < colCount - 1; + } + + @Override + public Entry next() { + colIndex++; + return rowCellEntry; + } + } + + private class TableRowCellSet extends AbstractSet> { + @Override + public Iterator> iterator() { + colIndex = -1; + return rowCellIterator; + } + + @Override + public int size() { + return colCount; + } + } + + private class TableRowCellMap extends AbstractMap { + @Override + public int size() { + return colCount; + } + + @Override + public boolean containsKey(Object key) { + return colsByHead.containsKey(key); + } + + @Override + public String get(Object key) { + Column col = colsByHead.get(key); + if (col == null) { + return null; + } + return cells[rowIndex * colCount + col.num]; + } + + @Override + public Set> entrySet() { + return rowCellSet; + } + } + + public class TableRowView extends AbstractList> { + @Override + public Map get(int index) { + if (0 <= index && index < rowCount) { + rowIndex = index; + return rowCellMap; + } + throw new IndexOutOfBoundsException(Integer.toString(index)); + } + + @Override + public int size() { + return rowCount; + } + } + + /* + * Implementation of the column-oriented view + * + * view is a map of lists: TableColumnView + * + * the map has an entry set, a set of head-list pairs: TableColumnSet + * + * the set must be iteratable, an iterator of head-list pairs: TableColumnIterator + * + * the iterator traverses head-list pairs: TableColumnEntry + * + * each list is a list of cells: TableColumnCellList + * + * The other methods are implemented by the abstract base classes provided by Java's + * collections. + */ + + private class TableColumnCellList extends AbstractList { + @Override + public String get(int index) { + return cells[index * colCount + colIndex]; + } + + @Override + public int size() { + return rowCount; + } + } + + private class TableColumnEntry implements Entry> { + @Override + public String getKey() { + return columns[colIndex].head; + } + + @Override + public List getValue() { + return columnCellList; + } + + @Override + public List setValue(List value) { + throw new UnsupportedOperationException(); + } + } + + private class TableColumnIterator implements Iterator>> { + @Override + public boolean hasNext() { + return colIndex < colCount - 1; + } + + @Override + public Entry> next() { + colIndex++; + return columnEntry; + } + } + + private class TableColumnSet extends AbstractSet>> { + @Override + public Iterator>> iterator() { + colIndex = -1; + return columnIterator; + } + + @Override + public int size() { + return colCount; + } + } + + public class TableColumnView extends AbstractMap> { + @Override + public Set>> entrySet() { + return columnSet; + } + } + + /* + * Table class definition + */ + + private final int rowCount; + private final int colCount; + private final String[] cells; + private final Column[] columns; + private final Map colsByHead; + //private final Map colsByName = new HashMap<>(); + + private int rowIndex = -1; + private int colIndex = -1; + + private final TableRowView rowView = new TableRowView(); + private final TableRowCellMap rowCellMap = new TableRowCellMap(); + private final TableRowCellSet rowCellSet = new TableRowCellSet(); + private final TableRowCellIterator rowCellIterator = new TableRowCellIterator(); + private final TableRowCellEntry rowCellEntry = new TableRowCellEntry(); + + private final TableColumnView columnView = new TableColumnView(); + private final TableColumnSet columnSet = new TableColumnSet(); + private final TableColumnIterator columnIterator = new TableColumnIterator(); + private final TableColumnEntry columnEntry = new TableColumnEntry(); + private final TableColumnCellList columnCellList = new TableColumnCellList(); + + /** + * Convert a parsed GDB/MI structure into a table + * + * See GDB's GDB/MI documentation for any command yielding a table for more information on the + * expected structure of GDB/MI table data. + * + * @param dataMap the parsed structure + * @param rowKey the key assigned to each row + */ + public GdbTable(GdbMiFieldList dataMap, String rowKey) { + rowCount = Integer.parseInt(dataMap.getString("nr_rows")); + colCount = Integer.parseInt(dataMap.getString("nr_cols")); + cells = new String[rowCount * colCount]; + columns = new Column[colCount]; + + List hdr = dataMap.getListOf(GdbMiFieldList.class, "hdr"); + if (hdr.size() != colCount) { + Msg.warn(this, "hdr contains fewer than nr_cols"); + } + Map byHead = new LinkedHashMap<>(); + for (int colno = 0; colno < colCount; colno++) { + Column column = new Column(colno, hdr.get(colno)); + columns[colno] = column; + //colsByName.put(column.name, column); + byHead.put(column.head, column); + } + this.colsByHead = Collections.unmodifiableMap(byHead); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + Collection body = (Collection) dataMap.getFieldList("body").get(rowKey); + if (body.size() != rowCount) { + Msg.warn(this, "body contains fewer than nr_rows"); + } + int rowno = 0; + for (GdbMiFieldList row : body) { + for (Column column : columns) { + cells[colCount * rowno + column.num] = row.getString(column.name); + } + rowno++; + } + } + + /** + * Get the table as rows: a list of head-to-cell maps + * + * @return the row view + */ + public TableRowView rows() { + return rowView; + } + + /** + * Get the table as columns: a head-to-cell-list map + * + * @return the column view + */ + public TableColumnView columns() { + return columnView; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbTargetOutputListener.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbTargetOutputListener.java new file mode 100644 index 0000000000..d81af32c5f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbTargetOutputListener.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +/** + * A listener for target console output + * + * Note the details of this listener are not well established, for lack of examples that use + * GDB/MI's target output record. + */ +public interface GdbTargetOutputListener { + /** + * The target outputted some text + * + * @param out the output + */ + void output(String out); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbThread.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbThread.java new file mode 100644 index 0000000000..c624d1db3c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbThread.java @@ -0,0 +1,141 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.GdbManager.ExecSuffix; +import agent.gdb.manager.breakpoint.GdbBreakpointInsertions; +import agent.gdb.manager.impl.GdbThreadInfo; + +/** + * A handle to a thread controlled by GDB + * + * Each thread is numbered by GDB. Methods that return a {@link CompletableFuture} send a command to + * GDB via its GDB/MI interpreter. Where applicable, the {@code --thread} parameter is provided to + * GDB to ensure commands are executed on this thread. The returned future completes when GDB has + * finished executing the command. + */ +public interface GdbThread + extends GdbBreakpointInsertions, GdbMemoryOperations, GdbStackFrameOperations { + + /** + * Get the inferior to which this thread belongs + * + * @return the inferior + */ + GdbInferior getInferior(); + + /** + * Get the GDB-assigned thread number + * + * This is not the OS-assigned TID. + * + * @return the number + */ + int getId(); + + /** + * Get the GDB thread information + * + * @return info + */ + CompletableFuture getInfo(); + + /** + * Get the state of the thread, {@link GdbState#RUNNING} or {@link GdbState#STOPPED}. + * + * @return the state + */ + GdbState getState(); + + /** + * Make this thread the current thread + * + * @return a future that completes when the thread is the current thread + */ + CompletableFuture select(); + + /** + * Set the value of an internal GDB variable + * + * This is equivalent to the CLI command: {@code set [VAR_NAME]=[VAL]}. + * + * @param varName the name of the GDB variable + * @param val the value to assign + * @return a future that completes when GDB has executed the command + */ + CompletableFuture setVar(String varName, String val); + + /** + * List the registers available to this thread + * + * @return a future that completes with a map of register names to descriptors + */ + CompletableFuture listRegisters(); + + /** + * List the frames of this thread's stack + * + * @return + */ + CompletableFuture> listStackFrames(); + + /** + * Continue execution + * + * This is equivalent to the CLI command: {@code continue}. Depending on GDB's execution mode, + * this may allow other threads to execute, too. + * + * @return a future that completes once the thread is running + */ + CompletableFuture cont(); + + /** + * Step the thread + * + * Note that the command can complete before the thread has finished stepping. The command + * completes as soon as the thread is running. A separate stop event is emitted when the step is + * completed. + * + * @param suffix specifies how far to step, or on what conditions stepping ends. + * + * @return a future that completes once the thread is running + */ + CompletableFuture step(ExecSuffix suffix); + + /** + * Detach from the entire process + * + * This is equivalent to the CLI command {@code detach}. It will detach the entire process, not + * just this thread. + * + * @return a future that completes when GDB has executed the command + */ + CompletableFuture kill(); + + /** + * Kill the entire process + * + * This is equivalent to the CLI command {@code kill}. It will kill the entire process, not just + * this thread. + * + * @return a future that completes when GDB has executed the command + */ + CompletableFuture detach(); + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointDisp.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointDisp.java new file mode 100644 index 0000000000..ad81096c51 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointDisp.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.breakpoint; + +/** + * GDB breakpoint disposition + * + * GDB allows the user to specify what happens to a breakpoint after it has been hit. Different + * targets may also have different default/preferred behavior. The breakpoint may remain, or it may + * be deleted after the target stops at that breakpoint. + */ +public enum GdbBreakpointDisp { + /** + * The breakpoint should remain until deleted by the user + */ + KEEP("keep"), + /** + * The breakpoint should be deleted once it is hit + */ + DEL("del"), + /** + * Some disposition unknown to the manager + */ + OTHER(""); + + /** + * Parse a disposition from a GDB/MI breakpoint information block + * + * @param string the value of disposition parsed + * @return the enum constant, or {@link #OTHER} if unrecognized + */ + public static GdbBreakpointDisp fromStr(String string) { + try { + return valueOf(string.toUpperCase()); + } + catch (IllegalArgumentException e) { + return OTHER; + } + } + + private final String name; + + private GdbBreakpointDisp(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public String getName() { + return name; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInfo.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInfo.java new file mode 100644 index 0000000000..01468e3ba5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInfo.java @@ -0,0 +1,363 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.breakpoint; + +import java.util.*; + +import agent.gdb.manager.parsing.GdbMiParser; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import agent.gdb.manager.parsing.GdbParsingUtils; + +/** + * Information about a GDB breakpoint + * + * The contains the semantic processing for GDB breakpoint information. Mostly, it just stores the + * information, but it also enumerates the locations of a breakpoint and generates the "effective" + * breakpoints. + * + * Note this is not a handle to the breakpoint. Rather, this is the captured information from some + * event or request. If other commands have been executed since this information was gathered, the + * information may be stale. + */ +public class GdbBreakpointInfo { + private static List parseIids(GdbMiFieldList bkpt) { + List iids = new ArrayList<>(); + List gids = bkpt.getListOf(String.class, "thread-groups"); + if (gids == null) { + return null; + } + for (String gid : gids) { + iids.add(GdbParsingUtils.parseInferiorId(gid)); + } + return iids; + } + + /** + * Process a parsed GDB breakpoint information block + * + * The passed info should be the field list containing "{@code bkpt={...}}." + * + * It may also contain {@code wpt}, {@code hw-awpt}, or {@code hw-rwpt}, in which case the + * "parsed info" is synthesized to match what would be given by {@code -break-list} for that + * watchpoint. + * + * @param info the parsed information block + * @param curIid in case of a watchpoint, the current inferior id + * @return the processed GDB breakpoint information + */ + public static GdbBreakpointInfo parse(GdbMiFieldList info, Integer curIid) { + GdbMiFieldList bkpt = info.getFieldList("bkpt"); + if (bkpt != null) { + return parseBkpt(bkpt, parseLocations(info), curIid); + } + GdbMiFieldList wpt = info.getFieldList("wpt"); + if (wpt != null) { + return parseWpt(wpt, GdbBreakpointType.HW_WATCHPOINT, curIid); + } + GdbMiFieldList hwAWpt = info.getFieldList("hw-awpt"); + if (hwAWpt != null) { + return parseWpt(hwAWpt, GdbBreakpointType.ACCESS_WATCHPOINT, curIid); + } + GdbMiFieldList hwRWpt = info.getFieldList("hw-rwpt"); + if (hwRWpt != null) { + return parseWpt(hwRWpt, GdbBreakpointType.READ_WATCHPOINT, curIid); + } + throw new AssertionError("No breakpoint or watchpoint in info: " + info); + } + + /** + * Parse the usual {@code bkpt} fields + * + * @param bkpt the breakpoint field list + * @param allLocs all (sub)locations given in the info or table body + * @param curIid in case of missing {@code thread-ids} field, the current inferior id + * @return the info + */ + public static GdbBreakpointInfo parseBkpt(GdbMiFieldList bkpt, + List allLocs, Integer curIid) { + long number = Long.parseLong(bkpt.getString("number")); + GdbBreakpointType type = GdbBreakpointType.fromStr(bkpt.getString("type")); + GdbBreakpointDisp disp = GdbBreakpointDisp.fromStr(bkpt.getString("disp")); + boolean enabled = "y".equals(bkpt.getString("enabled")); + String addr = bkpt.getString("addr"); + String at = bkpt.getString("at"); + if (at == null) { + at = bkpt.getString("what"); + } + String origLoc = bkpt.getString("original-location"); + String pending = bkpt.getString("pending"); + int times = Integer.parseInt(bkpt.getString("times")); + List locations = new ArrayList<>(); + + if ("".equals(addr)) { + allLocs.stream().filter(l -> l.getNumber() == number).forEachOrdered(locations::add); + } + else { + List iids = parseIids(bkpt); + if (iids == null) { + iids = curIid == null ? List.of() : List.of(curIid); + } + locations.add(new GdbBreakpointLocation(number, 1, true, addr, iids)); + } + return new GdbBreakpointInfo(number, type, disp, addr, at, origLoc, pending, enabled, times, + locations); + } + + /** + * Parse watchpoint fields and synthesize the info + * + * @param wpt the watchpoint field list + * @param type the type of watchpoint + * @param curIid the inferior id in which the watchpoint was created + * @return the info + */ + public static GdbBreakpointInfo parseWpt(GdbMiFieldList wpt, GdbBreakpointType type, + int curIid) { + int number = Integer.parseInt(wpt.getString("number")); + String origLoc = wpt.getString("exp"); + + List locs; + if (origLoc.startsWith(GdbBreakpointLocation.WATCHPOINT_LOCATION_PREFIX)) { + locs = List.of(new GdbBreakpointLocation(number, 1, true, null, List.of(curIid))); + } + else { + locs = List.of(); + } + return new GdbBreakpointInfo(number, type, GdbBreakpointDisp.KEEP, null, origLoc, origLoc, + null, true, 0, locs); + } + + /** + * Parse all (sub)locations from the given info or table body + * + * @param info the info or table body + * @return all locations parsed + */ + public static List parseLocations(GdbMiFieldList info) { + List locations = new ArrayList<>(); + for (Object obj : info.get(GdbMiParser.UNNAMED)) { + GdbMiFieldList loc = (GdbMiFieldList) obj; + String[] locIdParts = loc.getString("number").split("\\."); + long locNumber = Long.parseLong(locIdParts[0]); + long locSub = Long.parseLong(locIdParts[1]); + boolean locEnabled = "y".equals(loc.getString("enabled")); + String locAddr = loc.getString("addr"); + List locIids = parseIids(loc); + + locations.add( + new GdbBreakpointLocation(locNumber, locSub, locEnabled, locAddr, locIids)); + } + return locations; + } + + private final long number; + private final GdbBreakpointType type; + private final GdbBreakpointDisp disp; + private final String addr; + private final String at; + private final String originalLocation; + private final String pending; + private final boolean enabled; + private final int times; + private final List locations; + + /** + * Construct GDB breakpoint information + * + * @param number the GDB-assigned breakpoint number + * @param type the type of breakpoint + * @param disp the breakpoint disposition + * @param addr the location of the breakpoint + * @param pending if pending, the location that is not yet resolved + * @param enabled true if the breakpoint is enabled, false otherwise + * @param times the number of times the breakpoint has been hit + * @param locations the resolved address(es) of this breakpoint + */ + GdbBreakpointInfo(long number, GdbBreakpointType type, GdbBreakpointDisp disp, String addr, + String at, String origLoc, String pending, boolean enabled, int times, + List locations) { + this.number = number; + this.type = type; + this.disp = disp; + this.addr = addr; + this.at = at; + this.originalLocation = origLoc; + this.pending = pending; + this.enabled = enabled; + this.times = times; + this.locations = Collections.unmodifiableList(locations); + } + + @Override + public int hashCode() { + return Objects.hash(number, type, disp, addr, pending, enabled, times, locations); + } + + @Override + public String toString() { + return ""; + } + + @Override + public boolean equals(Object obj) { + if (!((obj instanceof GdbBreakpointInfo))) { + return false; + } + GdbBreakpointInfo that = (GdbBreakpointInfo) obj; + if (this.number != that.number) { + return false; + } + if (this.type != that.type) { + return false; + } + if (this.disp != that.disp) { + return false; + } + if (!Objects.equals(this.addr, that.addr)) { + return false; + } + if (!Objects.equals(this.pending, that.pending)) { + return false; + } + if (this.enabled != that.enabled) { + return false; + } + if (this.times != that.times) { + return false; + } + if (!Objects.equals(this.locations, that.locations)) { + return false; + } + return true; + } + + /** + * Get the GDB-assigned breakpoint number + * + * This is the key into GDB's breakpoint table to locate the breakpoint this information + * describes. + * + * @return the number + */ + public long getNumber() { + return number; + } + + /** + * Get the type of breakpoint + * + * @return the type + */ + public GdbBreakpointType getType() { + return type; + } + + /** + * Get the breakpoint disposition, i.e., what happens to the breakpoint once it has been hit + * + * @return the disposition + */ + public GdbBreakpointDisp getDisp() { + return disp; + } + + /** + * Get the location of the breakpoint + * + * @return the location (address) + */ + public String getAddress() { + return addr; + } + + /** + * Get the user-specified location ("What" column) + * + * @return the location + */ + public String getWhat() { + return at; + } + + /** + * Get the location as specified by the user + * + * @return the original location + */ + public String getOriginalLocation() { + return originalLocation; + } + + /** + * Assuming the location is an address, get it as a long + * + * @return the address + */ + public long addrAsLong() { + return GdbParsingUtils.parsePrefixedHex(addr); + } + + /** + * If the breakpoint is pending resolution, get the location that is pending + * + * @return the pending location + */ + public String getPending() { + return pending; + } + + /** + * Check if the breakpoint is enabled + * + * @return true if enabled, false otherwise + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Get the number of times the breakpoint has been hit + * + * @return the hit count + */ + public int getTimes() { + return times; + } + + /** + * Get a list of resolved addresses + * + * The effective locations may change for a variety of reasons. Most notable, a new module may + * be loaded, having location(s) that match the desired location of this breakpoint. The binary + * addresses within will become new effective locations of this breakpoint. + * + * @return the list of locations at the time the breakpoint information was captured + */ + public List getLocations() { + return locations; + } + + public GdbBreakpointInfo withEnabled(@SuppressWarnings("hiding") boolean enabled) { + if (isEnabled() == enabled) { + return this; + } + return new GdbBreakpointInfo(number, type, disp, addr, at, originalLocation, pending, + enabled, times, locations); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInsertions.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInsertions.java new file mode 100644 index 0000000000..775f6e56c0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInsertions.java @@ -0,0 +1,75 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.breakpoint; + +import java.util.concurrent.CompletableFuture; + +public interface GdbBreakpointInsertions { + /** + * Insert a breakpoint + * + * This is equivalent to the CLI command: {@code break [LOC]}, or {@code watch [LOC]}, etc. + * + * Breakpoints in GDB can get pretty complicated. Depending on the location specification, the + * actual location of the breakpoint may change during the lifetime of an inferior. Take note of + * the breakpoint number to track those changes across breakpoint modification events. + * + * @param loc the location (address, symbol, line number, etc.) to place the breakpoint at + * @param type the type of breakpoint (or watchpoint) to add + * @return a copy of the resulting breakpoint information once GDB has executed the command + */ + CompletableFuture insertBreakpoint(String loc, GdbBreakpointType type); + + /** + * Insert a (usually software) execution breakpoint + * + * @see #insertBreakpoint(String, GdbBreakpointType) + */ + default CompletableFuture insertBreakpoint(String loc) { + return insertBreakpoint(loc, GdbBreakpointType.BREAKPOINT); + } + + /** + * Insert a breakpoint (usually a watchpoint) at the given address range + * + * Note, this implements the length by casting the address pointer to a + * fixed-length-char-array-pointer where the array has the given length. Support for specific + * lengths may vary by platform. + * + * @param addr the starting address + * @param len the length of the range + * @param type the type of breakpoint (usually a watchpoint) + * @see #insertBreakpoint(String, GdbBreakpointType) + */ + default CompletableFuture insertBreakpoint(long addr, int len, + GdbBreakpointType type) { + if (len != 1 && type.isWatchpoint()) { + return insertBreakpoint(String.format("*((char(*)[%d]) 0x%x)", len, addr), type); + } + return insertBreakpoint(String.format("*0x%x", addr), type); + } + + /** + * Insert a (usually software) execution breakpoint at the given address offset + * + * Note, this uses the "Address Notation" specified by GDB. + * + * @see #insertBreakpoint(String, GdbBreakpointType) + */ + default CompletableFuture insertBreakpoint(long addr) { + return insertBreakpoint(String.format("*0x%x", addr)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointLocation.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointLocation.java new file mode 100644 index 0000000000..7fde987b86 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointLocation.java @@ -0,0 +1,145 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.breakpoint; + +import java.util.*; + +import agent.gdb.manager.parsing.GdbParsingUtils; + +/** + * A location of a breakpoint + * + * Some breakpoint information blocks list multiple locations. Keeping this information in a + * separate object allows the parsing and tracking of these locations. Usually multiple locations + * are presented by GDB when a single location specification resolves to multiple addresses. + */ +public class GdbBreakpointLocation { + public static final String WATCHPOINT_LOCATION_PREFIX = "-location "; + + private final long number; + private final long sub; + private final boolean enabled; + private final String addr; + private final List inferiorIds; + + /** + * Construct a breakpoint location + * + * @param number the number of breakpoint location, i.e., {@code x} in {@code x.y} + * @param sub the number of the breakpoint location, i.e., {@code y} in {@code x.y} + * @param enabled true if the location is enabled, false otherwise + * @param addr the address of this location, usually resolved, but maybe not + * @param inferiorIds a list of inferior IDs to which this location applies + */ + GdbBreakpointLocation(long number, long sub, boolean enabled, String addr, + List inferiorIds) { + this.number = number; + this.sub = sub; + this.enabled = enabled; + this.addr = addr; + this.inferiorIds = Collections.unmodifiableList(inferiorIds); + } + + @Override + public String toString() { + return ""; + } + + /** + * Get the breakpoint number, i.e., {@code x} in {@code x.y} + * + * @return the breakpoint number + */ + public long getNumber() { + return number; + } + + /** + * If present, get the location number, i.e., {@code y} in {@code x.y} + * + * @return the location number, or 0 + */ + public long getSub() { + return sub; + } + + /** + * Check if the location is enabled + * + * @return true if enabled, false otherwise + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Get the address, usually resolved, but maybe not + * + * @see {@link GdbBreakpointInfo#getPending()} + * @return the address + */ + public String getAddr() { + return addr; + } + + /** + * If numerical, get the address as a long + * + * @return the address + */ + public long addrAsLong() { + return GdbParsingUtils.parsePrefixedHex(addr); + } + + /** + * Get a list of inferior IDs to which this location applies + * + * @return the list of inferiors + */ + public List getInferiorIds() { + return inferiorIds; + } + + @Override + public int hashCode() { + return Objects.hash(number, sub, enabled, addr, inferiorIds); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof GdbBreakpointLocation)) { + return false; + } + GdbBreakpointLocation that = (GdbBreakpointLocation) obj; + if (this.number != that.number) { + return false; + } + if (this.sub != that.sub) { + return false; + } + if (this.enabled != that.enabled) { + return false; + } + if (!Objects.equals(this.addr, that.addr)) { + return false; + } + if (!Objects.equals(this.inferiorIds, that.inferiorIds)) { + return false; + } + return true; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointType.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointType.java new file mode 100644 index 0000000000..514092471d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointType.java @@ -0,0 +1,88 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.breakpoint; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * The type of GDB breakpoint + */ +public enum GdbBreakpointType { + /** + * A software execution breakpoint, usually set via {@code break} + */ + BREAKPOINT("breakpoint", false), + /** + * A hardware execution breakpoint, usually set via {@code hbreak} + */ + HW_BREAKPOINT("hw breakpoint", false), + /** + * A debug printf point, usually set via {@code dprint} + */ + DPRINTF("dprintf", false), + /** + * A hardware (write) watchpoint, usually set via {@code watch} + */ + HW_WATCHPOINT("hw watchpoint", true), + /** + * A read watchpoint, usually set via {@code rwatch} + */ + READ_WATCHPOINT("read watchpoint", true), + /** + * An access (r/w) watchpoint, usually set via {@code awatch} + */ + ACCESS_WATCHPOINT("acc watchpoint", true), + /** + * Some type not known to the manager + */ + OTHER("", false); + + public static final Map BY_NAME = + List.of(values()).stream().collect(Collectors.toMap(v -> v.getName(), v -> v)); + + /** + * Parse a type from a GDB/MI breakpoint information block + * + * @param string the value of type parsed + * @return the enum constant, or {@link #OTHER} if unrecognized + */ + public static GdbBreakpointType fromStr(String string) { + return BY_NAME.getOrDefault(string, OTHER); + } + + private final String name; + private final boolean isWatchpoint; + + private GdbBreakpointType(String name, boolean isWatchpoint) { + this.name = name; + this.isWatchpoint = isWatchpoint; + } + + @Override + public String toString() { + return name; + } + + public String getName() { + return name; + } + + public boolean isWatchpoint() { + return isWatchpoint; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbCompletedCommandEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbCompletedCommandEvent.java new file mode 100644 index 0000000000..6bbfd4288f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbCompletedCommandEvent.java @@ -0,0 +1,65 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import agent.gdb.manager.parsing.GdbParsingUtils; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * A base class for GDB command completion events + * + * Subclasses must specify the state implied by GDB issuing the event + */ +public abstract class AbstractGdbCompletedCommandEvent extends AbstractGdbEventWithFields { + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public AbstractGdbCompletedCommandEvent(CharSequence tail) throws GdbParseError { + super(tail); + } + + /** + * Construct a new event with the given information + * + * @param info the information + */ + public AbstractGdbCompletedCommandEvent(GdbMiFieldList fields) { + super(fields); + } + + /** + * Assume an inferior is specified, and get its ID + * + * @return the inferior ID + */ + public int assumeInferior() { + return GdbParsingUtils.parseInferiorId(getInfo().getString("inferior")); + } + + /** + * Assume a message is specified, and get that message + * + * @return the message + */ + public String assumeMsg() { + return getInfo().getString("msg"); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEvent.java new file mode 100644 index 0000000000..71841069da --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEvent.java @@ -0,0 +1,100 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.GdbCause; +import agent.gdb.manager.GdbCause.Causes; +import agent.gdb.manager.GdbState; +import agent.gdb.manager.impl.GdbEvent; +import agent.gdb.manager.impl.GdbPendingCommand; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * A base class for GDB events + * + * @param the type of information detailing the event + */ +public abstract class AbstractGdbEvent implements GdbEvent { + private final T info; + protected GdbCause cause = Causes.UNCLAIMED; + protected boolean stolen = false; + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + protected AbstractGdbEvent(CharSequence tail) throws GdbParseError { + this.info = parseInfo(tail); + } + + /** + * Construct a new event with the given information + * + * @param info the information + */ + protected AbstractGdbEvent(T info) { + this.info = info; + } + + /** + * Parse the tail into the required information type + * + * @param tail the text following the event type in the GDB/MI event record + * @return the parsed information + * @throws GdbParseError if the tail cannot be parsed + */ + protected abstract T parseInfo(CharSequence tail) throws GdbParseError; + + @Override + public T getInfo() { + return info; + } + + @Override + public void claim(GdbPendingCommand cmd) { + if (cause != Causes.UNCLAIMED) { + throw new IllegalStateException("Event is already claimed by " + cause); + } + cause = cmd; + } + + @Override + public GdbCause getCause() { + return cause; + } + + @Override + public void steal() { + stolen = true; + } + + @Override + public boolean isStolen() { + return stolen; + } + + @Override + public String toString() { + return "<" + getClass().getSimpleName() + " " + info + " >"; + } + + @Override + public GdbState newState() { + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithFields.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithFields.java new file mode 100644 index 0000000000..ffd03727da --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithFields.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbMiParser; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * A base class for GDB events whose details are a list of fields + */ +public abstract class AbstractGdbEventWithFields extends AbstractGdbEvent { + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public AbstractGdbEventWithFields(CharSequence tail) throws GdbParseError { + super(tail); + } + + /** + * Construct a new event with the given fields + * + * @param fields the fields + */ + public AbstractGdbEventWithFields(GdbMiFieldList fields) { + super(fields); + } + + @Override + protected GdbMiFieldList parseInfo(CharSequence tail) throws GdbParseError { + return GdbMiParser.parseFields(tail); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithStateChange.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithStateChange.java new file mode 100644 index 0000000000..a22de6cf36 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithStateChange.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.GdbState; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; +import agent.gdb.manager.reason.GdbReason; + +/** + * A base class for GDB events notifying of state changes + * + * Subclasses must specify the state implied by GDB issuing the event. This base class will parse + * the reason if it is given by GDB. + */ +public abstract class AbstractGdbEventWithStateChange extends AbstractGdbEventWithFields { + private final GdbReason reason; + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public AbstractGdbEventWithStateChange(CharSequence tail) throws GdbParseError { + super(tail); + this.reason = GdbReason.getReason(getInfo()); + } + + /** + * If applicable, get the reason for the event + * + * @return the reason, or {@link GdbReason.Reasons#NONE} + */ + public GdbReason getReason() { + return reason; + } + + @Override + public abstract GdbState newState(); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithString.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithString.java new file mode 100644 index 0000000000..3db056a340 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbEventWithString.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbMiParser; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * A base class for GDB events whose details are a string + */ +public abstract class AbstractGdbEventWithString extends AbstractGdbEvent { + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + protected AbstractGdbEventWithString(CharSequence tail) throws GdbParseError { + super(tail); + } + + /** + * Construct a new event with the given information + * + * @param info the information + */ + public AbstractGdbEventWithString(String info) { + super(info); + } + + @Override + protected String parseInfo(CharSequence tail) throws GdbParseError { + return GdbMiParser.parseString(tail); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbLibraryEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbLibraryEvent.java new file mode 100644 index 0000000000..e66d135d06 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbLibraryEvent.java @@ -0,0 +1,102 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import java.util.Objects; + +import agent.gdb.manager.GdbLibraryId; +import agent.gdb.manager.parsing.GdbParsingUtils; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * A base class for GDB events regarding loaded libraries + */ +public abstract class AbstractGdbLibraryEvent extends AbstractGdbEventWithFields { + private final GdbLibraryId lid; + private final String targetName; + private final Integer iid; + + private static class LibId implements GdbLibraryId { + private final String id; + + public LibId(String id) { + this.id = id; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof LibId)) { + return false; + } + LibId that = (LibId) obj; + return Objects.equals(this.id, that.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } + } + + /** + * Construct a new event, parsing the tail for information + * + * The library's ID and target name must be specified by GDB. The applicable inferiors + * (actually, thread groups) may also be specified. If not, the manager assumes all inferiors. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public AbstractGdbLibraryEvent(CharSequence tail) throws GdbParseError { + super(tail); + this.lid = new LibId(getInfo().getString("id")); + this.targetName = getInfo().getString("target-name"); + String gid = getInfo().getString("thread-group"); + if (gid == null) { + this.iid = null; + } + else { + this.iid = GdbParsingUtils.parseInferiorId(gid); + } + } + + /** + * Get the library ID + * + * @return the ID + */ + public GdbLibraryId getLibraryId() { + return lid; + } + + /** + * Get the target's name for the library + * + * @return the name + */ + public String getTargetName() { + return targetName; + } + + /** + * Get the applicable + * + * @return + */ + public Integer getInferiorId() { + return iid; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbOutputEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbOutputEvent.java new file mode 100644 index 0000000000..e7c9525e4d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbOutputEvent.java @@ -0,0 +1,71 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import java.io.PrintWriter; + +import agent.gdb.manager.impl.GdbManagerImpl.Interpreter; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * A base class for GDB output records + */ +public abstract class AbstractGdbOutputEvent extends AbstractGdbEventWithString { + protected final Interpreter interpreter; + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public AbstractGdbOutputEvent(CharSequence tail) throws GdbParseError { + super(tail); + this.interpreter = Interpreter.MI2; + } + + /** + * Construct a new event for the given output + * + * @param output the output + */ + public AbstractGdbOutputEvent(String output) { + super(output); + this.interpreter = Interpreter.CLI; + } + + /** + * Get the output + * + * GDB includes explicit line terminators, so the output may not necessarily be a complete line. + * It should be printed using, e.g., {@link PrintWriter#print(String)}, not + * {@link PrintWriter#println(String)}. + * + * @return the output + */ + public String getOutput() { + return getInfo(); + } + + /** + * Get the interpreter that produced this output + * + * @return the interpreter + */ + public Interpreter getInterpreter() { + return interpreter; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbThreadEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbThreadEvent.java new file mode 100644 index 0000000000..c7c4cf90fa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbThreadEvent.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * A base class for GDB events that involve a thread ID (including inferior ID) + */ +public abstract class AbstractGdbThreadEvent extends AbstractGdbEventWithFields { + private final int tid; + private final int iid; + + /** + * Construct a new event by parsing the tail for information + * + * The thread ID and thread group ID must be specified by GDB in the "id" and "group-id" fields, + * respectively. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public AbstractGdbThreadEvent(CharSequence tail) throws GdbParseError { + super(tail); + this.tid = Integer.parseInt(getInfo().getString("id")); + this.iid = GdbParsingUtils.parseInferiorId(getInfo().getString("group-id")); + } + + /** + * Get the thread ID + * + * @return the thread ID + */ + public int getThreadId() { + return tid; + } + + /** + * Get the inferior ID + * + * @return the inferior ID + */ + public int getInferiorId() { + return iid; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbThreadGroupEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbThreadGroupEvent.java new file mode 100644 index 0000000000..a54c9ad838 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/AbstractGdbThreadGroupEvent.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * A base class for GDB events that involve an inferior (a special kind of thread group) + */ +public abstract class AbstractGdbThreadGroupEvent extends AbstractGdbEventWithFields { + private final int iid; + + /** + * Construct a new event by parsing the tail for information + * + * The thread group ID must be specified by GDB in the "id" field. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public AbstractGdbThreadGroupEvent(CharSequence tail) throws GdbParseError { + super(tail); + this.iid = GdbParsingUtils.parseInferiorId(getInfo().getString("id")); + } + + /** + * Get the inferior ID + * + * @return the inferior ID + */ + public int getInferiorId() { + return iid; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointCreatedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointCreatedEvent.java new file mode 100644 index 0000000000..677a8b2fa4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointCreatedEvent.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import agent.gdb.manager.impl.GdbManagerImpl; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =breakpoint-created}" + */ +public class GdbBreakpointCreatedEvent extends AbstractGdbEventWithFields { + private final GdbBreakpointInfo bkptInfo; + + /** + * Construct a new event by parsing the tail for information + * + * The breakpoint information must be specified by GDB. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbBreakpointCreatedEvent(CharSequence tail, GdbManagerImpl manager) + throws GdbParseError { + super(tail); + this.bkptInfo = GdbBreakpointInfo.parse(getInfo(), manager.currentInferior().getId()); + } + + /** + * Get the breakpoint information + * + * @return the parsed, but not processed, breakpoint information + */ + public GdbBreakpointInfo getBreakpointInfo() { + return bkptInfo; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointDeletedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointDeletedEvent.java new file mode 100644 index 0000000000..0de06c0a18 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointDeletedEvent.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =breakpoint-deleted}" + */ +public class GdbBreakpointDeletedEvent extends AbstractGdbEventWithFields { + private final long number; + + /** + * Construct a new event by parsing the tail for information + * + * The breakpoint number must be specified by GDB. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbBreakpointDeletedEvent(CharSequence tail) throws GdbParseError { + super(tail); + this.number = Long.parseLong(getInfo().getString("id")); + // TODO: See what happens if I am allowed to delete x.y form breakpoint locations + } + + /** + * Get the breakpoint number + * + * @return the breakpoint number + */ + public long getNumber() { + return number; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointModifiedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointModifiedEvent.java new file mode 100644 index 0000000000..65c7c718f2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbBreakpointModifiedEvent.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =breakpoint-modified}" + */ +public class GdbBreakpointModifiedEvent extends AbstractGdbEventWithFields { + private final GdbBreakpointInfo bkptInfo; + + /** + * Construct a new event by parsing the tail for information + * + * The breakpoint information must be specified by GDB. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbBreakpointModifiedEvent(CharSequence tail) throws GdbParseError { + super(tail); + this.bkptInfo = GdbBreakpointInfo.parse(getInfo(), null); + } + + /** + * Get the breakpoint information + * + * @return the parsed, but not processed, breakpoint information + */ + public GdbBreakpointInfo getBreakpointInfo() { + return bkptInfo; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandConnectedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandConnectedEvent.java new file mode 100644 index 0000000000..b0a2b79ede --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandConnectedEvent.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code ^connected}" + */ +public class GdbCommandConnectedEvent extends AbstractGdbCompletedCommandEvent { + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbCommandConnectedEvent(CharSequence tail) throws GdbParseError { + super(tail); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandDoneEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandDoneEvent.java new file mode 100644 index 0000000000..4e17e8fa96 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandDoneEvent.java @@ -0,0 +1,199 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import java.util.ArrayList; +import java.util.List; + +import agent.gdb.manager.GdbInferiorThreadGroup; +import agent.gdb.manager.GdbProcessThreadGroup; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import agent.gdb.manager.parsing.GdbParsingUtils; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; +import ghidra.util.Msg; + +/** + * The event corresponding with "{@code ^done}" + */ +public class GdbCommandDoneEvent extends AbstractGdbCompletedCommandEvent { + private static Long parseLong(String nullable) { + return nullable == null ? null : Long.parseLong(nullable); + } + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbCommandDoneEvent(CharSequence tail) throws GdbParseError { + super(tail); + } + + /** + * Assume inferior groups are specified, and get those groups' IDs + * + * @return the list of inferior thread-group IDs + */ + public List assumeInferiorGroups() { + List groups = getInfo().getListOf(GdbMiFieldList.class, "groups"); + List iids = new ArrayList<>(); + for (GdbMiFieldList groupInfo : groups) { + String gid = groupInfo.getString("id"); + if (!gid.startsWith("i")) { + continue; + } + int iid = GdbParsingUtils.parseInferiorId(gid); + String type = groupInfo.getString("type"); + String pid = groupInfo.getString("pid"); + String exitCode = groupInfo.getString("exit-code"); + String executable = groupInfo.getString("executable"); + iids.add(new GdbInferiorThreadGroup(iid, type, parseLong(pid), parseLong(exitCode), + executable)); + } + return iids; + } + + /** + * Assume process groups are specified, and get those processes' descriptions + * + * @return the list of (available) process thread-groups + */ + public List assumeProcessGroups() { + List groups = getInfo().getListOf(GdbMiFieldList.class, "groups"); + List pids = new ArrayList<>(); + for (GdbMiFieldList groupInfo : groups) { + if (!"process".equals(groupInfo.getString("type"))) { + Msg.error(this, "Unexpected type in available thread groups: " + groupInfo); + // TODO: If necessary, communicate type and id in the returned list + continue; + } + String gid = groupInfo.getString("id"); + String desc = groupInfo.getString("description"); + try { + pids.add(new GdbProcessThreadGroup(Integer.parseInt(gid), desc)); + } + catch (NumberFormatException e) { + Msg.error(this, "Unexpected group id in available thread groups: " + groupInfo); + } + } + return pids; + } + + /** + * Assume threads are specified, and get those threads' IDs + * + * @return the list of thread IDs + */ + public List assumeThreadIds() { + List threads = getInfo().getListOf(GdbMiFieldList.class, "threads"); + List tids = new ArrayList<>(); + for (GdbMiFieldList threadInfo : threads) { + String tid = threadInfo.getString("id"); + try { + tids.add(Integer.parseInt(tid)); + } + catch (NumberFormatException e) { + Msg.error(this, "Unexpected thread id in: " + threadInfo); + } + } + return tids; + } + + /** + * Assume a value is specified, and get it as a string + * + * @return the value + */ + public String assumeValue() { + return getInfo().getString("value"); + } + + /** + * Check if a value is specified, and get it as a string + * + * @return the value, or null if not specified + */ + public String maybeValue() { + if (getInfo().containsKey("value")) { + return assumeValue(); + } + return null; + } + + /** + * Assume an "OSDataTable" is specified, and get that table + * + * @return the parsed, but not processed, table + */ + public GdbMiFieldList assumeOSDataTable() { + return getInfo().getFieldList("OSDataTable"); + } + + /** + * Assume a register name list is specified, and get that list + * + * @return the parsed, but not processed, list + */ + public List assumeRegisterNameList() { + return getInfo().getListOf(String.class, "register-names"); + } + + /** + * Assume a register value list is specified, and get that list + * + * @return the parsed, but not processed, list + */ + public List assumeRegisterValueList() { + return getInfo().getListOf(GdbMiFieldList.class, "register-values"); + } + + /** + * Assume a memory contents list is specified, and get that list + * + * @return the parsed, but not processed, list + */ + public List assumeMemoryContentsList() { + return getInfo().getListOf(GdbMiFieldList.class, "memory"); + } + + /** + * Assume a thread info list is specified, and get that list + * + * @return the parsed, but not processed, list + */ + public List assumeThreadInfoList() { + return getInfo().getListOf(GdbMiFieldList.class, "threads"); + } + + /** + * Assume a breakpoint table is specified, and get that table + * + * @return the parsed, but not processed, table + */ + public GdbMiFieldList assumeBreakpointTable() { + return getInfo().getFieldList("BreakpointTable"); + } + + /** + * Assume a stack is specified, and get that stack + * + * @return the parsed, but not processed, stack + */ + public GdbMiFieldList assumeStack() { + return getInfo().getFieldList("stack"); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandEchoEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandEchoEvent.java new file mode 100644 index 0000000000..6d70814cf0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandEchoEvent.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * An "event" corresponding with GDB/MI commands + * + * If using a PTY configured with local echo, the manager needs to recognize and ignore the commands + * it issued. GDB/MI makes them easy to distinguish, because they start with "-". + */ +public class GdbCommandEchoEvent extends AbstractGdbEvent { + + /** + * Construct a new "event", passing the tail through as information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbCommandEchoEvent(CharSequence tail) throws GdbParseError { + super(tail); + } + + @Override + protected String parseInfo(CharSequence tail) throws GdbParseError { + return tail.toString(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandErrorEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandErrorEvent.java new file mode 100644 index 0000000000..c506be9925 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandErrorEvent.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.impl.GdbEvent; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code ^error}" + */ +public class GdbCommandErrorEvent extends AbstractGdbCompletedCommandEvent { + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @return the new event + * @throws GdbParseError if the tail cannot be parsed + */ + public static GdbCommandErrorEvent fromMi2(CharSequence tail) throws GdbParseError { + return new GdbCommandErrorEvent(tail); + } + + /** + * Construct a new event using the given error message + * + * @param message the message + * @return the new event + */ + public static GdbEvent fromMessage(String message) { + return new GdbCommandErrorEvent(message); + } + + protected GdbCommandErrorEvent(CharSequence tail) throws GdbParseError { + super(tail); + } + + protected GdbCommandErrorEvent(String message) { + super(GdbMiFieldList.builder().add("msg", message.trim()).build()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandExitEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandExitEvent.java new file mode 100644 index 0000000000..857b528bd9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandExitEvent.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.GdbState; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code ^exit}" + */ +public class GdbCommandExitEvent extends AbstractGdbCompletedCommandEvent { + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbCommandExitEvent(CharSequence tail) throws GdbParseError { + super(tail); + } + + @Override + public GdbState newState() { + return GdbState.EXIT; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandRunningEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandRunningEvent.java new file mode 100644 index 0000000000..4b2b9bbe9f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbCommandRunningEvent.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.GdbState; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code ^running}" + */ +public class GdbCommandRunningEvent extends AbstractGdbCompletedCommandEvent { + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbCommandRunningEvent(CharSequence tail) throws GdbParseError { + super(tail); + } + + /** + * Construct a new event with no information + */ + public GdbCommandRunningEvent() { + super(GdbMiFieldList.builder().build()); + } + + @Override + public GdbState newState() { + return null; // Let *running cause change, as it has more info. + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbConsoleOutputEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbConsoleOutputEvent.java new file mode 100644 index 0000000000..61f5238cb9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbConsoleOutputEvent.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code ~""}" output records + */ +public class GdbConsoleOutputEvent extends AbstractGdbOutputEvent { + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @return the new event + * @throws GdbParseError if the tail cannot be parsed + */ + public static GdbConsoleOutputEvent fromMi2(CharSequence tail) throws GdbParseError { + return new GdbConsoleOutputEvent(tail); + } + + /** + * Construct a new event with the given output + * + * @param output the line of output + * @return the new event + */ + public static GdbConsoleOutputEvent fromCli(String output) { + return new GdbConsoleOutputEvent(output + "\n"); + } + + // Hidden in favor of named factory methods + protected GdbConsoleOutputEvent(CharSequence tail) throws GdbParseError { + super(tail); + } + + // Hidden in favor of named factory methods, as this is easily called accidentally + protected GdbConsoleOutputEvent(String output) { + super(output); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbDebugOutputEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbDebugOutputEvent.java new file mode 100644 index 0000000000..b28b23fb82 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbDebugOutputEvent.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code &""}" output records + */ +public class GdbDebugOutputEvent extends AbstractGdbOutputEvent { + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbDebugOutputEvent(CharSequence tail) throws GdbParseError { + super(tail); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbLibraryLoadedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbLibraryLoadedEvent.java new file mode 100644 index 0000000000..3cbb5a18e2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbLibraryLoadedEvent.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =library-loaded}" + */ +public class GdbLibraryLoadedEvent extends AbstractGdbLibraryEvent { + + /** + * Construct a new event, parsing the tail for information + * + * The library's ID and target name must be specified by GDB. The applicable inferiors + * (actually, thread groups) may also be specified. If not, the manager assumes all inferiors. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbLibraryLoadedEvent(CharSequence tail) throws GdbParseError { + super(tail); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbLibraryUnloadedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbLibraryUnloadedEvent.java new file mode 100644 index 0000000000..4d5ee62acc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbLibraryUnloadedEvent.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =library-unloaded}" + */ +public class GdbLibraryUnloadedEvent extends AbstractGdbLibraryEvent { + + /** + * Construct a new event, parsing the tail for information + * + * The library's ID and target name must be specified by GDB. The applicable inferiors + * (actually, thread groups) may also be specified. If not, the manager assumes all inferiors. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbLibraryUnloadedEvent(CharSequence tail) throws GdbParseError { + super(tail); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbMemoryChangedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbMemoryChangedEvent.java new file mode 100644 index 0000000000..a3e4b53313 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbMemoryChangedEvent.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =memory-changed}" + */ +public class GdbMemoryChangedEvent extends AbstractGdbEventWithFields { + private final int iid; + private final long addr; + private final int len; + + /** + * Construct a new event by parsing the tail for information + * + * The thread group, start address, and length must be specified by GDB. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbMemoryChangedEvent(CharSequence tail) throws GdbParseError { + super(tail); + this.iid = GdbParsingUtils.parseInferiorId(getInfo().getString("thread-group")); + this.addr = GdbParsingUtils.parsePrefixedHex(getInfo().getString("addr")); + this.len = Integer.parseInt(getInfo().getString("len")); + } + + /** + * Get the inferior ID + * + * @return the inferior ID + */ + public int getInferiorId() { + return iid; + } + + /** + * Get the starting address of the change + * + * @return the starting address + */ + public long getAddress() { + return addr; + } + + /** + * Get the length of the change + * + * @return the length + */ + public int getLength() { + return len; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbParamChangedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbParamChangedEvent.java new file mode 100644 index 0000000000..37c7d133ae --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbParamChangedEvent.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +public class GdbParamChangedEvent extends AbstractGdbEventWithFields { + public GdbParamChangedEvent(CharSequence tail) throws GdbParseError { + super(tail); + } + // TODO +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbRunningEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbRunningEvent.java new file mode 100644 index 0000000000..aa54db6f97 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbRunningEvent.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.GdbState; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code *running}" + */ +public class GdbRunningEvent extends AbstractGdbEventWithStateChange { + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbRunningEvent(CharSequence tail) throws GdbParseError { + super(tail); + } + + @Override + public GdbState newState() { + return GdbState.RUNNING; + } + + /** + * Assume a thread ID is specified, and get that ID + * + * @return the ID + */ + public String assumeThreadId() { + return getInfo().getString("thread-id"); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbStoppedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbStoppedEvent.java new file mode 100644 index 0000000000..46b48cfa99 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbStoppedEvent.java @@ -0,0 +1,77 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.GdbState; +import agent.gdb.manager.impl.GdbStackFrameImpl; +import agent.gdb.manager.impl.GdbThreadImpl; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code *stopped}" + */ +public class GdbStoppedEvent extends AbstractGdbEventWithStateChange { + private final Integer tid; + + /** + * Construct a new event, parsing the tail for information + * + * A thread ID must be specified by GDB. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbStoppedEvent(CharSequence tail) throws GdbParseError { + super(tail); + String tidStr = getInfo().getString("thread-id"); + this.tid = tidStr == null ? null : Integer.parseInt(tidStr); + } + + /** + * Get the ID of the thread causing the event + * + * @return the thread ID + */ + public Integer getThreadId() { + return tid; + } + + /** + * Get the current frame, if applicable + * + * @param thread the current thread + * @return the frame + */ + public GdbStackFrameImpl getFrame(GdbThreadImpl thread) { + GdbMiFieldList fields = getInfo().getFieldList("frame"); + return fields == null ? null : GdbStackFrameImpl.fromFieldList(thread, fields); + } + + /** + * Get the list of threads stopped by this event + * + * @return the list as a string, possibly "all" + */ + public String assumeStoppedThreads() { + return getInfo().getString("stopped-threads"); + } + + @Override + public GdbState newState() { + return GdbState.STOPPED; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbTargetOutputEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbTargetOutputEvent.java new file mode 100644 index 0000000000..6dfbad1ec2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbTargetOutputEvent.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code @""}" output records + */ +public class GdbTargetOutputEvent extends AbstractGdbOutputEvent { + + /** + * Construct a new event, parsing the tail for information + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbTargetOutputEvent(CharSequence tail) throws GdbParseError { + super(tail); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadCreatedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadCreatedEvent.java new file mode 100644 index 0000000000..9448288a19 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadCreatedEvent.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =thread-created}" + */ +public class GdbThreadCreatedEvent extends AbstractGdbThreadEvent { + + /** + * Construct a new event by parsing the tail for information + * + * The thread ID and thread group ID must be specified by GDB. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbThreadCreatedEvent(CharSequence tail) throws GdbParseError { + super(tail); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadExitedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadExitedEvent.java new file mode 100644 index 0000000000..bc8d9ebfef --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadExitedEvent.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =thread-exited}" + */ +public class GdbThreadExitedEvent extends AbstractGdbThreadEvent { + + /** + * Construct a new event by parsing the tail for information + * + * The thread ID and thread group ID must be specified by GDB. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbThreadExitedEvent(CharSequence tail) throws GdbParseError { + super(tail); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupAddedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupAddedEvent.java new file mode 100644 index 0000000000..4cdf15af00 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupAddedEvent.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =thread-group-added}" + */ +public class GdbThreadGroupAddedEvent extends AbstractGdbThreadGroupEvent { + + /** + * Construct a new event by parsing the tail for information + * + * The thread group ID must be specified by GDB in the "id" field. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbThreadGroupAddedEvent(CharSequence tail) throws GdbParseError { + super(tail); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupExitedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupExitedEvent.java new file mode 100644 index 0000000000..4b6e4e4dec --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupExitedEvent.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =thread-group-exited}" + */ +public class GdbThreadGroupExitedEvent extends AbstractGdbThreadGroupEvent { + private Long exitCode; + + /** + * Construct a new event by parsing the tail for information + * + * The thread group ID must be specified by GDB in the "id" field. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbThreadGroupExitedEvent(CharSequence tail) throws GdbParseError { + super(tail); + String exitCodeStr = getInfo().getString("exit-code"); + if (exitCodeStr != null) { + this.exitCode = GdbParsingUtils.parsePrefixedOctal(exitCodeStr); + } + else { + this.exitCode = null; + } + } + + /** + * Check if the event specifies an exit code + * + * @return true if an exit code is present, false otherwise + */ + public boolean hasExitCode() { + return exitCode != null; + } + + /** + * Get the exit code + * + * @return the exit code if present, or null + */ + public Long getExitCode() { + return exitCode; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupRemovedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupRemovedEvent.java new file mode 100644 index 0000000000..8904609077 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupRemovedEvent.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =thread-group-removed}" + */ +public class GdbThreadGroupRemovedEvent extends AbstractGdbThreadGroupEvent { + + /** + * Construct a new event by parsing the tail for information + * + * The thread group ID must be specified by GDB in the "id" field. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbThreadGroupRemovedEvent(CharSequence tail) throws GdbParseError { + super(tail); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupStartedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupStartedEvent.java new file mode 100644 index 0000000000..6b4f3022e2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadGroupStartedEvent.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =thread-group-started}" + */ +public class GdbThreadGroupStartedEvent extends AbstractGdbThreadGroupEvent { + private final long pid; + + /** + * Construct a new event by parsing the tail for information + * + * The thread group ID must be specified by GDB in the "id" field. The process ID of the new + * process must also be specified. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbThreadGroupStartedEvent(CharSequence tail) throws GdbParseError { + super(tail); + this.pid = Long.parseLong(getInfo().getString("pid")); + } + + /** + * Get the the process ID + * + * @return the process ID + */ + public long getPid() { + return pid; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadSelectedEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadSelectedEvent.java new file mode 100644 index 0000000000..9857f3d3e2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/evt/GdbThreadSelectedEvent.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.evt; + +import agent.gdb.manager.impl.GdbStackFrameImpl; +import agent.gdb.manager.impl.GdbThreadImpl; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +/** + * The event corresponding with "{@code =thread-selected}" + */ +public class GdbThreadSelectedEvent extends AbstractGdbEventWithFields { + private final int tid; + + /** + * Construct a new event by parsing the tail for information + * + * The selected thread ID must be specified by GDB. + * + * @param tail the text following the event type in the GDB/MI event record + * @throws GdbParseError if the tail cannot be parsed + */ + public GdbThreadSelectedEvent(CharSequence tail) throws GdbParseError { + super(tail); + this.tid = Integer.parseInt(getInfo().getString("id")); + } + + /** + * Get the selected thread ID + * + * @return the thread ID + */ + public int getThreadId() { + return tid; + } + + /** + * Get the selected frame, if applicable + * + * @param thread the selected thread + * @return the frame + */ + public GdbStackFrameImpl getFrame(GdbThreadImpl thread) { + GdbMiFieldList fields = getInfo().getFieldList("frame"); + return fields == null ? null : GdbStackFrameImpl.fromFieldList(thread, fields); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbCommand.java new file mode 100644 index 0000000000..8c78f11883 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbCommand.java @@ -0,0 +1,90 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.GdbState; +import agent.gdb.manager.impl.GdbManagerImpl.Interpreter; +import agent.gdb.manager.impl.cmd.AbstractGdbCommand; +import agent.gdb.manager.impl.cmd.AbstractGdbCommandWithThreadId; + +/** + * The interface for GDB command implementations + * + * Commands are executed by GDB in serial. In order to distinguish the likely cause of events, the + * manager will wait to issue each command until it has seen a prompt. Thus, commands are queued up, + * and the manager uses the {@link CompletableFuture} pattern to "return" results after execution + * completes. Once issued, a command is presumed to be executing until another prompt is received. + * This generally immediately follows the command result, i.e., the "^..." result line in GDB/MI. + * The command implementation is responsible for handling the command result. Implementors ought to + * use {@link AbstractGdbCommand} or {@link AbstractGdbCommandWithThreadId} to ensure consistent + * processing. + * + * To begin executing the command, the manager encodes the command, using {@link #encode()}. Any + * event that occurs during command execution is given to + * {@link #handle(GdbEvent, GdbPendingCommand)}. The implementor then has the option to "claim" or + * "steal" the event. When claimed, any subsequent event processor or listener is provided this + * command as the event's cause. When stolen, no subsequent event processors are called. The + * implementation ought to keep a list of claimed and stolen events. Once GDB has finished executing + * the command, the manager calls {@link #complete(GdbPendingCommand)}, allowing the implementation + * to process its claimed and stolen events and return the result of the command. + * + * @param the type of object "returned" by the command + */ +public interface GdbCommand { + + /** + * Check if this command can be executed given GDB's current state + * + * @param state GDB's state + * @return true if it can be executed, false otherwise + */ + public boolean validInState(GdbState state); + + /** + * Encode the command in GDB/MI + * + * @return the encoded command + */ + public String encode(); + + /** + * Handle an event that ocurred during the execution of this command + * + * @param evt the event + * @param pending a copy of the executing command instance + * @return true if the command is now ready to be completed + */ + public boolean handle(GdbEvent evt, GdbPendingCommand pending); + + /** + * Called when the manager believes this command is finished executing + * + * This is presumed when the manager receives the prompt after issuing the encoded command + * + * @param pending a copy of the now-finished-executing command instance + * @return the object "returned" by the command + */ + public T complete(GdbPendingCommand pending); + + /** + * Get the interpreter for which this command is encoded + * + * @return the interpreter + */ + public Interpreter getInterpreter(); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbEvent.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbEvent.java new file mode 100644 index 0000000000..c1dacc24dd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbEvent.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import agent.gdb.manager.GdbCause; +import agent.gdb.manager.GdbState; + +/** + * The interface for GDB events + * + * @param the type of parsed information detailing the event + */ +public interface GdbEvent { + + /** + * Get the information detailing the event + * + * @return the information + */ + public T getInfo(); + + /** + * Use {@link GdbPendingCommand#claim(GdbEvent)} instead + * + * @param cause the cause + */ + public void claim(GdbPendingCommand cause); + + /** + * If claimed, get the cause of this event + * + * @return the cause + */ + public GdbCause getCause(); + + /** + * Use {@link GdbPendingCommand#steal(GdbEvent)} instead + */ + public void steal(); + + /** + * Check if this event is stolen + * + * A stolen event should not be processed further, except by the thief + * + * @return true if stolen, false otherwise + */ + public boolean isStolen(); + + /** + * If this event implies a new GDB state, get that state + * + * @return the new state, or null for no change + */ + public GdbState newState(); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbFrameInfo.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbFrameInfo.java new file mode 100644 index 0000000000..a44af599ad --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbFrameInfo.java @@ -0,0 +1,116 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.util.*; + +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +/** + * Information about a GDB frame + * + *

+ * The contains the semantic processing for GDB frame information. + * + *

+ * Note this is not a handle to the frame. Rather, this is the captured information from some event + * or request. If other commands have been executed since this information was gathered, the + * information may be stale. + */ +public class GdbFrameInfo { + + /** + * Process a parsed GDB frame information + * + * @param info the parsed information block + * @return the processed GDB frame information + */ + public static GdbFrameInfo parseInfo(GdbMiFieldList info) { + String level = info.getString("level"); + String addr = info.getString("addr"); + String func = info.getString("func"); + Collection arginfo = info.get("args"); + List args = new ArrayList<>(); + for (Object object : arginfo) { + if (object instanceof GdbMiFieldList) { + args.add(((GdbMiFieldList) object).toString()); + } + } + return new GdbFrameInfo(level, addr, func, args); + } + + private final String level; + private final String addr; + private final String func; + private final List args; + + /** + * Construct GDB frame information + * + * @param level frame id + * @param addr the stack address + * @param func the enclosing function + * @param args the function args + */ + GdbFrameInfo(String level, String addr, String func, List args) { + this.level = level; + this.addr = addr; + this.func = func; + this.args = args; + } + + @Override + public int hashCode() { + return Objects.hash(getLevel(), getAddr()); + } + + @Override + public String toString() { + return ""; + } + + @Override + public boolean equals(Object obj) { + if (!((obj instanceof GdbFrameInfo))) { + return false; + } + GdbFrameInfo that = (GdbFrameInfo) obj; + if (this.getLevel() != that.getLevel()) { + return false; + } + if (this.getAddr() != that.getAddr()) { + return false; + } + return true; + } + + public String getLevel() { + return level; + } + + public String getAddr() { + return addr; + } + + public String getFunc() { + return func; + } + + public List getArgs() { + return args; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbInferiorImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbInferiorImpl.java new file mode 100644 index 0000000000..05576e4b09 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbInferiorImpl.java @@ -0,0 +1,495 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.google.common.collect.RangeSet; + +import agent.gdb.manager.*; +import agent.gdb.manager.GdbManager.ExecSuffix; +import agent.gdb.manager.impl.cmd.*; +import ghidra.async.AsyncLazyValue; +import ghidra.lifecycle.Internal; +import ghidra.util.Msg; + +/** + * The implementation of {@link GdbInferior} + */ +public class GdbInferiorImpl implements GdbInferior { + protected static final Pattern MEMORY_MAPPING_LINE_PATTERN = Pattern.compile("\\s*" + // + "0x(?[0-9,A-F,a-f]+)\\s+" + // + "0x(?[0-9,A-F,a-f]+)\\s+" + // + "0x(?[0-9,A-F,a-f]+)\\s+" + // + "0x(?[0-9,A-F,a-f]+)\\s*" + // + "(?\\S*)\\s*"); + + private final GdbManagerImpl manager; + private final int id; + + private Long pid; // Not always present + private Long exitCode; // Not always present + private String executable; // Not always present + + private ByteOrder endianness; + + private final Map threads = new LinkedHashMap<>(); + private final Map unmodifiableThreads = + Collections.unmodifiableMap(threads); + + private final Map modules = new LinkedHashMap<>(); + private final Map unmodifiableModules = Collections.unmodifiableMap(modules); + + // Because asking GDB to list sections lists those of all modules + protected final AsyncLazyValue loadSections = new AsyncLazyValue<>(this::doLoadSections); + + private final NavigableMap mappings = new TreeMap<>(); + private final NavigableMap unmodifiableMappings = + Collections.unmodifiableNavigableMap(mappings); + + public GdbInferiorImpl(GdbManagerImpl manager, int id) { + this.manager = manager; + this.id = id; + } + + /** + * Construct a new inferior + * + * @param manager the manager creating the inferior + * @param id the GDB-assigned inferior ID + */ + public GdbInferiorImpl(GdbManagerImpl manager, GdbInferiorThreadGroup g) { + this(manager, g.getInferiorId()); + update(g); + } + + public void update(GdbInferiorThreadGroup g) { + this.pid = g.getPid(); + this.exitCode = g.getExitCode(); + this.executable = g.getExecutable(); + } + + @Override + public String toString() { + return ""; + } + + @Override + public int getId() { + return id; + } + + /** + * Set the process ID of this inferior + * + * An inferior is associated to exactly one process at a time, but since it may be restarted, it + * may be associated with different processes at different times. This method allows the manager + * to set the PID when it changes. + * + * @param pid the PID + */ + public void setPid(long pid) { + this.pid = pid; + } + + @Override + public Long getPid() { + return pid; + } + + /** + * Set the inferior exit code + * + * When the inferior exits (or rather its associated process exits), this allows the manager to + * set the exit code. + * + * @param exitCode the exit code (status or signal) + */ + public void setExitCode(Long exitCode) { + this.exitCode = exitCode; + } + + @Override + public Long getExitCode() { + return exitCode; + } + + @Override + public String getExecutable() { + return executable; + } + + /** + * Add this inferior to the manager's list of inferiors, because of a given cause + * + * @param cause the cause of the new inferior + */ + public void add(GdbCause cause) { + manager.addInferior(this, cause); + } + + /** + * Remove this inferior from the manager's list of inferiors, because of a given cause + * + * @param cause the cause of removal + */ + public void remove(GdbCause cause) { + manager.removeInferior(id, cause); + } + + /** + * Use {@link GdbThreadImpl#add()} instead + * + * @param thread the thread to add + */ + public void addThread(GdbThreadImpl thread) { + GdbThreadImpl exists = threads.get(thread.getId()); + if (exists != null) { + throw new IllegalArgumentException("There is already thread " + exists); + } + threads.put(thread.getId(), thread); + + } + + @Override + public GdbThreadImpl getThread(int tid) { + GdbThreadImpl result = threads.get(tid); + if (result == null) { + throw new IllegalArgumentException("There is no thread with id " + tid); + } + return result; + } + + /** + * Use {@link GdbThreadImpl#remove()} instead + * + * @param tid the ID of the thread to remove + */ + public void removeThread(int tid) { + if (threads.remove(tid) == null) { + throw new IllegalArgumentException("There is no thread with id " + tid); + } + } + + @Override + public Map getKnownThreads() { + return unmodifiableThreads; + } + + public Map getKnownThreadsImpl() { + return threads; + } + + @Override + public CompletableFuture> listThreads() { + return manager.execute(new GdbListThreadsCommand(manager, this)); + } + + @Override + public Map getKnownModules() { + return unmodifiableModules; + } + + @Override + public CompletableFuture> listModules() { + // "unlikely" is an unlikely section name. Goal is to exclude section lines. + // TODO: See how this behaves on other GDB versions. + return manager.consoleCapture("maintenance info sections ALLOBJ unlikely") + .thenApply(this::parseModuleNames); + } + + protected CompletableFuture loadSections() { + return loadSections.request(); + } + + protected CompletableFuture doLoadSections() { + return manager.consoleCapture("maintenance info sections ALLOBJ") + .thenAccept(this::parseAndUpdateAllModuleSections); + } + + protected GdbModuleImpl resyncCreateModule(String name) { + Msg.warn(this, "Resync: Missed loaded module/library: " + name); + //manager.listenersInferior.fire.libraryLoaded(this, name, Causes.UNCLAIMED); + return createModule(name); + } + + protected GdbModuleImpl createModule(String name) { + return new GdbModuleImpl(this, name); + } + + protected void libraryLoaded(String name) { + modules.computeIfAbsent(name, this::createModule); + } + + protected void libraryUnloaded(String name) { + modules.remove(name); + } + + protected void resyncRetainModules(Set names) { + for (Iterator> mit = modules.entrySet().iterator(); mit + .hasNext();) { + Entry ent = mit.next(); + if (!names.contains(ent.getKey())) { + Msg.warn(this, "Resync: Missed unloaded module/library: " + ent); + /*manager.listenersInferior.fire.libraryUnloaded(this, ent.getKey(), + Causes.UNCLAIMED);*/ + } + } + } + + protected void parseAndUpdateAllModuleSections(String out) { + Set namesSeen = new HashSet<>(); + GdbModuleImpl curModule = null; + for (String line : out.split("\n")) { + Matcher nameMatcher = GdbModuleImpl.OBJECT_FILE_LINE_PATTERN.matcher(line); + if (nameMatcher.matches()) { + if (curModule != null) { + curModule.loadSections.provide().complete(null); + } + String name = nameMatcher.group("name"); + namesSeen.add(name); + curModule = modules.computeIfAbsent(name, this::resyncCreateModule); + // NOTE: This will usurp the module's lazy loader, but we're about to + // provide it anyway + if (curModule.loadSections.isDone()) { + curModule = null; + } + continue; + } + if (curModule == null) { + continue; + } + curModule.processSectionLine(line); + } + if (curModule != null) { + curModule.loadSections.provide().complete(null); + } + resyncRetainModules(namesSeen); + } + + protected Map parseModuleNames(String out) { + Set namesSeen = new HashSet<>(); + for (String line : out.split("\n")) { + Matcher nameMatcher = GdbModuleImpl.OBJECT_FILE_LINE_PATTERN.matcher(line); + if (nameMatcher.matches()) { + String name = nameMatcher.group("name"); + namesSeen.add(name); + modules.computeIfAbsent(name, this::resyncCreateModule); + } + } + resyncRetainModules(namesSeen); + return unmodifiableModules; + } + + @Override + public Map getKnownMappings() { + return unmodifiableMappings; + } + + @Override + public CompletableFuture> listMappings() { + return manager.consoleCapture("info proc mappings").thenApply(this::parseMappings); + } + + protected Map parseMappings(String out) { + Set startsSeen = new TreeSet<>(); + for (String line : out.split("\n")) { + Matcher mappingMatcher = MEMORY_MAPPING_LINE_PATTERN.matcher(line); + if (!mappingMatcher.matches()) { + continue; + } + try { + BigInteger start = new BigInteger(mappingMatcher.group("start"), 16); + BigInteger end = new BigInteger(mappingMatcher.group("end"), 16); + BigInteger size = new BigInteger(mappingMatcher.group("size"), 16); + BigInteger offset = new BigInteger(mappingMatcher.group("offset"), 16); + String objfile = mappingMatcher.group("file"); + startsSeen.add(start); + mappings.put(start, new GdbMemoryMapping(start, end, size, offset, objfile)); + } + catch (NumberFormatException e) { + Msg.error(this, "Could not parse mapping entry: " + line, e); + } + } + mappings.keySet().retainAll(startsSeen); + return unmodifiableMappings; + } + + @Override + public CompletableFuture select() { + return manager.selectInferior(this); + } + + @Override + public CompletableFuture fileExecAndSymbols(String file) { + return select().thenCompose(__ -> { + return manager.execute(new GdbFileExecAndSymbolsCommand(manager, file)); + }); + } + + @Override + public CompletableFuture run() { + return select().thenCompose(__ -> { + return manager.execute(new GdbRunCommand(manager)); + }); + } + + @Override + public CompletableFuture> attach(long toPid) { + return select().thenCompose(__ -> { + return manager.execute(new GdbAttachCommand(manager, toPid)); + }); + } + + protected CompletableFuture preferThread( + Function> viaThread, + Supplier> viaThis) { + Optional first = threads.values().stream().findFirst(); + if (first.isPresent()) { + return viaThread.apply(first.get()); + } + return select().thenCompose(__ -> viaThis.get()); + } + + @Override + public CompletableFuture console(String command) { + return preferThread(t -> t.console(command), () -> manager.console(command)); + } + + @Override + public CompletableFuture consoleCapture(String command) { + return preferThread(t -> t.consoleCapture(command), () -> manager.consoleCapture(command)); + } + + @Override + public CompletableFuture cont() { + return select().thenCompose(__ -> { + return manager.execute(new GdbContinueCommand(manager, null)); + }); + } + + @Override + public CompletableFuture step(ExecSuffix suffix) { + return select().thenCompose(__ -> { + return manager.execute(new GdbStepCommand(manager, null, suffix)); + }); + } + + @Override + public CompletableFuture evaluate(String expression) { + return preferThread(t -> t.evaluate(expression), + () -> manager.execute(new GdbEvaluateCommand(manager, null, null, expression))); + } + + @Override + public CompletableFuture setTty(String tty) { + return select().thenCompose(__ -> { + return manager.execute(new GdbSetInferiorTtyCommand(manager, tty)); + }); + } + + @Override + public CompletableFuture getVar(String varName) { + // TODO: Are these actually per-inferior? + // If so, should make them accessible via thread + return select().thenCompose(__ -> { + return manager.execute(new GdbGetVarCommand(manager, varName)); + }); + } + + @Override + public CompletableFuture setVar(String varName, String val) { + // TODO: Are these actually per-inferior? + // If so, should make them accessible via thread + return select().thenCompose(__ -> { + return manager.execute(new GdbSetVarCommand(manager, null, varName, val)); + }); + } + + @Override + public CompletableFuture detach() { + return select().thenCompose(__ -> { + return manager.execute(new GdbDetachCommand(manager, this, null)); + }); + } + + @Override + public CompletableFuture kill() { + return select().thenCompose(__ -> { + return manager.execute(new GdbKillCommand(manager, null)); + }); + } + + @Override + public CompletableFuture> readMemory(long addr, ByteBuffer buf, int len) { + // I can't imagine this working without a thread.... + return preferThread(t -> t.readMemory(addr, buf, len), + () -> manager.execute(new GdbReadMemoryCommand(manager, null, addr, buf, len))); + } + + @Override + public CompletableFuture writeMemory(long addr, ByteBuffer buf, int len) { + // I can't imagine this working without a thread.... + return preferThread(t -> t.writeMemory(addr, buf, len), + () -> manager.execute(new GdbWriteMemoryCommand(manager, null, addr, buf, len))); + } + + @Override + public CompletableFuture remove() { + return manager.removeInferior(this); + } + + @Override + public String getDescriptor() { + if (pid != null) { + return "process " + pid; + } + return ""; + } + + @Internal + public CompletableFuture syncEndianness() { + return consoleCapture("show endian").thenAccept(out -> { + if (out.toLowerCase().contains("little endian")) { + endianness = ByteOrder.LITTLE_ENDIAN; + } + else if (out.toLowerCase().contains("big endian")) { + endianness = ByteOrder.BIG_ENDIAN; + } + else { + endianness = null; + } + }); + } + + @Internal + public ByteOrder getEndianness() { + if (endianness == null) { + throw new AssertionError("Could not determine target endianness"); + } + return endianness; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java new file mode 100644 index 0000000000..84202a2a88 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java @@ -0,0 +1,1501 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import static ghidra.async.AsyncUtils.loop; + +import java.io.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.python.core.PyDictionary; +import org.python.util.InteractiveConsole; + +import agent.gdb.ffi.linux.Pty; +import agent.gdb.ffi.linux.PtyMaster; +import agent.gdb.manager.*; +import agent.gdb.manager.GdbCause.Causes; +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import agent.gdb.manager.breakpoint.GdbBreakpointType; +import agent.gdb.manager.evt.*; +import agent.gdb.manager.impl.cmd.*; +import agent.gdb.manager.parsing.GdbMiParser; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; +import ghidra.async.*; +import ghidra.dbg.util.HandlerMap; +import ghidra.dbg.util.PrefixMap; +import ghidra.lifecycle.Internal; +import ghidra.util.Msg; +import ghidra.util.datastruct.ListenerSet; +import sun.misc.Signal; +import sun.misc.SignalHandler; + +/** + * Implementation of {@link GdbManager} + * + *

+ * This is implemented using the asynchronous chaining library and executors. A single-threaded + * executor handles issuing GDB command and processing events. Another thread handles reading GDB's + * output and parsing events. Those events are then scheduled for processing on the executor. The + * executor guarantees that commands are executed serially while reducing the risk of deadlock since + * the asynchronous calls return futures immediately. + * + *

+ * A {@link PrefixMap} aids in parsing GDB events. The event details are then parsed with the + * {@link GdbMiParser} and passed around for processing. If a command is currently executed, that + * command has the first option to claim or steal the event. If it is stolen, no further processing + * takes place in the manager. If no command is executing, or the command does not steal it, the + * event is processed using a {@link HandlerMap}. + */ +public class GdbManagerImpl implements GdbManager { + @Internal + public enum Interpreter { + CLI, MI2; + } + + private static final boolean LOG_IO = false; + private static final PrintWriter DBG_LOG; + static { + if (LOG_IO) { + try { + DBG_LOG = new PrintWriter(new FileOutputStream(new File("DBG.log"))); + } + catch (FileNotFoundException e) { + throw new AssertionError(e); + } + } + else { + DBG_LOG = null; + } + } + private static final String PROMPT_GDB = "(gdb)"; + public static final int INTERRUPT_MAX_RETRIES = 3; + public static final int INTERRUPT_RETRY_PERIOD_MILLIS = 100; + + private final AsyncReference state = + new AsyncReference<>(GdbState.NOT_STARTED); + // A copy of state, which is updated on the eventThread. + private final AsyncReference asyncState = new AsyncReference<>(state.get()); + private Interpreter runningInterpreter; + private final AsyncReference mi2Prompt = new AsyncReference<>(false); + + private final PrefixMap, GdbParseError> mi2PrefixMap = new PrefixMap<>(); + private final HandlerMap, Void, Void> handlerMap = new HandlerMap<>(); + private final AtomicBoolean exited = new AtomicBoolean(false); + + private Pty cliPty; + private Pty mi2Pty; + private Process gdb; + private Thread gdbWaiter; + + private Thread cliReader; + private Thread mi2Reader; + private PrintWriter cliWriter; + private PrintWriter mi2Writer; + + private final AsyncLock cmdLock = new AsyncLock(); + private final AtomicReference cmdLockHold = new AtomicReference<>(null); + private ExecutorService executor; + private final AsyncTimer timer = AsyncTimer.DEFAULT_TIMER; + + private GdbPendingCommand curCmd = null; + + private final Map inferiors = new LinkedHashMap<>(); + private GdbInferiorImpl curInferior = null; + private final Map unmodifiableInferiors = + Collections.unmodifiableMap(inferiors); + + private final Map threads = new LinkedHashMap<>(); + private final Map unmodifiableThreads = + Collections.unmodifiableMap(threads); + + private final Map breakpoints = new LinkedHashMap<>(); + private final Map unmodifiableBreakpoints = + Collections.unmodifiableMap(breakpoints); + + protected final ListenerSet listenersEvent = + new ListenerSet<>(GdbEventsListener.class); + protected final ListenerSet listenersTargetOutput = + new ListenerSet<>(GdbTargetOutputListener.class); + protected final ListenerSet listenersConsoleOutput = + new ListenerSet<>(GdbConsoleOutputListener.class); + protected final ExecutorService eventThread = Executors.newSingleThreadExecutor(); + + /** + * Instantiate a new manager + */ + public GdbManagerImpl() { + state.filter(this::stateFilter); + state.addChangeListener(this::trackRunningInterpreter); + state.addChangeListener((os, ns, c) -> event(() -> asyncState.set(ns, c), "managerState")); + defaultPrefixes(); + defaultHandlers(); + } + + CompletableFuture event(Runnable r, String text) { + //Msg.debug(this, "Queueing event: " + text); + return CompletableFuture.runAsync(r, eventThread).exceptionally(ex -> { + Msg.error(this, "Error in event callback:", ex); + return ExceptionUtils.rethrow(ex); + }); + } + + private GdbState stateFilter(GdbState cur, GdbState set, GdbCause cause) { + if (set == null) { + return cur; + } + return set; + } + + private void trackRunningInterpreter(GdbState oldSt, GdbState st, GdbCause cause) { + if (st == GdbState.RUNNING && cause instanceof GdbPendingCommand) { + GdbPendingCommand pcmd = (GdbPendingCommand) cause; + runningInterpreter = pcmd.getCommand().getInterpreter(); + //Msg.debug(this, "Entered " + st + " with interpreter: " + runningInterpreter); + } + else { + runningInterpreter = null; + } + } + + private void defaultPrefixes() { + mi2PrefixMap.put("-", GdbCommandEchoEvent::new); + mi2PrefixMap.put("~", GdbConsoleOutputEvent::fromMi2); + mi2PrefixMap.put("@", GdbTargetOutputEvent::new); + mi2PrefixMap.put("&", GdbDebugOutputEvent::new); + + mi2PrefixMap.put("^done", GdbCommandDoneEvent::new); + mi2PrefixMap.put("^running", GdbCommandRunningEvent::new); + mi2PrefixMap.put("^connected", GdbCommandConnectedEvent::new); + mi2PrefixMap.put("^exit", GdbCommandExitEvent::new); + mi2PrefixMap.put("^error", GdbCommandErrorEvent::fromMi2); + + mi2PrefixMap.put("*running", GdbRunningEvent::new); + mi2PrefixMap.put("*stopped", GdbStoppedEvent::new); + + mi2PrefixMap.put("=thread-group-added", GdbThreadGroupAddedEvent::new); + mi2PrefixMap.put("=thread-group-removed", GdbThreadGroupRemovedEvent::new); + mi2PrefixMap.put("=thread-group-started", GdbThreadGroupStartedEvent::new); + mi2PrefixMap.put("=thread-group-exited", GdbThreadGroupExitedEvent::new); + + mi2PrefixMap.put("=thread-created", GdbThreadCreatedEvent::new); + mi2PrefixMap.put("=thread-exited", GdbThreadExitedEvent::new); + mi2PrefixMap.put("=thread-selected", GdbThreadSelectedEvent::new); + mi2PrefixMap.put("=library-loaded", GdbLibraryLoadedEvent::new); + mi2PrefixMap.put("=library-unloaded", GdbLibraryUnloadedEvent::new); + mi2PrefixMap.put("=breakpoint-created", t -> new GdbBreakpointCreatedEvent(t, this)); + mi2PrefixMap.put("=breakpoint-modified", GdbBreakpointModifiedEvent::new); + mi2PrefixMap.put("=breakpoint-deleted", GdbBreakpointDeletedEvent::new); + + mi2PrefixMap.put("=memory-changed", GdbMemoryChangedEvent::new); + mi2PrefixMap.put("=cmd-param-changed", GdbParamChangedEvent::new); + } + + private void defaultHandlers() { + handlerMap.putVoid(GdbCommandEchoEvent.class, this::ignoreCmdEcho); + handlerMap.putVoid(GdbConsoleOutputEvent.class, this::processStdOut); + handlerMap.putVoid(GdbTargetOutputEvent.class, this::processTargetOut); + handlerMap.putVoid(GdbDebugOutputEvent.class, this::processStdErr); + + handlerMap.putVoid(GdbCommandDoneEvent.class, this::processCommandDone); + handlerMap.putVoid(GdbCommandRunningEvent.class, this::processCommandRunning); + handlerMap.putVoid(GdbCommandConnectedEvent.class, this::processCommandConnected); + handlerMap.putVoid(GdbCommandExitEvent.class, this::processCommandExit); + handlerMap.putVoid(GdbCommandErrorEvent.class, this::processCommandError); + + handlerMap.putVoid(GdbRunningEvent.class, this::processRunning); + handlerMap.putVoid(GdbStoppedEvent.class, this::processStopped); + + handlerMap.putVoid(GdbThreadGroupAddedEvent.class, this::processThreadGroupAdded); + handlerMap.putVoid(GdbThreadGroupRemovedEvent.class, this::processThreadGroupRemoved); + handlerMap.putVoid(GdbThreadGroupStartedEvent.class, this::processThreadGroupStarted); + handlerMap.putVoid(GdbThreadGroupExitedEvent.class, this::processThreadGroupExited); + + handlerMap.putVoid(GdbThreadCreatedEvent.class, this::processThreadCreated); + handlerMap.putVoid(GdbThreadExitedEvent.class, this::processThreadExited); + handlerMap.putVoid(GdbThreadSelectedEvent.class, this::processThreadSelected); + handlerMap.putVoid(GdbLibraryLoadedEvent.class, this::processLibraryLoaded); + handlerMap.putVoid(GdbLibraryUnloadedEvent.class, this::processLibraryUnloaded); + handlerMap.putVoid(GdbBreakpointCreatedEvent.class, this::processBreakpointCreated); + handlerMap.putVoid(GdbBreakpointModifiedEvent.class, this::processBreakpointModified); + handlerMap.putVoid(GdbBreakpointDeletedEvent.class, this::processBreakpointDeleted); + + handlerMap.putVoid(GdbMemoryChangedEvent.class, this::processMemoryChanged); + } + + @Override + public boolean isAlive() { + return state.get().isAlive(); + } + + @Override + public void addStateListener(GdbStateListener listener) { + asyncState.addChangeListener(listener); + } + + @Override + public void removeStateListener(GdbStateListener listener) { + asyncState.removeChangeListener(listener); + } + + @Override + public void addEventsListener(GdbEventsListener listener) { + listenersEvent.add(listener); + } + + @Override + public void removeEventsListener(GdbEventsListener listener) { + listenersEvent.remove(listener); + } + + @Internal // for detach command + public void fireThreadExited(int tid, GdbInferiorImpl inferior, GdbCause cause) { + event(() -> listenersEvent.fire.threadExited(tid, inferior, cause), "threadExited"); + } + + @Override + public void addTargetOutputListener(GdbTargetOutputListener listener) { + listenersTargetOutput.add(listener); + } + + @Override + public void removeTargetOutputListener(GdbTargetOutputListener listener) { + listenersTargetOutput.remove(listener); + } + + @Override + public void addConsoleOutputListener(GdbConsoleOutputListener listener) { + listenersConsoleOutput.add(listener); + } + + @Override + public void removeConsoleOutputListener(GdbConsoleOutputListener listener) { + listenersConsoleOutput.remove(listener); + } + + /** + * Use {@link GdbThreadImpl#add()} instead + * + * @param thread the thread to add + */ + public void addThread(GdbThreadImpl thread) { + GdbThreadImpl exists = threads.get(thread.getId()); + if (exists != null) { + throw new IllegalArgumentException("There is already thread " + exists); + } + threads.put(thread.getId(), thread); + } + + @Override + public GdbThreadImpl getThread(int tid) { + GdbThreadImpl result = threads.get(tid); + if (result == null) { + throw new IllegalArgumentException("There is no thread with id " + tid); + } + return result; + } + + public CompletableFuture getThreadInfo(int threadId) { + return execute(new GdbGetThreadInfoCommand(this, threadId)); + } + + /** + * Use {@link GdbThreadImpl#remove()} instead + * + * @param tid the thread ID to remove + */ + public void removeThread(int tid) { + if (threads.remove(tid) == null) { + throw new IllegalArgumentException("There is no thread with id " + tid); + } + } + + /** + * Use {@link GdbInferiorImpl#add(GdbCause)} instead + * + * @param inferior the inferior to add + * @param cause the cause of the new inferior + */ + @Internal + public void addInferior(GdbInferiorImpl inferior, GdbCause cause) { + GdbInferiorImpl exists = inferiors.get(inferior.getId()); + if (exists != null) { + throw new IllegalArgumentException("There is already inferior " + exists); + } + inferiors.put(inferior.getId(), inferior); + event(() -> listenersEvent.fire.inferiorAdded(inferior, cause), "addInferior"); + } + + /** + * Use {@link GdbInferiorImpl#remove(GdbCause)} instead + * + * @param iid the inferior ID to remove + * @param cause the cause of removal + */ + @Internal + public void removeInferior(int iid, GdbCause cause) { + if (inferiors.remove(iid) == null) { + throw new IllegalArgumentException("There is no inferior with id " + iid); + } + event(() -> listenersEvent.fire.inferiorRemoved(iid, cause), "removeInferior"); + } + + /** + * Update the selected inferior + * + * @param inferior the inferior that now has focus + * @param cause the cause of the focus change + */ + protected boolean updateCurrentInferior(GdbInferiorImpl inferior, GdbCause cause, + boolean fire) { + // GDB will not permit removing all inferiors, so one is guaranteed to exist + // GDB may actually have already selected it, but without generating events + GdbInferiorImpl inf = inferior != null ? inferior : inferiors.values().iterator().next(); + if (curInferior != inf) { + curInferior = inf; + if (fire) { + event(() -> listenersEvent.fire.inferiorSelected(inf, cause), + "updateCurrentInferior"); + } + return true; + } + return false; + } + + @Override + public GdbInferiorImpl getInferior(int iid) { + GdbInferiorImpl result = inferiors.get(iid); + if (result == null) { + throw new IllegalArgumentException("There is no inferior with id " + iid); + } + return result; + } + + private void checkStarted() { + if (state.get() == GdbState.NOT_STARTED) { + throw new IllegalStateException( + "GDB has not been started or has not finished starting"); + } + } + + private void checkStartedNotExit() { + GdbState st = state.get(); + if (st == GdbState.NOT_STARTED || st == GdbState.EXIT) { + throw new IllegalStateException("GDB is not yet, or no longer running"); + } + } + + @Override + public GdbInferior currentInferior() { + checkStartedNotExit(); + return curInferior; + } + + @Override + public Map getKnownInferiors() { + return unmodifiableInferiors; + } + + @Internal + public Map getKnownInferiorsInternal() { + return inferiors; + } + + @Override + public Map getKnownThreads() { + return unmodifiableThreads; + } + + @Override + public Map getKnownBreakpoints() { + return unmodifiableBreakpoints; + } + + private GdbBreakpointInfo addKnownBreakpoint(GdbBreakpointInfo bkpt, boolean expectExisting) { + GdbBreakpointInfo old = breakpoints.put(bkpt.getNumber(), bkpt); + if (expectExisting && old == null) { + Msg.warn(this, "Breakpoint " + bkpt.getNumber() + " is not known"); + } + else if (!expectExisting && old != null) { + Msg.warn(this, "Breakpoint " + bkpt.getNumber() + " is already known"); + } + return old; + } + + private GdbBreakpointInfo getKnownBreakpoint(long number) { + GdbBreakpointInfo info = breakpoints.get(number); + if (info == null) { + Msg.warn(this, "Breakpoint " + number + " is not known"); + } + return info; + } + + private GdbBreakpointInfo removeKnownBreakpoint(long number) { + GdbBreakpointInfo del = breakpoints.remove(number); + if (del == null) { + Msg.warn(this, "Breakpoint " + number + " is not known"); + } + return del; + } + + @Override + public CompletableFuture insertBreakpoint(String loc, + GdbBreakpointType type) { + return execute(new GdbInsertBreakpointCommand(this, null, loc, type)); + } + + @Override + public CompletableFuture disableBreakpoints(long... numbers) { + return execute(new GdbDisableBreakpointsCommand(this, numbers)); + } + + @Override + public CompletableFuture enableBreakpoints(long... numbers) { + return execute(new GdbEnableBreakpointsCommand(this, numbers)); + } + + @Override + public CompletableFuture deleteBreakpoints(long... numbers) { + return execute(new GdbDeleteBreakpointsCommand(this, numbers)); + } + + @Override + public CompletableFuture> listBreakpoints() { + return execute(new GdbListBreakpointsCommand(this, null)); + } + + private void submit(Runnable runnable) { + checkStartedNotExit(); + executor.submit(() -> { + try { + runnable.run(); + } + catch (Throwable e) { + e.printStackTrace(); + } + }); + } + + @Override + public void start(String gdbCmd, String... args) throws IOException { + List fullargs = new ArrayList<>(); + fullargs.addAll(Arrays.asList(gdbCmd)); + fullargs.addAll(Arrays.asList(args)); + + state.set(GdbState.STARTING, Causes.UNCLAIMED); + executor = Executors.newSingleThreadExecutor(); + + mi2Pty = Pty.openpty(); + if (gdbCmd != null) { + cliPty = Pty.openpty(); + try { + gdb = cliPty.getSlave().session(fullargs.toArray(new String[] {}), null); + } + catch (IOException e) { + // TODO: Seems I should declare this, but it makes client code ugly :( + throw new RuntimeException(e); + } + + PtyMaster cliMaster = cliPty.getMaster(); + cliReader = new Thread( + () -> readStream(cliMaster.getInputStream(), Channel.STDOUT, Interpreter.CLI), + "GDB Read CLI"); + cliReader.start(); + cliWriter = new PrintWriter(cliMaster.getOutputStream()); + + gdbWaiter = new Thread(this::waitGdbExit, "GDB WaitExit"); + gdbWaiter.start(); + + cliWriter.println("new-ui mi2 " + mi2Pty.getSlave().getFile()); + cliWriter.flush(); + } + else { + System.out.println( + "Agent is waiting for GDB/MI v2 interpreter at " + mi2Pty.getSlave().getFile()); + } + + PtyMaster mi2Master = mi2Pty.getMaster(); + mi2Reader = new Thread( + () -> readStream(mi2Master.getInputStream(), Channel.STDOUT, Interpreter.MI2), + "GDB Read MI2"); + mi2Reader.start(); + mi2Writer = new PrintWriter(mi2Master.getOutputStream()); + } + + @Override + public CompletableFuture runRC() { + return waitForPrompt().thenCompose(__ -> rc()); + } + + /** + * Execute commands upon GDB startup + * + * @return a future which completes when the rc commands are complete + */ + protected CompletableFuture rc() { + return AsyncUtils.NIL; + } + + private void waitGdbExit() { + try { + int exitcode = gdb.waitFor(); + state.set(GdbState.EXIT, Causes.UNCLAIMED); + exited.set(true); + if (!executor.isShutdown()) { + submit(() -> { + processGdbExited(exitcode); + terminate(); + }); + } + } + catch (InterruptedException e) { + terminate(); + } + } + + private void readStream(InputStream in, Channel channel, Interpreter interpreter) { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + try { + String line; + while (isAlive() && null != (line = reader.readLine())) { + String l = line; + //Msg.debug(this, channel + ": " + line); + submit(() -> { + if (LOG_IO) { + DBG_LOG.println("<" + interpreter + ": " + l); + DBG_LOG.flush(); + } + processLine(l, channel, interpreter); + }); + } + } + catch (Throwable e) { + terminate(); + Msg.debug(this, channel + "," + interpreter + " reader exiting because " + e); + //throw new AssertionError(e); + } + } + + @Override + public synchronized void terminate() { + Msg.debug(this, "Terminating " + this); + checkStarted(); + exited.set(true); + executor.shutdownNow(); + if (cliPty != null) { + gdbWaiter.interrupt(); + cliReader.interrupt(); + } + mi2Reader.interrupt(); + if (gdb != null) { + gdb.destroyForcibly(); + } + try { + if (cliPty != null) { + cliPty.close(); + } + mi2Pty.close(); + } + catch (IOException e) { + throw new AssertionError(e); + } + cmdLock.dispose("GDB is terminating"); + state.dispose("GDB is terminating"); + mi2Prompt.dispose("GDB is terminating"); + for (GdbThreadImpl thread : threads.values()) { + thread.dispose("GDB is terminating"); + } + GdbPendingCommand cc = this.curCmd; // read volatile + if (cc != null && !cc.isDone()) { + cc.completeExceptionally(new IllegalStateException("GDB is terminating")); + } + } + + /** + * Schedule a command for execution + * + * @param cmd the command to execute + * @return the pending command, which acts as a future for later completion + */ + protected GdbPendingCommand execute(GdbCommand cmd) { + assert cmd != null; + checkStartedNotExit(); + GdbPendingCommand pcmd = new GdbPendingCommand<>(cmd); + + //Msg.debug(this, "WAITING cmdLock: " + pcmd); + cmdLock.acquire(null).thenAccept(hold -> { + cmdLockHold.set(hold); + //Msg.debug(this, "ACQUIRED cmdLock: " + pcmd); + synchronized (this) { + if (curCmd != null) { + throw new AssertionError("Cannot execute more than one command at a time"); + } + if (gdb != null && !cmd.validInState(state.get())) { + throw new GdbCommandError( + "Command " + cmd + " is not valid while " + state.get()); + } + curCmd = pcmd; + } + //Msg.debug(this, "CURCMD = " + curCmd); + String text = cmd.encode(); + if (text != null) { + Interpreter interpreter = cmd.getInterpreter(); + PrintWriter wr = getWriter(interpreter); + //Msg.debug(this, "STDIN: " + text); + wr.println(text); + wr.flush(); + if (LOG_IO) { + DBG_LOG.println(">" + interpreter + ": " + text); + DBG_LOG.flush(); + } + } + }).exceptionally((exc) -> { + pcmd.completeExceptionally(exc); + //Msg.debug(this, "ON_EXCEPTION: CURCMD = " + curCmd); + curCmd = null; + //Msg.debug(this, "SET CURCMD = null"); + //Msg.debug(this, "RELEASING cmdLock"); + cmdLockHold.getAndSet(null).release(); + return null; + }); + return pcmd; + } + + protected PrintWriter getWriter(Interpreter interpreter) { + switch (interpreter) { + case CLI: + return cliWriter; + case MI2: + return mi2Writer; + default: + throw new AssertionError(); + } + } + + protected synchronized void processEvent(GdbEvent evt) { + /** + * NOTE: I've forgotten why, but the the state update needs to happen between handle and + * finish. + */ + boolean cmdFinished = false; + if (curCmd != null) { + cmdFinished = curCmd.handle(evt); + } + + GdbState newState = evt.newState(); + //Msg.debug(this, evt + " transitions state to " + newState); + state.set(newState, evt.getCause()); + + if (cmdFinished) { + event(curCmd::finish, "curCmd::finish"); + curCmd = null; + cmdLockHold.getAndSet(null).release(); + } + + // NOTE: Do not check if claimed here. + // Downstream processing should check for cause + handlerMap.handle(evt, null); + } + + /** + * Schedule a line of GDB output for processing + * + * Before the implementation started using a PTY, the channel was used to distinguish whether + * the line was read from stdout or stderr. Now, all output is assumed to be from stdout. + * + * @param line the line + * @param channel the channel producing the line (stdout) + */ + protected synchronized void processLine(String line, Channel channel, Interpreter interpreter) { + if (interpreter == Interpreter.CLI) { + processEvent(GdbConsoleOutputEvent.fromCli(line)); + return; + } + if ("".equals(line.trim())) { + return; + } + //Msg.debug(this, "processing: " + channel + ": " + line); + mi2Prompt.set(false, null); // Go ahead and fire on a second consecutive prompt + if (PROMPT_GDB.equals(line.trim())) { + if (state.get() == GdbState.STARTING) { + state.set(GdbState.STOPPED, Causes.UNCLAIMED); + } + //Msg.debug(this, "AT_PROMPT: CURCMD = " + curCmd); + /*if (curCmd != null) { + curCmd.finish(); + curCmd = null; + //Msg.debug(this, "SET CURCMD = null"); + //Msg.debug(this, "RELEASING cmdLock"); + cmdLockHold.getAndSet(null).release(); + }*/ + mi2Prompt.set(true, null); + } + else { + GdbEvent evt = null; + try { + while (line.startsWith("^C")) { + Msg.info(this, "Got ^C"); + line = line.substring(2); + } + evt = mi2PrefixMap.construct(line); + if (evt == null) { + Msg.warn(this, "Unknown event: " + line); + return; + } + processEvent(evt); + } + catch (GdbParseError e) { + throw new RuntimeException("GDB gave an unrecognized response", e); + } + catch (IllegalArgumentException e) { + Msg.warn(this, e.getMessage()); + } + } + } + + protected void processGdbExited(int exitcode) { + Msg.info(this, "GDB exited with code " + exitcode); + } + + /** + * Called for lines starting with "-", which are just commands echoed back by the PTY + * + * @param evt the "event" + * @param v nothing + */ + protected void ignoreCmdEcho(GdbCommandEchoEvent evt, Void v) { + // Do nothing + } + + /** + * Called for lines starting with "~", which are lines GDB would like printed to stdout + * + * @param evt the event + * @param v nothing + */ + protected void processStdOut(GdbConsoleOutputEvent evt, Void v) { + String out = evt.getOutput(); + System.out.print(out); + if (!evt.isStolen()) { + listenersConsoleOutput.fire.output(Channel.STDOUT, out); + } + if (evt.getInterpreter() == Interpreter.MI2 && + out.toLowerCase().contains("switching to inferior")) { + String[] parts = out.trim().split("\\s+"); + int iid = Integer.parseInt(parts[3]); + updateCurrentInferior(getInferior(iid), evt.getCause(), true); + } + } + + /** + * Called for lines starting with "@", which are lines printed by the target (limited support) + * + * @param evt the event + * @param v nothing + */ + protected void processTargetOut(GdbTargetOutputEvent evt, Void v) { + listenersTargetOutput.fire.output(evt.getOutput()); + } + + /** + * Called for lines starting with "&", which are lines GDB would like printed to stderr + * + * @param evt the event + * @param v nothing + */ + protected void processStdErr(GdbDebugOutputEvent evt, Void v) { + String out = evt.getOutput(); + System.err.print(out); + if (!evt.isStolen()) { + listenersConsoleOutput.fire.output(Channel.STDERR, out); + } + } + + /** + * Handler for "=thread-group-added" events + * + * @param evt the event + * @param v nothing + */ + protected void processThreadGroupAdded(GdbThreadGroupAddedEvent evt, Void v) { + int iid = evt.getInferiorId(); + GdbInferiorImpl inferior = new GdbInferiorImpl(this, iid); + /** + * Update currentInferior, but delay event. inferiorAdded callbacks may ask for + * currentInferior, so it must be up-to-date. However, inferiorSelected callbacks should not + * refer to an inferior that has not appeared in an inferiorAdded event. + */ + boolean fireSelected = false; + if (inferiors.isEmpty()) { + fireSelected = updateCurrentInferior(inferior, evt.getCause(), false); + } + inferior.add(evt.getCause()); + if (fireSelected) { + event(() -> listenersEvent.fire.inferiorSelected(inferior, evt.getCause()), + "groupAdded-sel"); + } + } + + /** + * Handler for "=thread-group-removed" events + * + * @param evt the event + * @param v nothing + */ + protected void processThreadGroupRemoved(GdbThreadGroupRemovedEvent evt, Void v) { + int iid = evt.getInferiorId(); + GdbInferiorImpl inferior = getInferior(iid); + GdbInferiorImpl cur; + boolean fireSelected = false; + if (curInferior == inferior) { + // Select a new current before removing, so no event is generated yet + cur = inferiors.values().stream().filter(i -> i != inferior).findFirst().get(); + // Can't remove all, so this should always come out true + fireSelected = updateCurrentInferior(cur, evt.getCause(), false); + } + else { + cur = null; + } + inferior.remove(evt.getCause()); + if (fireSelected) { + event(() -> listenersEvent.fire.inferiorSelected(cur, evt.getCause()), + "groupRemoved-sel"); + // Also cause GDB to generate thread selection events, if applicable + selectInferior(cur); + } + } + + /** + * Handler for "=thread-group-started" events + * + * @param evt the event + * @param v nothing + */ + protected void processThreadGroupStarted(GdbThreadGroupStartedEvent evt, Void v) { + int iid = evt.getInferiorId(); + GdbInferiorImpl inf = getInferior(iid); + inf.setPid(evt.getPid()); + event(() -> listenersEvent.fire.inferiorStarted(inf, evt.getCause()), "inferiorStarted"); + } + + /** + * Handler for "=thread-group-exited" events + * + * @param evt the event + * @param v nothing + */ + protected void processThreadGroupExited(GdbThreadGroupExitedEvent evt, Void v) { + int iid = evt.getInferiorId(); + GdbInferiorImpl inf = getInferior(iid); + inf.setExitCode(evt.getExitCode()); + event(() -> listenersEvent.fire.inferiorExited(inf, evt.getCause()), "inferiorExited"); + } + + /** + * Handler for "=thread-created" events + * + * @param evt the event + * @param v nothing + */ + protected void processThreadCreated(GdbThreadCreatedEvent evt, Void v) { + int tid = evt.getThreadId(); + int iid = evt.getInferiorId(); + GdbInferiorImpl inf = getInferior(iid); + GdbThreadImpl thread = new GdbThreadImpl(this, inf, tid); + thread.add(); + event(() -> listenersEvent.fire.threadCreated(thread, evt.getCause()), "threadCreated"); + } + + /** + * Handler for "=thread-exited" events + * + * @param evt the event + * @param v nothing + */ + protected void processThreadExited(GdbThreadExitedEvent evt, Void v) { + int tid = evt.getThreadId(); + int iid = evt.getInferiorId(); + GdbInferiorImpl inf = getInferior(iid); + GdbThreadImpl thread = inf.getThread(tid); + thread.remove(); + event(() -> listenersEvent.fire.threadExited(tid, inf, evt.getCause()), "threadExited"); + } + + /** + * Handler for "=thread-selected" events + * + * @param evt the event + * @param v nothing + */ + protected void processThreadSelected(GdbThreadSelectedEvent evt, Void v) { + int tid = evt.getThreadId(); + GdbThreadImpl thread = getThread(tid); + GdbStackFrameImpl frame = evt.getFrame(thread); + doThreadSelected(thread, frame, evt.getCause()); + } + + /** + * Fire thread (and frame) selection event + * + * @param thread the new thread + * @param frame the new frame + * @param cause the cause of the selection change + */ + public void doThreadSelected(GdbThreadImpl thread, GdbStackFrame frame, GdbCause cause) { + updateCurrentInferior(thread.getInferior(), cause, true); + event(() -> listenersEvent.fire.threadSelected(thread, frame, cause), "threadSelected"); + } + + /** + * Handler for "=library-loaded" events + * + * @param evt the event + * @param v nothing + */ + protected void processLibraryLoaded(GdbLibraryLoadedEvent evt, Void v) { + Integer iid = evt.getInferiorId(); + String name = evt.getTargetName(); + if (iid == null) { // Context of all inferiors + for (GdbInferiorImpl inf : inferiors.values()) { + inf.libraryLoaded(name); + event(() -> listenersEvent.fire.libraryLoaded(inf, name, evt.getCause()), + "libraryLoaded"); + } + } + else { + GdbInferiorImpl inf = getInferior(iid); + inf.libraryLoaded(name); + event(() -> listenersEvent.fire.libraryLoaded(inf, name, evt.getCause()), + "libraryLoaded"); + } + } + + /** + * Handler for "=library-unloaded" events + * + * @param evt the event + * @param v nothing + */ + protected void processLibraryUnloaded(GdbLibraryUnloadedEvent evt, Void v) { + Integer iid = evt.getInferiorId(); + String name = evt.getTargetName(); + if (iid == null) { // Context of all inferiors + for (GdbInferiorImpl inf : inferiors.values()) { + inf.libraryUnloaded(name); + event(() -> listenersEvent.fire.libraryUnloaded(inf, name, evt.getCause()), + "libraryUnloaded"); + } + } + else { + GdbInferiorImpl inf = getInferior(iid); + inf.libraryUnloaded(name); + event(() -> listenersEvent.fire.libraryUnloaded(inf, name, evt.getCause()), + "libraryUnloaded"); + } + } + + /** + * Fire breakpoint created event + * + * @param newInfo the new information + * @param cause the cause of the creation + */ + @Internal + public void doBreakpointCreated(GdbBreakpointInfo newInfo, GdbCause cause) { + addKnownBreakpoint(newInfo, false); + event(() -> listenersEvent.fire.breakpointCreated(newInfo, cause), "breakpointCreated"); + } + + /** + * Handler for "=breakpoint-created" events + * + * @param evt the event + * @param v nothing + */ + protected void processBreakpointCreated(GdbBreakpointCreatedEvent evt, Void v) { + doBreakpointCreated(evt.getBreakpointInfo(), evt.getCause()); + } + + /** + * Fire breakpoint modified event + * + * @param newInfo the new information + * @param cause the cause of the modification + */ + @Internal + public void doBreakpointModified(GdbBreakpointInfo newInfo, GdbCause cause) { + GdbBreakpointInfo oldInfo = addKnownBreakpoint(newInfo, true); + event(() -> listenersEvent.fire.breakpointModified(newInfo, oldInfo, cause), + "breakpointModified"); + } + + /** + * Handler for "=breakpoint-modified" events + * + * @param evt the event + * @param v nothing + */ + protected void processBreakpointModified(GdbBreakpointModifiedEvent evt, Void v) { + doBreakpointModified(evt.getBreakpointInfo(), evt.getCause()); + } + + /** + * Fire breakpoint deleted event + * + * @param number the deleted breakpoint number + * @param cause the cause of the deletion + */ + @Internal + public void doBreakpointDeleted(long number, GdbCause cause) { + GdbBreakpointInfo oldInfo = removeKnownBreakpoint(number); + if (oldInfo == null) { + return; + } + event(() -> listenersEvent.fire.breakpointDeleted(oldInfo, cause), "breakpointDeleted"); + } + + protected void doBreakpointModifiedSameLocations(GdbBreakpointInfo newInfo, + GdbBreakpointInfo oldInfo, GdbCause cause) { + if (Objects.equals(newInfo, oldInfo)) { + return; + } + event(() -> listenersEvent.fire.breakpointModified(newInfo, oldInfo, cause), + "breakpointModified"); + } + + @Internal + public void doBreakpointDisabled(long number, GdbCause cause) { + GdbBreakpointInfo oldInfo = getKnownBreakpoint(number); + if (oldInfo == null) { + return; + } + GdbBreakpointInfo newInfo = oldInfo.withEnabled(false); + oldInfo = oldInfo.withEnabled(true); + doBreakpointModifiedSameLocations(newInfo, oldInfo, cause); + } + + @Internal + public void doBreakpointEnabled(long number, GdbCause cause) { + GdbBreakpointInfo oldInfo = getKnownBreakpoint(number); + if (oldInfo == null) { + return; + } + GdbBreakpointInfo newInfo = oldInfo.withEnabled(true); + oldInfo = oldInfo.withEnabled(false); + doBreakpointModifiedSameLocations(newInfo, oldInfo, cause); + } + + /** + * Handler for "=breakpoint-deleted" events + * + * @param evt the event + * @param v nothing + */ + protected void processBreakpointDeleted(GdbBreakpointDeletedEvent evt, Void v) { + doBreakpointDeleted(evt.getNumber(), evt.getCause()); + } + + /** + * Handler for "=memory-changed" events + * + * @param evt the event + * @param v nothing + */ + protected void processMemoryChanged(GdbMemoryChangedEvent evt, Void v) { + int iid = evt.getInferiorId(); + GdbInferior inf = getInferior(iid); + event(() -> listenersEvent.fire.memoryChanged(inf, evt.getAddress(), evt.getLength(), + evt.getCause()), "memoryChanged"); + } + + /** + * Check that a command completion event was claimed + * + * Except under certain error conditions, GDB should never issue a command completed event that + * is not associated with a command. A command implementation in the manager must claim the + * completion event. This is an assertion to ensure no implementation forgets to do that. + * + * @param evt the event + */ + protected void checkClaimed(GdbEvent evt) { + if (evt.getCause() == Causes.UNCLAIMED) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + AbstractGdbCompletedCommandEvent completed = (AbstractGdbCompletedCommandEvent) evt; + String msg = completed.assumeMsg(); + if (msg != null) { + if (evt instanceof GdbCommandErrorEvent) { + Msg.error(this, msg); + } + else { + Msg.info(this, msg); + throw new AssertionError("Command completion left unclaimed!"); + } + } + } + } + } + + /** + * Handler for "^done" + * + * @param evt the event + * @param v nothing + */ + protected void processCommandDone(GdbCommandDoneEvent evt, Void v) { + checkClaimed(evt); + } + + /** + * Handler for "^running" + * + * @param evt the event + * @param v nothing + */ + protected void processCommandRunning(GdbCommandRunningEvent evt, Void v) { + checkClaimed(evt); + Msg.debug(this, "Target is running"); + } + + /** + * Handler for "^connected" + * + * @param evt the event + * @param v nothing + */ + protected void processCommandConnected(GdbCommandConnectedEvent evt, Void v) { + checkClaimed(evt); + Msg.debug(this, "Connected to target"); + } + + /** + * Handler for "^exit" + * + * @param evt the event + * @param v nothing + */ + protected void processCommandExit(GdbCommandExitEvent evt, Void v) { + checkClaimed(evt); + Msg.debug(this, "GDB is exiting...."); + } + + /** + * Handler for "^error" + * + * @param evt the event + * @param v nothing + */ + protected void processCommandError(GdbCommandErrorEvent evt, Void v) { + checkClaimed(evt); + } + + /** + * Handler for "*running" + * + * @param evt the event + * @param v nothing + */ + protected void processRunning(GdbRunningEvent evt, Void v) { + String threadId = evt.assumeThreadId(); + if (threadId == null) { + threadId = "all"; + } + if ("all".equals(threadId)) { + GdbInferiorImpl cur = curInferior; + event(() -> { + listenersEvent.fire.inferiorStateChanged(cur, cur.getKnownThreads().values(), + evt.newState(), null, evt.getCause(), evt.getReason()); + }, "inferiorState-running"); + for (GdbThreadImpl thread : curInferior.getKnownThreadsImpl().values()) { + thread.setState(evt.newState(), evt.getCause(), evt.getReason()); + } + } + else { + int id = Integer.parseUnsignedInt(threadId); + GdbThreadImpl thread = threads.get(id); + event(() -> { + listenersEvent.fire.inferiorStateChanged(thread.getInferior(), + List.of(thread), evt.newState(), null, evt.getCause(), evt.getReason()); + }, "inferiorState-running"); + thread.setState(evt.newState(), evt.getCause(), evt.getReason()); + } + } + + /** + * Handler for "*stopped" + * + * @param evt the event + * @param v nothing + */ + protected void processStopped(GdbStoppedEvent evt, Void v) { + String stoppedThreadsStr = evt.assumeStoppedThreads(); + Collection stoppedThreads; + if (null == stoppedThreadsStr || "all".equals(stoppedThreadsStr)) { + stoppedThreads = threads.values(); + } + else { + stoppedThreads = new LinkedHashSet<>(); + for (String stopped : stoppedThreadsStr.split(",")) { + stoppedThreads.add(threads.get(Integer.parseInt(stopped))); + } + } + + Integer tid = evt.getThreadId(); + GdbThreadImpl evtThread = tid == null ? null : threads.get(tid); + Map> byInf = new LinkedHashMap<>(); + for (GdbThreadImpl thread : stoppedThreads) { + thread.setState(evt.newState(), evt.getCause(), evt.getReason()); + byInf.computeIfAbsent(thread.getInferior(), i -> new LinkedHashSet<>()).add(thread); + } + for (Map.Entry> ent : byInf.entrySet()) { + event(() -> { + listenersEvent.fire.inferiorStateChanged(ent.getKey(), ent.getValue(), + evt.newState(), evtThread, evt.getCause(), evt.getReason()); + }, "inferiorState-stopped"); + } + if (evtThread != null) { + GdbStackFrameImpl frame = evt.getFrame(evtThread); + event(() -> listenersEvent.fire.threadSelected(evtThread, frame, evt.getCause()), + "inferiorState-stopped"); + } + } + + // Link lazily to Jython + private static class JythonConsole { + /** + * Launch a Jython interpreter + * + * The interpreter the variable "{@code mgr}" bound to the manager. This method does not + * return until the user exits the interpreter. + * + * @param manager the manager + */ + static void interact(GdbManagerImpl manager) { + PyDictionary dict = new PyDictionary(); + dict.put("mgr", manager); + try (InteractiveConsole jyConsole = new InteractiveConsole(dict);) { + jyConsole.interact(); + } + catch (Throwable e) { + e.printStackTrace(); + } + } + } + + /** + * An interface for taking lines of input + */ + public interface LineReader { + String readLine(String prompt) throws IOException; + } + + /** + * An implementation of {@link LineReader} that does not use GPL code + */ + public static class BufferedReaderLineReader implements LineReader { + private BufferedReader reader; + + BufferedReaderLineReader() { + this.reader = new BufferedReader(new InputStreamReader(System.in)); + } + + @Override + public String readLine(String prompt) throws IOException { + System.out.print(prompt); + return reader.readLine(); + } + } + + @Override + public void consoleLoop() throws IOException { + checkStarted(); + Signal sigInterrupt = new Signal("INT"); + SignalHandler oldHandler = Signal.handle(sigInterrupt, (sig) -> { + try { + sendInterruptNow(); + } + catch (IOException e) { + e.printStackTrace(); + } + }); + try { + /* + * prompt.addChangeListener((p, v) -> { if (p) { System.out.print(PROMPT_GDB + " "); } + * }); + */ + LineReader reader = new BufferedReaderLineReader(); + //LineReader reader = new GnuReadlineLineReader(); + // System.out.print(PROMPT_GDB + " "); + while (isAlive()) { + String cmd = reader.readLine(PROMPT_GDB + " "); + if (cmd == null) { + System.out.println("quit"); + return; + } + if (">>>".equals(cmd.trim())) { + try { + JythonConsole.interact(this); + } + catch (NoClassDefFoundError e) { + Msg.error(this, "Jython is not in the classpath"); + } + catch (Throwable e) { + e.printStackTrace(); + } + } + else { + console(cmd).exceptionally((e) -> { + Throwable realExc = AsyncUtils.unwrapThrowable(e); + if (realExc instanceof GdbCommandError) { + return null; // Gdb will have already printed it + } + e.printStackTrace(); + //System.out.print(PROMPT_GDB + " "); + return null; + }); + } + } + } + finally { + Signal.handle(sigInterrupt, oldHandler); + } + } + + @Override + public void sendInterruptNow() throws IOException { + checkStarted(); + Msg.info(this, "Interrupting"); + if (hasCli()) { + OutputStream os = cliPty.getMaster().getOutputStream(); + os.write(3); + os.flush(); + } + OutputStream os = mi2Pty.getMaster().getOutputStream(); + os.write(3); + os.flush(); + } + + @Override + public synchronized GdbState getState() { + return state.get(); + } + + @Override + public synchronized CompletableFuture waitForState(GdbState forState) { + checkStarted(); + return state.waitValue(forState); + } + + @Override + public CompletableFuture waitForPrompt() { + return mi2Prompt.waitValue(true); + } + + @Override + public CompletableFuture claimStopped() { + return execute(new GdbClaimStopped(this)); + } + + @Override + public CompletableFuture addInferior() { + return execute(new GdbAddInferiorCommand(this)); + } + + @Override + public CompletableFuture removeInferior(GdbInferior inferior) { + return execute(new GdbRemoveInferiorCommand(this, inferior.getId())); + } + + /** + * Select the given inferior + * + * This issues a command to GDB to change its focus. It is not just a manager concept. + * + * @param inferior the inferior to select + * @return a future that completes when GDB has executed the command + */ + CompletableFuture selectInferior(GdbInferior inferior) { + return execute(new GdbInferiorSelectCommand(this, inferior.getId())); + } + + @Override + public CompletableFuture console(String command) { + return execute(new GdbConsoleExecCommand(this, null, null, command, + GdbConsoleExecCommand.Output.CONSOLE)).thenApply(e -> null); + } + + @Override + public CompletableFuture consoleCapture(String command) { + return execute(new GdbConsoleExecCommand(this, null, null, command, + GdbConsoleExecCommand.Output.CAPTURE)); + } + + @Override + public CompletableFuture interrupt() { + AtomicInteger retryCount = new AtomicInteger(); + return loop(TypeSpec.VOID, loop -> { + GdbCommand interrupt = new GdbInterruptCommand(this); + execute(interrupt).thenApply(e -> (Throwable) null) + .exceptionally(e -> e) + .handle(loop::consume); + }, TypeSpec.cls(Throwable.class), (exc, loop) -> { + Msg.debug(this, "Executed an interrupt"); + if (exc == null) { + loop.exit(); + } + else if (state.get() == GdbState.STOPPED) { + // Not the cleanest, but as long as we're stopped, why not call it good? + loop.exit(); + } + else if (retryCount.getAndAdd(1) >= INTERRUPT_MAX_RETRIES) { + loop.exit(exc); + } + else { + Msg.error(this, "Error executing interrupt: " + exc); + timer.mark().after(INTERRUPT_RETRY_PERIOD_MILLIS).handle(loop::repeat); + } + }); + } + + @Override + public CompletableFuture> listInferiors() { + return execute(new GdbListInferiorsCommand(this)); + } + + @Override + public CompletableFuture> listAvailableProcesses() { + return execute(new GdbListAvailableProcessesCommand(this)); + } + + @Override + public CompletableFuture infoOs(String type) { + return execute(new GdbInfoOsCommand(this, type)); + } + + @Override + public String getMi2PtyName() { + return mi2Pty.getSlave().getFile().getAbsolutePath(); + } + + public boolean hasCli() { + return cliWriter != null; + } + + public Interpreter getRunningInterpreter() { + return runningInterpreter; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbMemoryMapping.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbMemoryMapping.java new file mode 100644 index 0000000000..0e6ab709d5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbMemoryMapping.java @@ -0,0 +1,58 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.math.BigInteger; +import java.util.Objects; + +public class GdbMemoryMapping { + private final BigInteger start; + private final BigInteger end; + private final BigInteger size; + private final BigInteger offset; + private final String objfile; + + public GdbMemoryMapping(BigInteger start, BigInteger end, BigInteger size, BigInteger offset, + String objfile) { + this.start = start; + this.end = end; + this.size = size; + this.offset = offset; + this.objfile = objfile; + + assert Objects.equals(start.add(size), end); + } + + public BigInteger getStart() { + return start; + } + + public BigInteger getEnd() { + return end; + } + + public BigInteger getSize() { + return size; + } + + public BigInteger getOffset() { + return offset; + } + + public String getObjfile() { + return objfile; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbMinimalSymbol.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbMinimalSymbol.java new file mode 100644 index 0000000000..b95a2a8846 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbMinimalSymbol.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +public class GdbMinimalSymbol { + protected final long index; + protected final String type; + protected final String name; + protected final long address; + + public GdbMinimalSymbol(long index, String type, String name, long address) { + this.index = index; + this.type = type; + this.name = name; + this.address = address; + } + + public long getIndex() { + return index; + } + + public String getType() { + // TODO: Interpret these types + // Observed: t, T, D, S + return type; + } + + public String getName() { + return name; + } + + public long getAddress() { + return address; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbModuleImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbModuleImpl.java new file mode 100644 index 0000000000..9603f6c43a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbModuleImpl.java @@ -0,0 +1,177 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import agent.gdb.manager.GdbModule; +import agent.gdb.manager.GdbModuleSection; +import ghidra.async.AsyncLazyValue; +import ghidra.async.AsyncUtils; +import ghidra.util.MathUtilities; +import ghidra.util.Msg; + +public class GdbModuleImpl implements GdbModule { + protected static final Pattern OBJECT_FILE_LINE_PATTERN = + Pattern.compile("\\s*Object file: (?.*)"); + protected static final Pattern OBJECT_SECTION_LINE_PATTERN = Pattern.compile( + "\\s*" + // + "0x(?[0-9A-Fa-f]+)\\s*->\\s*" + // + "0x(?[0-9A-Fa-f]+)\\s+at\\s+" + // + "0x(?[0-9A-Fa-f]+)\\s*:\\s*" + // + "(?\\S+)\\s+" + // + "(?.*)"); + protected static final Pattern MSYMBOL_LINE_PATTERN = Pattern.compile( + "\\s*" + // + "\\[\\s*(?\\d+)\\]\\s+" + // + "(?\\S+)\\s+" + // + "0x(?[0-9A-Fa-f]+)\\s+" + // + "(?\\S+)\\s+" + // + ".*"); + + protected final GdbInferiorImpl inferior; + protected final String name; + protected Long base = null; + protected Long max = null; + + protected final Map sections = new LinkedHashMap<>(); + protected final Map unmodifiableSections = + Collections.unmodifiableMap(sections); + protected final AsyncLazyValue loadSections = new AsyncLazyValue<>(this::doLoadSections); + + protected final AsyncLazyValue> minimalSymbols = + new AsyncLazyValue<>(this::doGetMinimalSymbols); + + public GdbModuleImpl(GdbInferiorImpl inferior, String name) { + this.inferior = inferior; + this.name = name; + } + + @Override + public String getName() { + return name; + } + + protected CompletableFuture doLoadSections() { + return inferior.loadSections().thenCompose(__ -> { + if (!loadSections.isDone()) { + /** + * The inferior's load sections should have provided the value out of band before it + * is completed from the request that got us invoked. If it didn't it's because the + * response to the load in progress did not include this module. We should only have + * to force it at most once more. + */ + inferior.loadSections.forget(); + return inferior.loadSections(); + } + return AsyncUtils.NIL; + }).thenAccept(__ -> { + assert loadSections.isDone(); + }); + } + + @Override + public CompletableFuture computeBase() { + return loadSections.request().thenApply(__ -> base); + } + + @Override + public CompletableFuture computeMax() { + return loadSections.request().thenApply(__ -> max); + } + + @Override + public Long getKnownBase() { + return base; + } + + @Override + public Long getKnownMax() { + return max; + } + + @Override + public CompletableFuture> listSections() { + return loadSections.request().thenApply(__ -> unmodifiableSections); + } + + @Override + public Map getKnownSections() { + return unmodifiableSections; + } + + protected CompletableFuture> doGetMinimalSymbols() { + // TODO: Apparently, this is using internal GDB-debugging commands.... + // TODO: Also make methods for "full" symbols (DWARF?) + String cmd = "maintenance print msymbols -objfile " + name; + return inferior.consoleCapture(cmd).thenApply(out -> { + Map result = new LinkedHashMap<>(); + for (String line : out.split("\n")) { + Matcher mat = MSYMBOL_LINE_PATTERN.matcher(line); + if (!mat.matches()) { + continue; + } + long index = Long.parseLong(mat.group("idx")); + String type = Objects.requireNonNull(mat.group("type")); + long address = Long.parseLong(mat.group("addr"), 16); + String symName = Objects.requireNonNull(mat.group("name")); + result.put(symName, new GdbMinimalSymbol(index, type, symName, address)); + } + return Collections.unmodifiableMap(result); + }); + } + + @Override + public CompletableFuture> listMinimalSymbols() { + // TODO: getKnownMinimalSymbols method, too? + return minimalSymbols.request(); + } + + protected void processSectionLine(String line) { + Matcher matcher = OBJECT_SECTION_LINE_PATTERN.matcher(line); + if (matcher.matches()) { + try { + long vmaStart = Long.parseLong(matcher.group("vmaS"), 16); + long vmaEnd = Long.parseLong(matcher.group("vmaE"), 16); + long offset = Long.parseLong(matcher.group("offset"), 16); + + String sectionName = matcher.group("name"); + List attrs = new ArrayList<>(); + for (String a : matcher.group("attrs").split("\\s+")) { + if (a.length() != 0) { + attrs.add(a); + } + } + if (attrs.contains("ALLOC")) { + long b = vmaStart - offset; + base = base == null ? b : MathUtilities.unsignedMin(base, b); + max = max == null ? b : MathUtilities.unsignedMax(max, vmaEnd); + } + if (sections.put(sectionName, + new GdbModuleSectionImpl(sectionName, vmaStart, vmaEnd, offset, + attrs)) != null) { + Msg.warn(this, "Duplicate section name: " + line); + } + } + catch (NumberFormatException e) { + Msg.error(this, "Invalid number in section entry: " + line); + } + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbModuleSectionImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbModuleSectionImpl.java new file mode 100644 index 0000000000..7b283369c1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbModuleSectionImpl.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.util.List; + +import agent.gdb.manager.GdbModuleSection; + +public class GdbModuleSectionImpl implements GdbModuleSection { + protected final String name; + protected final long vmaStart; + protected final long vmaEnd; + protected final long fileOffset; + protected final List attrs; + + public GdbModuleSectionImpl(String name, long vmaStart, long vmaEnd, long fileOffset, + List attrs) { + this.name = name; + this.vmaStart = vmaStart; + this.vmaEnd = vmaEnd; + this.fileOffset = fileOffset; + this.attrs = List.copyOf(attrs); + } + + @Override + public String getName() { + return name; + } + + @Override + public long getVmaStart() { + return vmaStart; + } + + @Override + public long getVmaEnd() { + return vmaEnd; + } + + @Override + public long getFileOffset() { + return fileOffset; + } + + @Override + public List getAttributes() { + return attrs; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbPendingCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbPendingCommand.java new file mode 100644 index 0000000000..4a28bd830c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbPendingCommand.java @@ -0,0 +1,232 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.GdbCause; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandErrorEvent; +import agent.gdb.manager.impl.cmd.GdbCommandError; + +/** + * A command queued on the GDB manager + * + * A {@link GdbCommand} is queued by wrapping it in a {@link GdbPendingCommand} and submitting it to + * the manager implementation's executor. This object also keep track of claimed/stolen events and + * provides convenience methods for sifting through them. + * + * @param the type "returned" by the command + */ +public class GdbPendingCommand extends CompletableFuture implements GdbCause { + private final GdbCommand cmd; + private final Set> evts = new LinkedHashSet<>(); + + /** + * Wrap a command for execution + * + * @param cmd the command + */ + public GdbPendingCommand(GdbCommand cmd) { + this.cmd = cmd; + } + + /** + * Get the command being executed + * + * @return + */ + public GdbCommand getCommand() { + return cmd; + } + + /** + * Finish the execution of this command + */ + public void finish() { + //Msg.trace(this, "Finishing " + cmd); + try { + T result = cmd.complete(this); + complete(result); + } + catch (Throwable e) { + completeExceptionally(e); + } + } + + /** + * Handle an event + * + * This gives the command implementation the first chance to claim or steal an event + * + * @param evt the event + * @return true if the command is ready to be completed + */ + public boolean handle(GdbEvent evt) { + return cmd.handle(evt, this); + } + + /** + * Claim an event + * + * This stores the event for later retrieval and processing. + * + * @param evt the event + */ + public void claim(GdbEvent evt) { + evt.claim(this); + evts.add(evt); + } + + /** + * Steal an event + * + * This stores the event for later retrieval and processing. + * + * @param evt the event + */ + public void steal(GdbEvent evt) { + claim(evt); + evt.steal(); + } + + /** + * Assume a single event was claimed/stolen, and get that event as the given type + * + * @param cls the type of the event + * @return the event cast to the type + * @throws IllegalStateException if more than one event was claimed/stolen + * @throws ClassCastException if the event cannot be cast to the given type + */ + public > E castSingleEvent(Class cls) { + if (evts.size() != 1) { + throw new IllegalStateException("Command did not claim exactly one event"); + } + return cls.cast(evts.iterator().next()); + } + + /** + * Get the first claimed/stolen event of a given type + * + * @param the type of the event + * @param cls the class of the event + * @return the event cast to the type, or null + */ + public > E getFirstOf(Class cls) { + for (GdbEvent evt : evts) { + if (cls.isAssignableFrom(evt.getClass())) { + return cls.cast(evt); + } + } + return null; + } + + /** + * Find the first claimed/stolen event of a given type + * + * @param the type of the event + * @param cls the class of the event + * @return the event cast to the type + * @throws IllegalStateException if no event of the given type was claimed/stolen + */ + public > E findFirstOf(Class cls) { + E first = getFirstOf(cls); + if (first != null) { + return first; + } + throw new IllegalStateException("Command did not claim any " + cls); + } + + /** + * Check if any event of a given type has been claimed + * + * @param cls the class of the event + * @return true if at least one is claimed, false otherwise + */ + public boolean hasAny(Class> cls) { + return getFirstOf(cls) != null; + } + + /** + * Find all events claimed/stolen of a given type + * + * @param cls the type of the events + * @return the list of events cast to the type + */ + public > List findAllOf(Class cls) { + List found = new ArrayList<>(); + for (GdbEvent evt : evts) { + if (cls.isAssignableFrom(evt.getClass())) { + found.add(cls.cast(evt)); + } + } + return found; + } + + /** + * Assume exactly one event of the given type was claimed/stolen, and get that event + * + * @param cls the type of the event + * @return the event cast to the type + * @throws IllegalStateException if more than one event matches + */ + public > E findSingleOf(Class cls) { + List found = findAllOf(cls); + if (found.size() != 1) { + throw new IllegalStateException( + "Command did not claim exactly one " + cls + ". Have " + evts); + } + return found.get(0); + } + + /** + * Check that the command completed with one of the given results + * + * {@link GdbCommandErrorEvent} need not be listed. This method will handle it as a special case + * already. To avoid the special treatment, list it explicitly. + * + * @param classes the completion type to accept + * @return the completion event, cast to the greatest common subclass + */ + @SafeVarargs + public final E checkCompletion( + Class... classes) { + AbstractGdbCompletedCommandEvent completion = + findSingleOf(AbstractGdbCompletedCommandEvent.class); + // Allow query for exact class to override error interpretation + for (Class cls : classes) { + if (cls == completion.getClass()) { + return cls.cast(completion); + } + } + if (completion instanceof GdbCommandErrorEvent) { + throw new GdbCommandError(completion.getInfo(), cmd); + } + for (Class cls : classes) { + if (cls.isAssignableFrom(completion.getClass())) { + return cls.cast(completion); + } + } + throw new IllegalStateException( + "Command completed with " + completion + ", not any of " + Arrays.asList(classes)); + } + + @Override + public String toString() { + return super.toString() + "(" + cmd + ")"; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbStackFrameImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbStackFrameImpl.java new file mode 100644 index 0000000000..b10f6bc9df --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbStackFrameImpl.java @@ -0,0 +1,112 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.math.BigInteger; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.GdbRegister; +import agent.gdb.manager.GdbStackFrame; +import agent.gdb.manager.impl.cmd.*; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import agent.gdb.manager.parsing.GdbParsingUtils; + +public class GdbStackFrameImpl implements GdbStackFrame { + protected final GdbManagerImpl manager; + protected final GdbThreadImpl thread; + protected final int level; + protected final BigInteger addr; + protected final String func; + + public static GdbStackFrameImpl fromFieldList(GdbThreadImpl thread, GdbMiFieldList fields) { + String lvlString = fields.getString("level"); + // NOTE: When given with breakpoint-hit, level is absent. Safe to assume innermost. + int level = lvlString == null ? 0 : Integer.parseInt(lvlString); + BigInteger addr = GdbParsingUtils.parsePrefixedHexBig(fields.getString("addr")); + String func = fields.getString("func"); + return new GdbStackFrameImpl(thread, level, addr, func); + } + + public GdbStackFrameImpl(GdbThreadImpl thread, int level, BigInteger addr, String func) { + this.manager = thread.manager; + this.thread = thread; + this.level = level; + this.addr = addr; + this.func = func; + } + + @Override + public String toString() { + return ""; + } + + @Override + public int getLevel() { + return level; + } + + @Override + public BigInteger getAddress() { + return addr; + } + + @Override + public String getFunction() { + return func; + } + + @Override + public GdbThreadImpl getThread() { + return thread; + } + + @Override + public CompletableFuture select() { + return manager.execute(new GdbThreadSelectCommand(manager, thread.getId(), level)); + } + + @Override + public CompletableFuture evaluate(String expression) { + return manager.execute(new GdbEvaluateCommand(manager, thread.getId(), level, expression)); + } + + @Override + public CompletableFuture> readRegisters(Set regs) { + return thread.getInferior().syncEndianness().thenCompose(__ -> { + return manager.execute(new GdbReadRegistersCommand(manager, thread, level, regs)); + }); + } + + @Override + public CompletableFuture writeRegisters(Map regVals) { + return manager.execute(new GdbWriteRegistersCommand(manager, thread, level, regVals)); + } + + @Override + public CompletableFuture console(String command) { + return manager.execute(new GdbConsoleExecCommand(manager, thread.getId(), level, command, + GdbConsoleExecCommand.Output.CONSOLE)).thenApply(e -> null); + } + + @Override + public CompletableFuture consoleCapture(String command) { + return manager.execute(new GdbConsoleExecCommand(manager, thread.getId(), level, command, + GdbConsoleExecCommand.Output.CAPTURE)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadImpl.java new file mode 100644 index 0000000000..9cbe888480 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadImpl.java @@ -0,0 +1,274 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +import com.google.common.collect.RangeSet; + +import agent.gdb.manager.*; +import agent.gdb.manager.GdbManager.ExecSuffix; +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import agent.gdb.manager.breakpoint.GdbBreakpointType; +import agent.gdb.manager.impl.cmd.*; +import agent.gdb.manager.parsing.GdbCValueParser; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; +import agent.gdb.manager.reason.GdbReason; +import ghidra.async.AsyncLazyValue; +import ghidra.async.AsyncReference; + +/** + * The implementation of {@link GdbThread} + */ +public class GdbThreadImpl implements GdbThread { + private static class CauseReasonPair { + private final GdbCause cause; + private final GdbReason reason; + + CauseReasonPair(GdbCause cause, GdbReason reason) { + this.cause = cause; + this.reason = reason; + } + } + + protected final GdbManagerImpl manager; + private final int id; + private final GdbInferiorImpl inferior; + + private final AsyncReference state = + new AsyncReference<>(GdbState.STOPPED); + + private final AsyncLazyValue registers = + new AsyncLazyValue<>(this::doListRegisters); + + /** + * Construct a new thread + * + * @param manager the manager creating the thread + * @param inferior the inferior to which the thread belongs + * @param id the GDB-assigned thread ID + */ + public GdbThreadImpl(GdbManagerImpl manager, GdbInferiorImpl inferior, int id) { + this.manager = manager; + this.id = id; + this.inferior = inferior; + } + + @Override + public GdbInferiorImpl getInferior() { + return inferior; + } + + /** + * Add this thread to the inferior and manager + */ + public void add() { + this.inferior.addThread(this); + this.manager.addThread(this); + state.addChangeListener((oldState, newState, pair) -> { + manager.event(() -> manager.listenersEvent.fire.threadStateChanged(this, newState, + pair.cause, pair.reason), "threadState"); + }); + } + + /** + * Remove this thread from the inferior and manager + */ + public void remove() { + this.inferior.removeThread(id); + this.manager.removeThread(id); + } + + @Override + public int getId() { + return id; + } + + @Override + public String toString() { + return ""; + } + + @Override + public GdbState getState() { + return state.get(); + } + + /** + * Set the state of this thread + * + * @param state the new state + * @param cause the cause for the change + * @param reason the reason (usually a stop reason) for the change + * @return true if the state actually changed + */ + protected boolean setState(GdbState state, GdbCause cause, GdbReason reason) { + return this.state.set(state, new CauseReasonPair(cause, reason)); + } + + protected CompletableFuture execute(AbstractGdbCommand cmd) { + switch (cmd.getInterpreter()) { + case CLI: + return select().thenCompose(v -> manager.execute(cmd)); + case MI2: + return manager.execute(cmd); + default: + throw new AssertionError(); + } + } + + @Override + public CompletableFuture select() { + // Bypass the select-me-first logic + return manager.execute(new GdbThreadSelectCommand(manager, id, null)); + } + + @Override + public CompletableFuture evaluate(String expression) { + return execute(new GdbEvaluateCommand(manager, id, null, expression)); + } + + @Override + public CompletableFuture setVar(String varName, String val) { + return execute(new GdbSetVarCommand(manager, id, varName, val)); + } + + @Override + // TODO: Is this per thread or per inferior? + public CompletableFuture listRegisters() { + return registers.request(); + } + + private CompletableFuture doListRegisters() { + Map namesByNumber = new TreeMap<>(); + return execute(new GdbListRegisterNamesCommand(manager, id)).thenCompose(names -> { + for (int i = 0; i < names.size(); i++) { + String n = names.get(i); + if ("".equals(n)) { + continue; + } + namesByNumber.put(i, n); + } + List sizeofNames = namesByNumber.values() + .stream() + .map(n -> "sizeof($" + n + ")") + .collect(Collectors.toList()); + String expr = "{" + StringUtils.join(sizeofNames, ",") + "}"; + return evaluate(expr); + }).thenApply(value -> { + List regs = new ArrayList<>(); + List sizes; + try { + sizes = GdbCValueParser.parseArray(value).expectInts(); + } + catch (GdbParseError e) { + throw new AssertionError("GDB did not give an integer array!"); + } + if (sizes.size() != namesByNumber.size()) { + throw new AssertionError("GDB did not give all the sizes!"); + } + Iterator sit = sizes.iterator(); + Iterator> eit = namesByNumber.entrySet().iterator(); + while (sit.hasNext()) { + int size = sit.next(); + Map.Entry ent = eit.next(); + regs.add(new GdbRegister(ent.getValue(), ent.getKey(), size)); + } + return new GdbRegisterSet(regs); + }); + } + + @Override + public CompletableFuture> listStackFrames() { + return execute(new GdbStackListFramesCommand(manager, this)); + } + + @Override + public CompletableFuture> readRegisters(Set regs) { + // TODO: Re-sync not ideal, but it works + return inferior.syncEndianness().thenCompose(__ -> { + return execute(new GdbReadRegistersCommand(manager, this, null, regs)); + }); + } + + @Override + public CompletableFuture writeRegisters(Map regVals) { + return execute(new GdbWriteRegistersCommand(manager, this, null, regVals)); + } + + @Override + public CompletableFuture> readMemory(long addr, ByteBuffer buf, int len) { + return execute(new GdbReadMemoryCommand(manager, id, addr, buf, len)); + } + + @Override + public CompletableFuture writeMemory(long addr, ByteBuffer buf, int len) { + return execute(new GdbWriteMemoryCommand(manager, id, addr, buf, len)); + } + + @Override + public CompletableFuture insertBreakpoint(String loc, + GdbBreakpointType type) { + return execute(new GdbInsertBreakpointCommand(manager, id, loc, type)); + } + + @Override + public CompletableFuture console(String command) { + return execute(new GdbConsoleExecCommand(manager, id, null, command, + GdbConsoleExecCommand.Output.CONSOLE)).thenApply(e -> null); + } + + @Override + public CompletableFuture consoleCapture(String command) { + return execute(new GdbConsoleExecCommand(manager, id, null, command, + GdbConsoleExecCommand.Output.CAPTURE)); + } + + @Override + public CompletableFuture cont() { + return execute(new GdbContinueCommand(manager, id)); + } + + @Override + public CompletableFuture step(ExecSuffix suffix) { + return execute(new GdbStepCommand(manager, id, suffix)); + } + + @Override + public CompletableFuture kill() { + return execute(new GdbKillCommand(manager, id)); + } + + @Override + public CompletableFuture detach() { + return execute(new GdbDetachCommand(manager, inferior, id)); + } + + public void dispose(String reason) { + state.dispose(reason); + } + + public CompletableFuture getInfo() { + return manager.getThreadInfo(id); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadInfo.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadInfo.java new file mode 100644 index 0000000000..f1096a8884 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadInfo.java @@ -0,0 +1,165 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.math.BigInteger; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import ghidra.util.Msg; + +/** + * Information about a GDB thread + */ +public class GdbThreadInfo { + + protected static final Pattern TARGET_ID_LINE_PATTERN0 = Pattern.compile("\\s*" + // + "Thread 0x(?[0-9,A-F,a-f]+)\\s+" + // + "\\(LWP (?[0-9]+)\\)\\s*"); + protected static final Pattern TARGET_ID_LINE_PATTERN1 = Pattern.compile("\\s*" + // + "process (?[0-9]+)\\s*"); + + /** + * Process a parsed GDB thread information + * + * @param info the parsed information block + * @return the processed GDB thread information + */ + public static GdbThreadInfo parseInfo(GdbMiFieldList info) { + String id = info.getString("id"); + String targetId = info.getString("target-id"); + String name = info.getString("name"); + String state = info.getString("state"); + String core = info.getString("core"); + Collection finfo = info.get("frame"); + List frames = new ArrayList<>(); + for (Object object : finfo) { + if (object instanceof GdbMiFieldList) { + frames.add(GdbFrameInfo.parseInfo((GdbMiFieldList) object)); + } + } + return new GdbThreadInfo(id, targetId, name, state, core, frames); + } + + private final String id; + private final String targetId; + private final String name; + private final String state; + private final String core; + private final List frames; + private BigInteger addr; + private Integer tid; + + /** + * Construct GDB thread information + * + * @param id the GDB-assigned id + * @param targetId the system id + * @param name the inferior name + * @param state current thread state + * @param core the active core + * @param frames thread stack + */ + GdbThreadInfo(String id, String targetId, String name, String state, String core, + List frames) { + this.id = id; + this.targetId = targetId; + this.name = name; + this.frames = frames; + this.state = state; + this.core = core; + Matcher mappingMatcher = TARGET_ID_LINE_PATTERN0.matcher(targetId); + if (mappingMatcher.matches()) { + try { + this.addr = new BigInteger(mappingMatcher.group("addr"), 16); + this.tid = Integer.parseInt(mappingMatcher.group("tid")); + return; + } + catch (NumberFormatException e) { + Msg.error(this, "Could not parse target id: " + targetId, e); + } + } + mappingMatcher = TARGET_ID_LINE_PATTERN1.matcher(targetId); + if (mappingMatcher.matches()) { + try { + this.tid = Integer.parseInt(mappingMatcher.group("tid")); + } + catch (NumberFormatException e) { + Msg.error(this, "Could not parse target id: " + targetId, e); + } + } + } + + @Override + public int hashCode() { + return Objects.hash(getId(), getTargetId()); + } + + @Override + public String toString() { + return ""; + } + + @Override + public boolean equals(Object obj) { + if (!((obj instanceof GdbThreadInfo))) { + return false; + } + GdbThreadInfo that = (GdbThreadInfo) obj; + if (this.getId() != that.getId()) { + return false; + } + if (this.getTargetId() != that.getTargetId()) { + return false; + } + return true; + } + + public String getId() { + return id; + } + + public String getTargetId() { + return targetId; + } + + public String getInferiorName() { + return name; + } + + public String getState() { + return state; + } + + public String getCore() { + return core; + } + + public List getFrames() { + return frames; + } + + public BigInteger getAddr() { + return addr; + } + + public Integer getTid() { + return tid; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommand.java new file mode 100644 index 0000000000..52657bb67a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommand.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbState; +import agent.gdb.manager.impl.GdbCommand; +import agent.gdb.manager.impl.GdbManagerImpl; +import agent.gdb.manager.impl.GdbManagerImpl.Interpreter; + +/** + * A base class for interacting with specific GDB commands + * + * @param the type of object "returned" by the command + */ +public abstract class AbstractGdbCommand implements GdbCommand { + protected final GdbManagerImpl manager; + + /** + * Construct a new command to be executed by the given manager + * + * @param manager the manager to execute the command + */ + protected AbstractGdbCommand(GdbManagerImpl manager) { + this.manager = manager; + } + + @Override + public boolean validInState(GdbState state) { + //return state == GdbState.STOPPED; + return true; // With dual interpreters, shouldn't have to worry. + } + + @Override + public String toString() { + return ""; + } + + /** + * {@inheritDoc} + * + * Selects mi2 by default. Check {@link GdbManagerImpl#hasCli()} before selecting the + * command-line (console) interface. + */ + @Override + public Interpreter getInterpreter() { + return Interpreter.MI2; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadAndFrameId.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadAndFrameId.java new file mode 100644 index 0000000000..4269904086 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadAndFrameId.java @@ -0,0 +1,76 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.impl.GdbManagerImpl; + +/** + * An extension to {@link AbstractGdbCommandWithThreadId} that also passes the "{@code --frame}" + * argument + * + * @param the type of object "returned" by the command + */ +public abstract class AbstractGdbCommandWithThreadAndFrameId + extends AbstractGdbCommandWithThreadId { + protected final Integer frameId; + + /** + * Construct a frame-specific command to be executed by the given manager + * + * @param manager the manager to execute the command + * @param threadId the ID of the thread to pass to "{@code --thread}" + * @param frameId the ID "level" of the frame to pass to "{@code --frame}" + */ + protected AbstractGdbCommandWithThreadAndFrameId(GdbManagerImpl manager, Integer threadId, + Integer frameId) { + super(manager, threadId); + this.frameId = frameId; + } + + protected String makeFramePart() { + return frameId == null ? "" : " --frame " + frameId; + } + + /** + * Get the level of the frame to pass to "{@code --frame}" + * + * @return the frame level + */ + public Integer getFrameId() { + return frameId; + } + + @Override + protected String encode(String threadPart) { + return encode(threadPart, makeFramePart()); + } + + /** + * Encode the command in GDB/MI, given the pre-constructed thread and frame parts + * + * The given parts are preceded by spaces, but not followed by spaces. For example, a command + * can be properly constructed as: + * + *
+	 * return "-some-command" + threadPart + threadPart + " some-argument";
+	 * 
+ * + * @param threadPart the "{@code --thread [ID]}" part + * @param framePart the "{@code --frame [level]}" part + * @return the encoded command + */ + protected abstract String encode(String threadPart, String framePart); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadId.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadId.java new file mode 100644 index 0000000000..1b8caa53c1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadId.java @@ -0,0 +1,71 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.impl.GdbManagerImpl; + +/** + * An extension to {@link AbstractGdbCommand} that passes the "{@code --thread}" argument + * + * @param the type of object "returned" by the command + */ +public abstract class AbstractGdbCommandWithThreadId extends AbstractGdbCommand { + protected final Integer threadId; + + /** + * Construct a thread-specific command to be executed by the given manager + * + * @param manager the manager to execute the command + * @param threadId the ID of the thread to pass to "{@code --thread}" + */ + protected AbstractGdbCommandWithThreadId(GdbManagerImpl manager, Integer threadId) { + super(manager); + this.threadId = threadId; + } + + protected String makeThreadPart() { + return threadId == null ? "" : " --thread " + threadId; + } + + @Override + public String encode() { + return encode(makeThreadPart()); + } + + /** + * Get the ID of the thread to pass to "{@code --thread}" + * + * @return the thread ID + */ + public Integer getThreadId() { + return threadId; + } + + /** + * Encode the command in GDB/MI, given the pre-constructed thread argument part + * + * The given thread argument is preceded by a space, but not followed by one. For example, a + * command can be properly constructed as: + * + *
+	 * return "-some-command" + threadPart + " some-argument";
+	 * 
+ * + * @param threadPart the "{@code --thread [ID]}" part + * @return the encoded command + */ + protected abstract String encode(String threadPart); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbAddInferiorCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbAddInferiorCommand.java new file mode 100644 index 0000000000..4f384350ac --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbAddInferiorCommand.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.GdbManager; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbManager#addInferior()} + */ +public class GdbAddInferiorCommand extends AbstractGdbCommand { + public GdbAddInferiorCommand(GdbManagerImpl manager) { + super(manager); + } + + @Override + public String encode() { + return "-add-inferior"; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public GdbInferior complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + int iid = done.assumeInferior(); + return manager.getInferior(iid); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbAttachCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbAttachCommand.java new file mode 100644 index 0000000000..0194439c66 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbAttachCommand.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.util.LinkedHashSet; +import java.util.Set; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.GdbThread; +import agent.gdb.manager.evt.*; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#attach(long)} + */ +public class GdbAttachCommand extends AbstractGdbCommand> { + + private final long pid; + + public GdbAttachCommand(GdbManagerImpl manager, long pid) { + super(manager); + this.pid = pid; + } + + @Override + public String encode() { + return "-target-attach " + pid; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + else if (evt instanceof GdbThreadCreatedEvent) { + pending.claim(evt); + } + return false; + } + + @Override + public Set complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + + Set threads = new LinkedHashSet<>(); + for (GdbThreadCreatedEvent created : pending.findAllOf(GdbThreadCreatedEvent.class)) { + int tid = created.getThreadId(); + threads.add(manager.getThread(tid)); + } + return threads; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbClaimStopped.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbClaimStopped.java new file mode 100644 index 0000000000..e9aa2a0d6b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbClaimStopped.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbManager; +import agent.gdb.manager.GdbState; +import agent.gdb.manager.evt.GdbStoppedEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbManager#claimStopped()} + */ +public class GdbClaimStopped extends AbstractGdbCommand { + public GdbClaimStopped(GdbManagerImpl manager) { + super(manager); + } + + @Override + public boolean validInState(GdbState state) { + return state == GdbState.RUNNING; + } + + @Override + public String encode() { + // This is not really a command, it just claims *stopped + // The executor will also wait for (gdb). Good. + return null; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof GdbStoppedEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.findSingleOf(GdbStoppedEvent.class); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbCommandError.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbCommandError.java new file mode 100644 index 0000000000..155e6ba618 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbCommandError.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.impl.GdbCommand; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +/** + * Exception generated by the default "^error" handler + */ +public class GdbCommandError extends RuntimeException { + private final GdbMiFieldList info; + + /** + * Construct an error with the given details + * + * @param info the detail information + */ + public GdbCommandError(GdbMiFieldList info, GdbCommand cmd) { + super(cmd + " caused '" + info + "'"); + this.info = info; + } + + /** + * Construct an error with the given message + * + * @param message the message + */ + public GdbCommandError(String message) { + super(message); + this.info = null; + } + + /** + * Get the details, if present + * + * @return the details, or null + */ + public GdbMiFieldList getInfo() { + return info; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbConsoleExecCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbConsoleExecCommand.java new file mode 100644 index 0000000000..c740c3f603 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbConsoleExecCommand.java @@ -0,0 +1,77 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import org.apache.commons.text.StringEscapeUtils; + +import agent.gdb.manager.GdbManager; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbConsoleOutputEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbManager#console(String)} and similar + */ +public class GdbConsoleExecCommand extends AbstractGdbCommandWithThreadAndFrameId { + public enum Output { + CONSOLE, CAPTURE; + } + + private String command; + private Output to; + + public GdbConsoleExecCommand(GdbManagerImpl manager, Integer threadId, Integer frameId, + String command, Output to) { + super(manager, threadId, frameId); + this.command = command; + this.to = to; + } + + @Override + public String encode(String threadPart, String framePart) { + return "-interpreter-exec" + threadPart + framePart + " console \"" + + StringEscapeUtils.escapeJava(command) + "\""; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + else if (evt instanceof GdbConsoleOutputEvent && to == Output.CAPTURE) { + GdbConsoleOutputEvent out = (GdbConsoleOutputEvent) evt; + if (out.getInterpreter() == getInterpreter()) { + pending.steal(evt); + } + } + return false; + } + + @Override + public String complete(GdbPendingCommand pending) { + pending.checkCompletion(AbstractGdbCompletedCommandEvent.class); + + if (to == Output.CONSOLE) { + return null; + } + StringBuilder builder = new StringBuilder(); + for (GdbConsoleOutputEvent out : pending.findAllOf(GdbConsoleOutputEvent.class)) { + builder.append(out.getOutput()); + } + return builder.toString(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbContinueCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbContinueCommand.java new file mode 100644 index 0000000000..b5033d65eb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbContinueCommand.java @@ -0,0 +1,95 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.evt.*; +import agent.gdb.manager.impl.*; +import agent.gdb.manager.impl.GdbManagerImpl.Interpreter; +import ghidra.util.Msg; + +/** + * Implementation of {@link GdbInferior#cont()} + */ +public class GdbContinueCommand extends AbstractGdbCommandWithThreadId { + public GdbContinueCommand(GdbManagerImpl manager, Integer threadId) { + super(manager, threadId); + } + + @Override + public String encode(String threadPart) { + switch (getInterpreter()) { + case CLI: + return "continue"; + case MI2: + return "-exec-continue" + threadPart; + default: + throw new AssertionError(); + } + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + if (!pending.hasAny(AbstractGdbCompletedCommandEvent.class)) { + pending.claim(evt); + } + return evt instanceof GdbCommandErrorEvent || pending.hasAny(GdbRunningEvent.class); + } + else if (evt instanceof GdbRunningEvent) { + // Event happens no matter which interpreter received the command + pending.claim(evt); + return pending.hasAny(AbstractGdbCompletedCommandEvent.class); + } + else if (evt instanceof GdbConsoleOutputEvent) { + Msg.debug(this, "EXAMINING: " + evt); + if (pending.hasAny(GdbCommandRunningEvent.class)) { + // Only attempt to process/claim the first line after our command + return false; + } + GdbConsoleOutputEvent out = (GdbConsoleOutputEvent) evt; + if (out.getOutput().trim().equals("continue")) { + // Echoed back my command + return false; + } + pending.claim(evt); + if (out.getOutput().trim().startsWith("Continuing") && + !pending.hasAny(GdbCommandRunningEvent.class)) { + pending.claim(new GdbCommandRunningEvent()); + return pending.hasAny(GdbRunningEvent.class); + } + else { + pending.claim(GdbCommandErrorEvent.fromMessage(out.getOutput())); + return true; + } + } + return false; + } + + @Override + public Interpreter getInterpreter() { + if (manager.hasCli()) { + return Interpreter.CLI; + } + return Interpreter.MI2; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandRunningEvent.class); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDeleteBreakpointsCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDeleteBreakpointsCommand.java new file mode 100644 index 0000000000..041be2fdba --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDeleteBreakpointsCommand.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import org.apache.commons.lang3.StringUtils; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#deleteBreakpoint(long)} + */ +public class GdbDeleteBreakpointsCommand extends AbstractGdbCommand { + + private final long[] numbers; + + public GdbDeleteBreakpointsCommand(GdbManagerImpl manager, long... numbers) { + super(manager); + this.numbers = numbers; + } + + @Override + public String encode() { + return "-break-delete " + StringUtils.join(numbers, ' '); + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + // GDB does not give notification for breakpoints removed by GDB/MI commands + for (long number : numbers) { + manager.doBreakpointDeleted(number, pending); + } + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDetachCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDetachCommand.java new file mode 100644 index 0000000000..77cc059491 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDetachCommand.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.util.ArrayList; +import java.util.Collection; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.evt.*; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#detach()} + */ +public class GdbDetachCommand extends AbstractGdbCommandWithThreadId { + private final GdbInferiorImpl inferior; + + public GdbDetachCommand(GdbManagerImpl manager, GdbInferiorImpl inferior, Integer threadId) { + super(manager, threadId); + this.inferior = inferior; + } + + @Override + public String encode(String threadPart) { + return "-target-detach" + threadPart; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + else if (evt instanceof GdbThreadExitedEvent) { + pending.claim(evt); + } + else if (evt instanceof GdbThreadGroupExitedEvent) { + pending.claim(evt); + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + + // GDB does not notify thread exit when detaching. At least not via GDB/MI + // TODO: Copy on write instead? + Collection threads = + new ArrayList<>(inferior.getKnownThreadsImpl().values()); + for (GdbThreadImpl t : threads) { + manager.fireThreadExited(t.getId(), inferior, pending); + t.remove(); + } + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDisableBreakpointsCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDisableBreakpointsCommand.java new file mode 100644 index 0000000000..ff5b9dae82 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbDisableBreakpointsCommand.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import org.apache.commons.lang3.StringUtils; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#deleteBreakpoint(long)} + */ +public class GdbDisableBreakpointsCommand extends AbstractGdbCommand { + + private final long[] numbers; + + public GdbDisableBreakpointsCommand(GdbManagerImpl manager, long... numbers) { + super(manager); + this.numbers = numbers; + } + + @Override + public String encode() { + return "-break-disable " + StringUtils.join(numbers, ' '); + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + // GDB does not give notification for breakpoints added by GDB/MI commands + for (long number : numbers) { + manager.doBreakpointDisabled(number, pending); + } + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEnableBreakpointsCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEnableBreakpointsCommand.java new file mode 100644 index 0000000000..5edcc9a03d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEnableBreakpointsCommand.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import org.apache.commons.lang3.StringUtils; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#deleteBreakpoint(long)} + */ +public class GdbEnableBreakpointsCommand extends AbstractGdbCommand { + + private final long[] numbers; + + public GdbEnableBreakpointsCommand(GdbManagerImpl manager, long... numbers) { + super(manager); + this.numbers = numbers; + } + + @Override + public String encode() { + return "-break-enable " + StringUtils.join(numbers, ' '); + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + // GDB does not give notification for breakpoints added by GDB/MI commands + for (long number : numbers) { + manager.doBreakpointEnabled(number, pending); + } + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEvaluateCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEvaluateCommand.java new file mode 100644 index 0000000000..5a8dcf8428 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEvaluateCommand.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import org.apache.commons.text.StringEscapeUtils; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#evaluate(String)} + */ +public class GdbEvaluateCommand extends AbstractGdbCommandWithThreadAndFrameId { + private final String expression; + + public GdbEvaluateCommand(GdbManagerImpl manager, Integer threadId, Integer frameId, + String expression) { + super(manager, threadId, frameId); + this.expression = expression; + } + + @Override + protected String encode(String threadPart, String framePart) { + return "-data-evaluate-expression" + threadPart + framePart + " \"" + + StringEscapeUtils.escapeJava(expression) + '"'; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public String complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + return done.assumeValue(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbFileExecAndSymbolsCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbFileExecAndSymbolsCommand.java new file mode 100644 index 0000000000..f57c2e49fe --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbFileExecAndSymbolsCommand.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#fileExecAndSymbols(String)} + */ +public class GdbFileExecAndSymbolsCommand extends AbstractGdbCommand { + + private final String file; + + public GdbFileExecAndSymbolsCommand(GdbManagerImpl manager, String file) { + super(manager); + this.file = file; + } + + @Override + public String encode() { + return "-file-exec-and-symbols " + file; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbGetThreadInfoCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbGetThreadInfoCommand.java new file mode 100644 index 0000000000..b41eb937ab --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbGetThreadInfoCommand.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.util.List; + +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +/** + * Implementation of {@link GdbManagerImpl#getThreadInfo()} + */ +public class GdbGetThreadInfoCommand extends AbstractGdbCommandWithThreadId { + + public GdbGetThreadInfoCommand(GdbManagerImpl manager, Integer threadId) { + super(manager, threadId); + } + + @Override + protected String encode(String threadPart) { + return "-thread-info" + threadPart; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public GdbThreadInfo complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + List infoList = done.assumeThreadInfoList(); + return GdbThreadInfo.parseInfo(infoList.get(0)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbGetVarCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbGetVarCommand.java new file mode 100644 index 0000000000..50c3a9c809 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbGetVarCommand.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#getVar(String)} + */ +public class GdbGetVarCommand extends AbstractGdbCommand { + private final String varName; + + public GdbGetVarCommand(GdbManagerImpl manager, String varName) { + super(manager); + this.varName = varName; + } + + @Override + public String encode() { + return "-gdb-show " + varName; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public String complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + return done.maybeValue(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInferiorSelectCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInferiorSelectCommand.java new file mode 100644 index 0000000000..15df8f6f02 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInferiorSelectCommand.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.evt.*; +import agent.gdb.manager.impl.*; + +public class GdbInferiorSelectCommand extends AbstractGdbCommand { + private final int id; + + public GdbInferiorSelectCommand(GdbManagerImpl manager, int id) { + super(manager); + this.id = id; + } + + @Override + public String encode() { + /** + * There does not appear to be a real -inferior-select command + * + * Also, if the requested inferior is already current, don't do anything. + */ + if (manager.currentInferior().getId() == id) { + return "-interpreter-exec console echo"; + } + return "-interpreter-exec console \"inferior " + id + "\""; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + if (evt instanceof GdbThreadSelectedEvent) { + pending.claim(evt); + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInfoOsCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInfoOsCommand.java new file mode 100644 index 0000000000..a307cf5e3c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInfoOsCommand.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbManager; +import agent.gdb.manager.GdbTable; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbManager#infoOs(String)} + */ +public class GdbInfoOsCommand extends AbstractGdbCommand { + private final String type; + + public GdbInfoOsCommand(GdbManagerImpl manager, String type) { + super(manager); + this.type = type; + } + + @Override + public String encode() { + return "-info-os " + type; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public GdbTable complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + return new GdbTable(done.assumeOSDataTable(), "item"); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInsertBreakpointCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInsertBreakpointCommand.java new file mode 100644 index 0000000000..6f88e521ef --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInsertBreakpointCommand.java @@ -0,0 +1,98 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import org.apache.commons.text.StringEscapeUtils; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import agent.gdb.manager.breakpoint.GdbBreakpointType; +import agent.gdb.manager.evt.*; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#insertBreakpoint(String)} + */ +public class GdbInsertBreakpointCommand extends AbstractGdbCommandWithThreadId { + private final String loc; + private final GdbBreakpointType type; + + public GdbInsertBreakpointCommand(GdbManagerImpl manager, Integer threadId, String loc, + GdbBreakpointType type) { + super(manager, threadId); + this.loc = loc; + this.type = type; + } + + @Override + protected String makeThreadPart() { + return threadId == null ? "" : " -p " + threadId; + } + + protected static String esc(String param) { + return '"' + StringEscapeUtils.escapeJava(param) + '"'; + } + + @Override + protected String encode(String threadPart) { + String cmd; + switch (type) { + case BREAKPOINT: + return "-break-insert" + threadPart + " " + esc(loc); + case HW_BREAKPOINT: + return "-break-insert -h" + threadPart + " " + esc(loc); + case DPRINTF: + return "-dprintf-insert" + threadPart + " " + esc(loc); + case HW_WATCHPOINT: + cmd = "watch -l " + loc; // escaping here causes GDB to treat as literal??? + return "-interpreter-exec" + threadPart + " console " + esc(cmd); + case READ_WATCHPOINT: + cmd = "rwatch -l " + loc; + return "-interpreter-exec" + threadPart + " console " + esc(cmd); + case ACCESS_WATCHPOINT: + cmd = "awatch -l " + loc; + return "-interpreter-exec" + threadPart + " console " + esc(cmd); + default: + throw new IllegalArgumentException("type=" + type); + } + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + else if (evt instanceof GdbBreakpointCreatedEvent) { + pending.claim(evt); + } + return false; + } + + @Override + public GdbBreakpointInfo complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + if (type.isWatchpoint()) { + GdbBreakpointCreatedEvent evt = pending.findSingleOf(GdbBreakpointCreatedEvent.class); + return evt.getBreakpointInfo(); + } + GdbBreakpointInfo bkpt = + GdbBreakpointInfo.parse(done.getInfo(), manager.currentInferior().getId()); + // GDB does not give notification for breakpoints added by GDB/MI commands + manager.doBreakpointCreated(bkpt, pending); + return bkpt; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInterruptCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInterruptCommand.java new file mode 100644 index 0000000000..650fa991fa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbInterruptCommand.java @@ -0,0 +1,81 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbManager; +import agent.gdb.manager.GdbState; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbStoppedEvent; +import agent.gdb.manager.impl.*; +import agent.gdb.manager.impl.GdbManagerImpl.Interpreter; + +/** + * Implementation of {@link GdbManager#interrupt()} when we start GDB + */ +public class GdbInterruptCommand extends AbstractGdbCommand { + public GdbInterruptCommand(GdbManagerImpl manager) { + super(manager); + } + + @Override + public boolean validInState(GdbState state) { + //return state == GdbState.RUNNING; + return true; + } + + @Override + public String encode() { + Interpreter i = getInterpreter(); + if (i == manager.getRunningInterpreter()) { + return "\u0003"; + } + switch (i) { + case CLI: + return "interrupt"; + case MI2: + return "-exec-interrupt"; + default: + throw new AssertionError(); + } + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + else if (evt instanceof GdbStoppedEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.findSingleOf(GdbStoppedEvent.class); + return null; + } + + @Override + public Interpreter getInterpreter() { + if (manager.hasCli() && manager.getRunningInterpreter() == Interpreter.MI2) { + return Interpreter.CLI; + } + return Interpreter.MI2; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbKillCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbKillCommand.java new file mode 100644 index 0000000000..3e0eb51a34 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbKillCommand.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.evt.*; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#kill()} + */ +public class GdbKillCommand extends AbstractGdbCommandWithThreadId { + public GdbKillCommand(GdbManagerImpl manager, Integer threadId) { + super(manager, threadId); + } + + @Override + public String encode(String threadPart) { + // NOTE: In later versions, the GDB/MI command is "-exec-abort" + return "-interpreter-exec" + threadPart + " console kill"; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + else if (evt instanceof GdbThreadExitedEvent) { + pending.claim(evt); + } + else if (evt instanceof GdbThreadGroupExitedEvent) { + pending.claim(evt); + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListAvailableProcessesCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListAvailableProcessesCommand.java new file mode 100644 index 0000000000..7dc4371684 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListAvailableProcessesCommand.java @@ -0,0 +1,51 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.util.List; + +import agent.gdb.manager.GdbProcessThreadGroup; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +public class GdbListAvailableProcessesCommand + extends AbstractGdbCommand> { + public GdbListAvailableProcessesCommand(GdbManagerImpl manager) { + super(manager); + } + + @Override + public String encode() { + return "-list-thread-groups --available"; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public List complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + // There is no GDB event for changes in available processes, so not tracking for now. + return done.assumeProcessGroups(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListBreakpointsCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListBreakpointsCommand.java new file mode 100644 index 0000000000..d08a7696cb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListBreakpointsCommand.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.util.*; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import agent.gdb.manager.breakpoint.GdbBreakpointLocation; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +/** + * Implementation of {@link GdbInferior#listBreakpoints()} + */ +public class GdbListBreakpointsCommand + extends AbstractGdbCommandWithThreadId> { + + public GdbListBreakpointsCommand(GdbManagerImpl manager, Integer threadId) { + super(manager, threadId); + } + + @Override + protected String encode(String threadPart) { + return "-break-list" + threadPart; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Map complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + // Do not use GdbTable here, since col_names provide good IDs + // Also, there are some bonus fields that don't appear in cells.... + GdbMiFieldList tbl = done.assumeBreakpointTable(); + GdbMiFieldList body = tbl.getFieldList("body"); + Map list = new LinkedHashMap<>(); + List allLocs = GdbBreakpointInfo.parseLocations(body); + for (Object bkpt : body.get("bkpt")) { + GdbBreakpointInfo info = + GdbBreakpointInfo.parseBkpt((GdbMiFieldList) bkpt, allLocs, null); + list.put(info.getNumber(), info); + } + return list; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListInferiorsCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListInferiorsCommand.java new file mode 100644 index 0000000000..abd7fcd1cd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListInferiorsCommand.java @@ -0,0 +1,77 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.util.*; + +import agent.gdb.manager.*; +import agent.gdb.manager.GdbCause.Causes; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; +import ghidra.util.Msg; + +/** + * Implementation of {@link GdbManager#listInferiors()} + */ +public class GdbListInferiorsCommand extends AbstractGdbCommand> { + public GdbListInferiorsCommand(GdbManagerImpl manager) { + super(manager); + } + + @Override + public String encode() { + return "-list-thread-groups"; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Map complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + List groups = done.assumeInferiorGroups(); + Set ids = new HashSet<>(); + Map allInferiors = manager.getKnownInferiorsInternal(); + Set curIds = allInferiors.keySet(); + for (GdbInferiorThreadGroup g : groups) { + ids.add(g.getInferiorId()); + GdbInferiorImpl exists = allInferiors.get(g.getInferiorId()); + if (exists != null) { + exists.update(g); + continue; // Otherwise, we're in sync + } + // Need to create the inferior as if we received =thread-group-created + Msg.warn(this, "Resync: Was missing group: i" + g); + manager.addInferior(new GdbInferiorImpl(manager, g), Causes.UNCLAIMED); + } + for (int id : new ArrayList<>(curIds)) { + if (ids.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to remove the inferior as if we received =thread-group-removed + Msg.warn(this, "Resync: Had extra group: i" + id); + manager.removeInferior(id, Causes.UNCLAIMED); + } + return manager.getKnownInferiors(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListRegisterNamesCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListRegisterNamesCommand.java new file mode 100644 index 0000000000..a4bc81d746 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListRegisterNamesCommand.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.util.List; + +import agent.gdb.manager.GdbThread; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbThread#listRegisters()} + */ +public class GdbListRegisterNamesCommand extends AbstractGdbCommandWithThreadId> { + + public GdbListRegisterNamesCommand(GdbManagerImpl manager, Integer threadId) { + super(manager, threadId); + } + + @Override + protected String encode(String threadPart) { + return "-data-list-register-names" + threadPart; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public List complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + return done.assumeRegisterNameList(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListThreadsCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListThreadsCommand.java new file mode 100644 index 0000000000..8630c1f672 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbListThreadsCommand.java @@ -0,0 +1,74 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.util.*; + +import agent.gdb.manager.GdbThread; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; +import ghidra.util.Msg; + +public class GdbListThreadsCommand extends AbstractGdbCommand> { + protected final GdbInferiorImpl inferior; + + public GdbListThreadsCommand(GdbManagerImpl manager, GdbInferiorImpl inferior) { + super(manager); + this.inferior = inferior; + } + + @Override + public String encode() { + return "-list-thread-groups i" + inferior.getId(); + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Map complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + List ids = done.assumeThreadIds(); + Map infThreads = inferior.getKnownThreads(); + Set cur = infThreads.keySet(); + for (int id : ids) { + if (cur.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to create the thread as if we receive =thread-created + Msg.warn(this, "Resync: Was missing thread: " + id); + new GdbThreadImpl(manager, inferior, id).add(); + } + for (int id : new ArrayList<>(cur)) { + if (ids.contains(id)) { + continue; // Do nothing, we're in sync + } + // Need to remove the thread as if we received =thread-exited + Msg.warn(this, "Resync: Had extra thread: " + id); + inferior.removeThread(id); + manager.removeThread(id); + } + return infThreads; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadMemoryCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadMemoryCommand.java new file mode 100644 index 0000000000..b0e1eb377f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadMemoryCommand.java @@ -0,0 +1,91 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.nio.ByteBuffer; +import java.util.List; + +import com.google.common.collect.*; + +import agent.gdb.manager.GdbThread; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; +import agent.gdb.manager.parsing.GdbParsingUtils; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; + +/** + * Implementation of {@link GdbThread#readMemory(long, ByteBuffer, int)} + */ +public class GdbReadMemoryCommand extends AbstractGdbCommandWithThreadId> { + + private final long addr; + private final ByteBuffer buf; + private final int len; + + public GdbReadMemoryCommand(GdbManagerImpl manager, Integer threadId, long addr, ByteBuffer buf, + int len) { + super(manager, threadId); + this.addr = addr; + this.buf = buf; + this.len = len; + } + + @Override + protected String encode(String threadPart) { + return "-data-read-memory-bytes" + threadPart + " 0x" + Long.toHexString(addr) + " " + len; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public RangeSet complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + List rangeList = done.assumeMemoryContentsList(); + RangeSet rangeSet = TreeRangeSet.create(); + int pos = buf.position(); + int max = pos; + for (GdbMiFieldList r : rangeList) { + long begin = GdbParsingUtils.parsePrefixedHex(r.getString("begin")); + long offset = GdbParsingUtils.parsePrefixedHex(r.getString("offset")); + long end = GdbParsingUtils.parsePrefixedHex(r.getString("end")); + byte[] contents = NumericUtilities.convertStringToBytes(r.getString("contents")); + + long start = begin + offset; + int length = (int) (end - start); + if (length != contents.length) { + Msg.warn(this, "Received fewer bytes than indicated by bounds"); + } + int diff = (int) (start - addr); + int newPos = pos + diff; + max = Math.max(max, newPos + length); + buf.position(newPos); + buf.put(contents); + rangeSet.add(Range.closedOpen(start, end)); + } + buf.position(max); + return rangeSet; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadRegistersCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadRegistersCommand.java new file mode 100644 index 0000000000..182d892010 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadRegistersCommand.java @@ -0,0 +1,144 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.math.BigInteger; +import java.nio.ByteOrder; +import java.util.*; +import java.util.stream.Collectors; + +import agent.gdb.manager.GdbRegister; +import agent.gdb.manager.GdbStackFrameOperations; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; +import agent.gdb.manager.parsing.GdbCValueParser; +import agent.gdb.manager.parsing.GdbCValueParser.*; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; +import ghidra.util.Msg; + +/** + * Implementation of {@link GdbStackFrameOperations#readRegisters(Set)} + */ +public class GdbReadRegistersCommand + extends AbstractGdbCommandWithThreadAndFrameId> { + protected static final Set BYTE_ARRAY_KEYS = Set.of( + "v1_int8", "v2_int8", "v4_int8", "v8_int8", "v16_int8", "v32_int8", "v64_int8", // Observed on i386:x86-64 + "u8" // Observed on armv7 + ); + private final Set regs; + private final GdbThreadImpl thread; + + public GdbReadRegistersCommand(GdbManagerImpl manager, GdbThreadImpl thread, Integer frameId, + Set regs) { + super(manager, thread.getId(), frameId); + this.thread = thread; + this.regs = regs; + } + + @Override + protected String encode(String threadPart, String framePart) { + if (regs.isEmpty()) { + return "-interpreter-exec console echo"; + } + StringBuilder b = new StringBuilder(); + b.append("-data-list-register-values"); + b.append(threadPart); + b.append(framePart); + b.append(" x"); + for (GdbRegister r : regs) { + b.append(" "); + b.append(r.getNumber()); + } + return b.toString(); + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + protected BigInteger parseAndFindInteger(String val, int byteCount) throws GdbParseError { + ByteOrder endianness = thread.getInferior().getEndianness(); + GdbCValue value = GdbCValueParser.parseValue(val); + if (value instanceof GdbIntValue) { + GdbIntValue iv = (GdbIntValue) value; + return iv.getValue(); + } + if (value instanceof GdbCompositeValue) { + GdbCompositeValue cv = (GdbCompositeValue) value; + for (GdbCValue v : cv.values()) { + if (v instanceof GdbIntValue) { + GdbIntValue iv = (GdbIntValue) v; + return iv.getValue(); + } + } + for (Map.Entry ent : cv.entrySet()) { + if (BYTE_ARRAY_KEYS.contains(ent.getKey())) { + GdbCValue int8v = ent.getValue(); + if (!(int8v instanceof GdbArrayValue)) { + throw new AssertionError("Expected an array of ints for " + ent); + } + GdbArrayValue int8a = (GdbArrayValue) int8v; + List int8l = int8a.expectInts(); + byte[] ordered = new byte[int8l.size()]; + int i = endianness == ByteOrder.BIG_ENDIAN ? 0 : ordered.length - 1; + int step = endianness == ByteOrder.BIG_ENDIAN ? 1 : -1; + for (int b : int8l) { + ordered[i] = (byte) b; + i += step; + } + return new BigInteger(1, ordered); + } + } + } + throw new AssertionError("Expected an int, or a union containing an int. Got " + val); + } + + @Override + public Map complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + if (regs.isEmpty()) { + return Collections.emptyMap(); + } + Map regsByNumber = + regs.stream().collect(Collectors.toMap(GdbRegister::getNumber, r -> r)); + List valueList = done.assumeRegisterValueList(); + Map result = new LinkedHashMap<>(); + for (GdbMiFieldList fields : valueList) { + int number = Integer.parseInt(fields.getString("number")); + String value = fields.getString("value"); + GdbRegister r = regsByNumber.get(number); + if (r == null) { + Msg.error(this, "GDB gave value for non-requested register: " + number); + continue; + } + try { + result.put(r, parseAndFindInteger(value, r.getSize())); + } + catch (GdbParseError | AssertionError e) { + Msg.warn(this, + "Could not figure register value for [" + number + "] = " + value, e); + } + } + return result; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbRemoveInferiorCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbRemoveInferiorCommand.java new file mode 100644 index 0000000000..d29ccc02ec --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbRemoveInferiorCommand.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.evt.*; +import agent.gdb.manager.impl.*; + +public class GdbRemoveInferiorCommand extends AbstractGdbCommand { + + private final int id; + + public GdbRemoveInferiorCommand(GdbManagerImpl manager, int id) { + super(manager); + this.id = id; + } + + @Override + public String encode() { + return "-remove-inferior i" + id; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + if (evt instanceof GdbThreadGroupRemovedEvent) { + pending.claim(evt); + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbRunCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbRunCommand.java new file mode 100644 index 0000000000..d37fda1cb1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbRunCommand.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.GdbThread; +import agent.gdb.manager.evt.*; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#run()} + */ +public class GdbRunCommand extends AbstractGdbCommand { + + public GdbRunCommand(GdbManagerImpl manager) { + super(manager); + } + + @Override + public String encode() { + return "-exec-run"; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof GdbCommandRunningEvent) { + pending.claim(evt); + return pending.hasAny(GdbRunningEvent.class); + } + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; // Not the expected Completed event + } + else if (evt instanceof GdbRunningEvent) { + pending.claim(evt); + return pending.hasAny(GdbCommandRunningEvent.class); + } + else if (evt instanceof GdbThreadCreatedEvent) { + /** + * TODO: Why do I care? Because I think as this stands, none of these get claimed, since + * the command will be completed by the time these events arrive. + */ + pending.claim(evt); + } + return false; + } + + @Override + public GdbThread complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandRunningEvent.class); + + // Just take the first thread. Others are considered clones. + GdbThreadCreatedEvent created = pending.findFirstOf(GdbThreadCreatedEvent.class); + int tid = created.getThreadId(); + return manager.getThread(tid); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbSetInferiorTtyCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbSetInferiorTtyCommand.java new file mode 100644 index 0000000000..835348d49b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbSetInferiorTtyCommand.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#setTty(String)} + */ +public class GdbSetInferiorTtyCommand extends AbstractGdbCommand { + private final String tty; + + public GdbSetInferiorTtyCommand(GdbManagerImpl manager, String tty) { + super(manager); + this.tty = tty; + } + + @Override + public String encode() { + return "-inferior-tty-set " + tty; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbSetVarCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbSetVarCommand.java new file mode 100644 index 0000000000..5c30766fbf --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbSetVarCommand.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbInferior#setVar(String, String)} + */ +public class GdbSetVarCommand extends AbstractGdbCommandWithThreadId { + private final String varName; + private final String val; + + public GdbSetVarCommand(GdbManagerImpl manager, Integer threadId, String varName, String val) { + super(manager, threadId); + this.varName = varName; + this.val = val; + } + + @Override + public String encode(String threadPart) { + return "-gdb-set" + threadPart + " " + varName + " " + val; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbStackListFramesCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbStackListFramesCommand.java new file mode 100644 index 0000000000..bab1a58bc2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbStackListFramesCommand.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.util.ArrayList; +import java.util.List; + +import agent.gdb.manager.GdbStackFrame; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +public class GdbStackListFramesCommand extends AbstractGdbCommandWithThreadId> { + protected final GdbThreadImpl thread; + + public GdbStackListFramesCommand(GdbManagerImpl manager, GdbThreadImpl thread) { + super(manager, thread.getId()); + this.thread = thread; + } + + @Override + protected String encode(String threadPart) { + return "-stack-list-frames" + threadPart; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public List complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + GdbMiFieldList stack = done.assumeStack(); + List result = new ArrayList<>(stack.size()); + for (Object obj : stack.get("frame")) { + GdbMiFieldList f = (GdbMiFieldList) obj; + GdbStackFrame frame = GdbStackFrameImpl.fromFieldList(thread, f); + result.add(frame); + } + return result; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbStepCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbStepCommand.java new file mode 100644 index 0000000000..a56c752c9d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbStepCommand.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.GdbManager.ExecSuffix; +import agent.gdb.manager.GdbThread; +import agent.gdb.manager.evt.*; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbThread#stepInstruction()} + */ +public class GdbStepCommand extends AbstractGdbCommandWithThreadId { + protected final ExecSuffix suffix; + + public GdbStepCommand(GdbManagerImpl manager, Integer threadId, ExecSuffix suffix) { + super(manager, threadId); + this.suffix = suffix; + } + + @Override + protected String encode(String threadPart) { + return "-exec-" + suffix + threadPart; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof GdbCommandRunningEvent) { + pending.claim(evt); + return pending.hasAny(GdbRunningEvent.class); + } + else if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; // Not the expected Completed event + } + else if (evt instanceof GdbRunningEvent) { + pending.claim(evt); + return pending.hasAny(GdbCommandRunningEvent.class); + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandRunningEvent.class); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbThreadSelectCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbThreadSelectCommand.java new file mode 100644 index 0000000000..e75d94a031 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbThreadSelectCommand.java @@ -0,0 +1,75 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import agent.gdb.manager.evt.*; +import agent.gdb.manager.impl.*; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +public class GdbThreadSelectCommand extends AbstractGdbCommandWithThreadAndFrameId { + /** + * Select the given thread and frame level + * + * To simply select a thread, you should use frame 0 as the default. + * + * @param manager the manager to execute the command + * @param threadId the desired thread Id + * @param frameId the desired frame level + */ + public GdbThreadSelectCommand(GdbManagerImpl manager, int threadId, Integer frameId) { + super(manager, threadId, frameId); + } + + @Override + public String encode(String threadPart, String framePart) { + /** + * Yes, it's a bit redundant to use {@code --thread} here, but this allows frame selection + * via {@code --frame} as well. Granted {@code -stack-select-frame} may be available, it + * doesn't appear to produce notifications, and so I've opted not to use it. + */ + return "-thread-select" + threadPart + framePart + " " + threadId; + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + else if (evt instanceof GdbThreadSelectedEvent) { + pending.claim(evt); + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + GdbCommandDoneEvent done = pending.checkCompletion(GdbCommandDoneEvent.class); + GdbThreadSelectedEvent already = pending.getFirstOf(GdbThreadSelectedEvent.class); + if (already != null) { + return null; + } + // Otherwise, we just changed frames within a thread. Fire the event ourselves. + GdbThreadImpl thread = manager.getThread(threadId); + GdbMiFieldList fields = done.getInfo().getFieldList("frame"); + if (fields == null) { // Uhhh... I guess we'll have to do without + return null; + } + GdbStackFrameImpl frame = GdbStackFrameImpl.fromFieldList(thread, fields); + manager.doThreadSelected(thread, frame, done.getCause()); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbWriteMemoryCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbWriteMemoryCommand.java new file mode 100644 index 0000000000..33644cb65d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbWriteMemoryCommand.java @@ -0,0 +1,75 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.nio.ByteBuffer; + +import agent.gdb.manager.GdbThread; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; + +/** + * Implementation of {@link GdbThread#writeMemory(long, ByteBuffer, int)} + */ +public class GdbWriteMemoryCommand extends AbstractGdbCommandWithThreadId { + + private final long addr; + private final ByteBuffer buf; + private final int len; + + public GdbWriteMemoryCommand(GdbManagerImpl manager, Integer threadId, long addr, + ByteBuffer buf, int len) { + super(manager, threadId); + this.addr = addr; + this.buf = buf.duplicate(); + this.len = len; + } + + @Override + protected String encode(String threadPart) { + ByteBuffer dup = buf.duplicate(); + StringBuilder b = new StringBuilder(); + b.append("-data-write-memory-bytes"); + b.append(threadPart); + b.append(" 0x"); + b.append(Long.toHexString(addr)); + b.append(" "); + for (int i = 0; i < len; i++) { + int n = dup.get(); + if (n < 0) { + n += 256; + } + b.append(String.format("%02x", n)); + } + return b.toString(); + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbWriteRegistersCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbWriteRegistersCommand.java new file mode 100644 index 0000000000..ddea0083cd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbWriteRegistersCommand.java @@ -0,0 +1,137 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl.cmd; + +import java.math.BigInteger; +import java.nio.ByteOrder; +import java.util.Map; + +import org.apache.commons.lang3.ArrayUtils; + +import agent.gdb.manager.GdbRegister; +import agent.gdb.manager.GdbStackFrameOperations; +import agent.gdb.manager.evt.AbstractGdbCompletedCommandEvent; +import agent.gdb.manager.evt.GdbCommandDoneEvent; +import agent.gdb.manager.impl.*; +import ghidra.dbg.util.ConversionUtils; + +/** + * Implementation of {@link GdbStackFrameOperations#writeRegisters(Map)} + */ +public class GdbWriteRegistersCommand extends AbstractGdbCommandWithThreadAndFrameId { + protected static final BigInteger UINT128_MAX = + BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE); + + private final GdbThreadImpl thread; + private final Map regVals; + + public GdbWriteRegistersCommand(GdbManagerImpl manager, GdbThreadImpl thread, Integer frameId, + Map regVals) { + super(manager, thread.getId(), frameId); + this.thread = thread; + + this.regVals = regVals; + } + + protected void encodeInt8Array(StringBuilder b, BigInteger value, int bytes) { + ByteOrder endianness = thread.getInferior().getEndianness(); + byte[] arr = ConversionUtils.bigIntegerToBytes(bytes, value); + if (endianness != ByteOrder.BIG_ENDIAN) { + ArrayUtils.reverse(arr); + } + boolean first = true; + for (int i = 0; i < bytes; i++) { + if (first) { + first = false; + } + else { + b.append(','); + } + int v = arr[i] & 0xff; + b.append(v); + } + } + + @Override + // Opting for console command, as -data-write-register-values is buggy + protected String encode(String threadPart, String framePart) { + StringBuilder b = new StringBuilder(); + b.append("-interpreter-exec"); + b.append(threadPart); + b.append(framePart); + b.append(" console \"set"); + boolean first = true; + for (Map.Entry ent : regVals.entrySet()) { + if (first) { + b.append(' '); + first = false; + } + else { + b.append(','); + } + b.append('$'); + GdbRegister reg = ent.getKey(); + b.append(reg.getName()); + + BigInteger value = ent.getValue(); + if (value.compareTo(UINT128_MAX) <= 0) { + b.append('='); + b.append(value.toString()); + } + else { + b.append(".v"); + b.append(reg.getSize()); + b.append("_int8={"); + encodeInt8Array(b, value, reg.getSize()); + b.append('}'); + } + } + b.append("\""); + return b.toString(); + } + + // This command can't seem to take larger than uint128 + // Attempting vector-ish stuff here may cause GDB to segfault + protected String encodeForMi(String threadPart, String framePart) { + StringBuilder b = new StringBuilder(); + b.append("-data-write-register-values"); + b.append(threadPart); + b.append(framePart); + b.append(" x"); + for (Map.Entry ent : regVals.entrySet()) { + b.append(" "); + b.append(ent.getKey().getNumber()); + b.append(" 0x"); + b.append(ent.getValue().toString(16)); + } + return b.toString(); + } + + @Override + public boolean handle(GdbEvent evt, GdbPendingCommand pending) { + if (evt instanceof AbstractGdbCompletedCommandEvent) { + pending.claim(evt); + return true; + } + return false; + } + + @Override + public Void complete(GdbPendingCommand pending) { + pending.checkCompletion(GdbCommandDoneEvent.class); + return null; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/parsing/GdbCValueParser.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/parsing/GdbCValueParser.java new file mode 100644 index 0000000000..5577b7c633 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/parsing/GdbCValueParser.java @@ -0,0 +1,320 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.parsing; + +import java.math.BigInteger; +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.apache.commons.collections4.list.AbstractListDecorator; +import org.apache.commons.collections4.map.AbstractMapDecorator; + +import agent.gdb.manager.parsing.GdbParsingUtils.AbstractGdbParser; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; +import utility.function.ExceptionalFunction; + +public class GdbCValueParser extends AbstractGdbParser { + public interface GdbCValue { + public GdbCValue EMPTY = new GdbCValue() { + @Override + public boolean isEmpty() { + return true; + } + }; + + boolean isEmpty(); + + default int expectInt() { + return ((GdbIntValue) this).val.intValueExact(); + } + + default long expectLong() { + return ((GdbIntValue) this).val.longValueExact(); + } + } + + public interface GdbCompositeValue extends GdbCValue, Map { + public GdbCompositeValue EMPTY = new DefaultGdbCompositeValue(Map.of()); + + public static class Builder { + private final Map map = new LinkedHashMap<>(); + + private Builder() { + } + + public Builder put(String name, GdbCValue value) { + if (map.containsKey(name)) { + throw new IllegalArgumentException("field " + name + " already present"); + } + map.put(name, value); + return this; + } + + public GdbCompositeValue build() { + return new DefaultGdbCompositeValue(Map.copyOf(map)); + } + } + + public static Builder builder() { + return new Builder(); + } + } + + public static class DefaultGdbCompositeValue extends AbstractMapDecorator + implements GdbCompositeValue { + private DefaultGdbCompositeValue(Map map) { + super(map); + } + } + + public interface GdbArrayValue extends GdbCValue, List { + public GdbArrayValue EMPTY = new DefaultGdbArrayValue(List.of()); + + public static class Builder { + private final List list = new ArrayList<>(); + + private Builder() { + } + + public Builder add(GdbCValue value) { + list.add(value); + return this; + } + + public GdbArrayValue build() { + return new DefaultGdbArrayValue(List.copyOf(list)); + } + } + + public static Builder builder() { + return new Builder(); + } + + public default List expectInts() { + return stream().map(v -> v.expectInt()).collect(Collectors.toList()); + } + + public default List expectLongs() { + return stream().map(v -> v.expectLong()).collect(Collectors.toList()); + } + } + + public static class DefaultGdbArrayValue extends AbstractListDecorator + implements GdbArrayValue { + private DefaultGdbArrayValue(List list) { + super(list); + } + } + + public static class GdbIntValue implements GdbCValue { + public static GdbIntValue valueOf(BigInteger val) { + // There shouldn't be a lot of these around to need to intern them + return new GdbIntValue(val); + } + + public static GdbIntValue valueOf(long val) { + return valueOf(BigInteger.valueOf(val)); + } + + @Override + public String toString() { + return "0x" + val.toString(16); + } + + @Override + public int hashCode() { + return val.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof GdbIntValue)) { + return false; + } + GdbIntValue that = (GdbIntValue) obj; + return Objects.equals(this.val, that.val); + } + + private final BigInteger val; + + private GdbIntValue(BigInteger val) { + this.val = val; + } + + public BigInteger getValue() { + return val; + } + + @Override + public boolean isEmpty() { + return false; + } + } + + protected static final Pattern COMMA = Pattern.compile(","); + protected static final Pattern LBRACE = Pattern.compile("\\{"); + protected static final Pattern RBRACE = Pattern.compile("\\}"); + protected static final Pattern ID = Pattern.compile("(?[A-Za-z_][0-9A-Za-z_]*)"); + protected static final Pattern EQUALS = Pattern.compile("="); + protected static final Pattern REPEATS = Pattern.compile(""); + protected static final Pattern INT_OCT = Pattern.compile("0(?[0-7]+)"); + protected static final Pattern INT_DEC = Pattern.compile("(?\\d+)"); + protected static final Pattern INT_HEX = Pattern.compile("0x(?[0-9A-Fa-f]+)"); + + public static T parseValue(CharSequence text, + ExceptionalFunction func) throws GdbParseError { + GdbCValueParser parser = new GdbCValueParser(text); + T val = func.apply(parser); + parser.checkEmpty(true); + return val; + } + + public static GdbCValue parseValue(CharSequence text) throws GdbParseError { + return parseValue(text, GdbCValueParser::parseValue); + } + + public static GdbArrayValue parseArray(CharSequence text) throws GdbParseError { + return parseValue(text, GdbCValueParser::parseArray); + } + + public GdbCValueParser(CharSequence text) { + super(text); + } + + public GdbCValue parseValue() throws GdbParseError { + switch (peek(true)) { + case '{': + return parseCompositeOrArray(); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return parseInteger(); + default: + throw new GdbParseError("{ or digit", buf); + } + } + + public GdbIntValue parseInteger() throws GdbParseError { + // Try hex first, so 0 in 0x doesn't get chewed by INT_DEC + try { + String match = match(INT_HEX, true, "hex"); + return GdbIntValue.valueOf(new BigInteger(match, 16)); + } + catch (GdbParseError e) { + // Just try next match + } + try { + String match = match(INT_OCT, true, "oct"); + return GdbIntValue.valueOf(new BigInteger(match, 8)); + } + catch (GdbParseError e) { + // Just try next match + } + try { + String match = match(INT_DEC, true, "dec"); + return GdbIntValue.valueOf(new BigInteger(match, 10)); + } + catch (GdbParseError e) { + // Fall through to error report + } + + throw new GdbParseError("0x[hex], 0[oct], or [dec]", buf); + } + + public GdbCValue parseCompositeOrArray() throws GdbParseError { + match(LBRACE, true); + char c = peek(true); + if (c == '}') { + match(RBRACE, true); + return GdbCValue.EMPTY; + } + if (Character.isAlphabetic(c) || c == '_') { + return parseCompositeAfterOpen(); + } + return parseArrayAfterOpen(); + } + + public GdbCompositeValue parseCompositeAfterOpen() throws GdbParseError { + GdbCompositeValue.Builder result = GdbCompositeValue.builder(); + while (true) { + String id = match(ID, true); + match(EQUALS, true); + GdbCValue val = parseValue(); + result.put(id, val); + + char c = peek(true); + if (c == '}') { + match(RBRACE, false); + return result.build(); + } + else if (c == ',') { + match(COMMA, false); + } + else { + throw new GdbParseError("} or ,", buf); + } + } + } + + public GdbArrayValue parseArray() throws GdbParseError { + match(LBRACE, true); + char c = peek(true); + if (c == '}') { + match(RBRACE, false); + return GdbArrayValue.EMPTY; + } + return parseArrayAfterOpen(); + } + + public GdbArrayValue parseArrayAfterOpen() throws GdbParseError { + GdbArrayValue.Builder result = GdbArrayValue.builder(); + while (true) { + GdbCValue val = parseValue(); + result.add(val); + + char c = peek(true); + if (c == '<') { + match(REPEATS, false); + GdbIntValue count = parseInteger(); + match(TIMES, false); // Pattern includes required whitespace + long n = count.val.longValueExact(); + for (int i = 1 /* Already did i=0 */; i < n; i++) { + result.add(val); + } + c = peek(true); + } + if (c == '}') { + match(RBRACE, false); + return result.build(); + } + else if (c == ',') { + match(COMMA, false); + } + else { + throw new GdbParseError("} , or map = new HashSetValuedHashMap() { + @Override + protected HashSet createCollection() { + return new LinkedHashSet<>(); + } + }; + private MultiValuedMap unmodifiableMap = + MultiMapUtils.unmodifiableMultiValuedMap(map); + private final List entryList = new ArrayList<>(); + private final List unmodifiableEntries = Collections.unmodifiableList(entryList); + private final boolean enclosed; + + private GdbMiFieldList(boolean enclosed) { + this.enclosed = enclosed; + } + + private void add(String key, Object value) { + entryList.add(new Entry(key, value)); + map.put(key, value); + } + + /** + * Get the list of entries, in order of appearance + * + * @return the list of key-value entries + */ + public List entries() { + return unmodifiableEntries; + } + + /** + * Get all values associated with the given key + * + * @param key the key + * @return the unordered collection of values + */ + public Collection get(String key) { + return unmodifiableMap.get(key); + } + + /** + * Assume only a single value is associated with the key, and get that value + * + * @param key the key + * @return the value + * @throws IllegalStateException if more than one value is associated + */ + public Object getSingleton(String key) { + Collection col = map.get(key); + if (col.size() == 0) { + return null; + } + if (col.size() != 1) { + throw new IllegalStateException("Key " + key + " is multi-valued: " + col); + } + return col.iterator().next(); + } + + /** + * Assume only a single string is associated with the key, and get that string + * + * @param key the key + * @return the value + * @throws IllegalStateException if more than one value is associated + */ + public String getString(String key) { + return (String) getSingleton(key); + } + + /** + * Assume only a single list is associated with the key, and get that list + * + * For convenience, the list is cast to a list of elements of a given type. This cast is + * unchecked. + * + * @param cls the type of elements in the list + * @param key the key + * @return the value + * @throws IllegalStateException if more than one value is associated + */ + @SuppressWarnings("unchecked") + public List getListOf(Class cls, String key) { + return (List) getSingleton(key); + } + + /** + * Assume only a single field list is associated with the key, and get that list + * + * @param key the key + * @return the value + * @throws IllegalStateException if more than one value is associated + */ + public GdbMiFieldList getFieldList(String key) { + Object obj = getSingleton(key); + if (obj instanceof List) { + if (((List) obj).isEmpty()) { + return GdbMiFieldList.builder().build(); + } + } + return (GdbMiFieldList) obj; + } + + /** + * Check if a key is present in the field list + * + * @param key the key + * @return true if present, false otherwise + */ + public boolean containsKey(String key) { + return map.containsKey(key); + } + + /** + * Count the number of entries (not keys) in the field list + * + * @return the count + */ + public int size() { + return entryList.size(); + } + + @Override + public String toString() { + return map.toString(); + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof GdbMiFieldList)) { + return false; + } + GdbMiFieldList that = (GdbMiFieldList) obj; + return Objects.equals(this.map, that.map); + } + } + + // see #parseString() for why this is no longer used.... + //protected static final Pattern CSTRING = Pattern.compile("\\\"(\\\\.|[^\\\\\"])*\\\""); + + protected static final Pattern COMMA = Pattern.compile(","); + protected static final Pattern LBRACKET = Pattern.compile("\\["); + protected static final Pattern RBRACKET = Pattern.compile("\\]"); + protected static final Pattern FIELD_ID = Pattern.compile("([0-9A-Za-z_]|-)+"); + protected static final Pattern EQUALS = Pattern.compile("="); + protected static final Pattern LBRACE = Pattern.compile("\\{"); + protected static final Pattern RBRACE = Pattern.compile("\\}"); + + /** + * Construct a parser of the given text + * + * The static methods {@link #parseObject(CharSequence)}, {@link #parseString(CharSequence)}, + * {@link #parseFields(CharSequence)} should probably be used instead. + * + * @param text the text to parse + */ + public GdbMiParser(CharSequence text) { + super(text); + } + + /** + * Parse the object in the text + * + * @param text the text to parse + * @return the object defined in the text + * @throws GdbParseError if no text matches the pattern + */ + public static Object parseObject(CharSequence text) throws GdbParseError { + GdbMiParser parser = new GdbMiParser(text); + Object result = parser.parseObject(); + parser.checkEmpty(true); + return result; + } + + /** + * Parse the string literal in the text + * + * @param text the text to parse + * @return the string parsed + * @throws GdbParseError if no text matches the pattern + */ + public static String parseString(CharSequence text) throws GdbParseError { + GdbMiParser parser = new GdbMiParser(text); + String result = parser.parseString(); + parser.checkEmpty(true); + return result; + } + + /** + * Parse the fields in the text + * + * @param text the text to parse + * @return the string parsed + * @throws GdbParseError if no text matches the pattern + */ + public static GdbMiFieldList parseFields(CharSequence text) throws GdbParseError { + GdbMiParser parser = new GdbMiParser(text); + GdbMiFieldList result = parser.parseFields(false); + parser.checkEmpty(true); + return result; + } + + /** + * Parse the object at the cursor + * + * @see #parseObject(CharSequence) + * @return the object + * @throws GdbParseError + */ + public Object parseObject() throws GdbParseError { + switch (peek(true)) { + case '"': + return parseString(); + case '[': + return parseList(); + case '{': + return parseMap(); + default: + // TODO: I'm a little uneasy about this + // It's basically a malformed map + return parseFields(false); + } + } + + /** + * Parse the string at the cursor + * + * @see #parseString(CharSequence) + * @return the string + * @throws GdbParseError if no text matches the pattern + */ + public String parseString() throws GdbParseError { + /* + * Matching CSTRING for inputs of too many characters (2048, really?) causes a + * StackOverflowException in Java's built-in Pattern object. Boo! Thus, I'll write this + * myself. All said and done, this might actually look better than the old regex + */ + // String match = match(CSTRING); + //return StringEscapeUtils.unescapeJava(match.substring(1, match.length() - 1)); + int start = buf.position(); + if ('"' != peek(false)) { // Keep whitespace that is in the string + throw new GdbParseError("\"", buf); + } + buf.get(); // consume " + while (true) { + char c = buf.get(); + if (c == '"') { + break; + } + else if (c == '\\') { + buf.get(); + } + } + // the closing " will already have been consumed + int end = buf.position(); + buf.position(0); + String result = buf.subSequence(start + 1, end - 1).toString(); // remove "s + buf.position(end); + return StringEscapeUtils.unescapeJava(result); + } + + /** + * Parse the list at the cursor + * + * @return the list + * @throws GdbParseError if no text matches the pattern + */ + public Object parseList() throws GdbParseError { + match(LBRACKET, true); + List result = new ArrayList<>(); + while (buf.hasRemaining()) { + char c = peek(true); + if (c == ']') { + match(RBRACKET, false); + break; + } + if (c == ',') { + match(COMMA, false); + } + result.add(parseObject()); + } + if (result.size() == 1) { + Object maybeFieldList = result.get(0); + if (maybeFieldList instanceof GdbMiFieldList) { + GdbMiFieldList fieldList = (GdbMiFieldList) maybeFieldList; + if (!fieldList.enclosed) { + return maybeFieldList; + } + } + } + return Collections.unmodifiableList(result); + } + + /** + * Parse the map at the cursor + * + * @return the map (as a field list) + * @throws GdbParseError if no text matches the pattern + */ + public GdbMiFieldList parseMap() throws GdbParseError { + match(LBRACE, true); + GdbMiFieldList result = parseFields(true); + match(RBRACE, true); + return result; + } + + /** + * Parse the fields at the cursor + * + * @see #parseFields(CharSequence) + * @param enclosed true if the field list is enclosed in brackets/braces + * @return the field list + * @throws GdbParseError if no text matches the pattern + */ + public GdbMiFieldList parseFields(boolean enclosed) throws GdbParseError { + GdbMiFieldList result = new GdbMiFieldList(enclosed); + while (buf.hasRemaining()) { + char c = peek(true); + if (c == ']' || c == '}') { + break; + } + if (c == ',') { + match(COMMA, false); + } + c = peek(true); + if (c == '{') { + Object fieldVal = parseObject(); + result.add(UNNAMED, fieldVal); + continue; + } + String fieldId = match(FIELD_ID, true); + match(EQUALS, true); + Object fieldVal = parseObject(); + result.add(fieldId, fieldVal); + } + return result; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/parsing/GdbParsingUtils.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/parsing/GdbParsingUtils.java new file mode 100644 index 0000000000..f1b7544361 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/parsing/GdbParsingUtils.java @@ -0,0 +1,171 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.parsing; + +import java.math.BigInteger; +import java.nio.CharBuffer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Some utility methods for working with GDB + */ +public enum GdbParsingUtils { + ; + + public static abstract class AbstractGdbParser { + protected final CharBuffer buf; + + protected AbstractGdbParser(CharSequence text) { + this.buf = CharBuffer.wrap(text); + } + + protected String match(Pattern pat, boolean chompWhitespace) throws GdbParseError { + return match(pat, chompWhitespace, null); + } + + protected String match(Pattern pat, boolean chompWhitespace, String group) + throws GdbParseError { + if (chompWhitespace) { + chompWhitespace(); + } + Matcher mat = pat.matcher(buf); + if (mat.lookingAt()) { + String result = group == null ? mat.group() : mat.group(group); + // TODO: Might just need end? + int length = mat.end() - mat.start(); + buf.position(buf.position() + length); + return result; + } + throw new GdbParseError("" + pat, buf); + } + + /** + * Peek at the next character + * + * @param chompWhitespace true to consume all whitespace at the current position first + * @return the next character + */ + protected char peek(boolean chompWhitespace) { + if (chompWhitespace) { + chompWhitespace(); + } + if (!buf.hasRemaining()) { + return '\0'; + } + return buf.get(buf.position()); + } + + /** + * Advance the buffer position past any whitespace + */ + protected void chompWhitespace() { + while (buf.hasRemaining() && Character.isWhitespace(buf.get(buf.position()))) { + buf.get(); + } + } + + /** + * Check that the parser has reached the end of the line (EOL) + * + * @throws GdbParseError if there is text remaining + */ + protected void checkEmpty(boolean chompWhitespace) throws GdbParseError { + if (chompWhitespace) { + chompWhitespace(); + } + if (buf.hasRemaining()) { + throw new GdbParseError("EOL", buf); + } + } + } + + /** + * An exception that is thrown when the manager cannot parse a command result, event, or value + */ + public static class GdbParseError extends Exception { + /** + * Construct a description of a parse error + * + * @param expected the character(s) expected + * @param s the tail from the first rejected character + */ + public GdbParseError(String expected, CharSequence s) { + super("Expected [" + expected + "] at tail '" + s + "'"); + } + } + + /** + * Parse a hex string to a long + * + * The string must have the {@code 0x} prefix + * + * @param hex the string + * @return the parsed long + */ + public static long parsePrefixedHex(String hex) { + if (!hex.startsWith("0x")) { + throw new NumberFormatException("Hex must start with 0x"); + } + return Long.parseUnsignedLong(hex.substring(2), 16); + } + + /** + * Parse a hex string to a big integer + * + * The string must have the {@code 0x} prefix + * + * @param hex the string + * @return the parsed big integer + */ + public static BigInteger parsePrefixedHexBig(String hex) { + if (!hex.startsWith("0x")) { + throw new NumberFormatException("Hex must start with 0x"); + } + return new BigInteger(hex.substring(2), 16); + } + + /** + * Parse an octal string to a long + * + * The string must have the {@code 0} prefix + * + * @param oct the string + * @return the parsed long + */ + public static long parsePrefixedOctal(String oct) { + if (!oct.startsWith("0")) { + throw new NumberFormatException("Octal must start with 0"); + } + return Long.parseUnsignedLong(oct, 8); + } + + /** + * Parse an inferior id + * + * The id must have the {@code i} prefix + * + * @param id the string + * @return the parsed integer + */ + public static int parseInferiorId(String id) { + if (!id.startsWith("i")) { + throw new IllegalArgumentException( + "Map id does not specify an inferior. Must start with 'i'"); + } + return Integer.parseInt(id.substring(1)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbBreakpointHitReason.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbBreakpointHitReason.java new file mode 100644 index 0000000000..1d6078bc0c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbBreakpointHitReason.java @@ -0,0 +1,77 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.reason; + +import agent.gdb.manager.GdbStackFrame; +import agent.gdb.manager.GdbThread; +import agent.gdb.manager.impl.GdbStackFrameImpl; +import agent.gdb.manager.impl.GdbThreadImpl; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +/** + * The inferior stopped because a thread hit a breakpoint + */ +public class GdbBreakpointHitReason implements GdbReason { + private final GdbMiFieldList frame; + private final long bkptno; + private final int threadId; + + public GdbBreakpointHitReason(GdbMiFieldList info) { + this.bkptno = Long.parseLong(info.getString("bkptno")); + this.threadId = Integer.parseInt(info.getString("thread-id")); + this.frame = info.getFieldList("frame"); + } + + /** + * Get the ID of the breakpoint that was hit + * + * @return the breakpoint number + */ + public long getBreakpointId() { + return bkptno; + } + + /** + * Get the stack frame where the breakpoint was hit + * + * TODO: Why would this ever be non-zero, again? + * + * @param thread the thread + * @return the frame + */ + public GdbStackFrame getFrame(GdbThread thread) { + return GdbStackFrameImpl.fromFieldList((GdbThreadImpl) thread, frame); + } + + /** + * Get the ID of the thread that hit the breakpoint + * + * @return the thread ID + */ + public int getThreadId() { + return threadId; + } + + @Override + public String toString() { + return ""; + } + + @Override + public String desc() { + return "Breakpoint " + bkptno + " hit"; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbEndSteppingRangeReason.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbEndSteppingRangeReason.java new file mode 100644 index 0000000000..bd9b791602 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbEndSteppingRangeReason.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.reason; + +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +/** + * The inferior stopped because it finished stepping + */ +public class GdbEndSteppingRangeReason implements GdbReason { + public GdbEndSteppingRangeReason(GdbMiFieldList info) { + // Nothing additional to parse + } + + @Override + public String desc() { + return "Stepping ended"; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbExitNormallyReason.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbExitNormallyReason.java new file mode 100644 index 0000000000..6a98af9a61 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbExitNormallyReason.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.reason; + +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +/** + * The inferior stopped because it has exited (with status 0) + */ +public class GdbExitNormallyReason implements GdbReason { + public GdbExitNormallyReason(GdbMiFieldList info) { + // Nothing additional to parse + } + + @Override + public String desc() { + return "Exited normally"; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbExitedReason.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbExitedReason.java new file mode 100644 index 0000000000..29b1011b37 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbExitedReason.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.reason; + +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +/** + * The inferior stopped because it has exited + */ +public class GdbExitedReason implements GdbReason { + private final int exitCode; + + public GdbExitedReason(GdbMiFieldList info) { + this.exitCode = Integer.parseInt(info.getString("exit-code")); + } + + /** + * Get the exit code of the inferior + * + * @return the exit code + */ + public int getExitCode() { + return exitCode; + } + + @Override + public String desc() { + return "Exited with code " + exitCode; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbReason.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbReason.java new file mode 100644 index 0000000000..289c54a840 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbReason.java @@ -0,0 +1,94 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.reason; + +import java.util.Map; +import java.util.function.Function; + +import com.google.common.collect.ImmutableMap; + +import agent.gdb.manager.GdbState; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import ghidra.util.Msg; + +/** + * Indicates the reason for a thread's state to change, usually only when {@link GdbState#STOPPED} + * + * This concept is native to GDB. When a thread stops, GDB may communicate the reason, e.g., a + * breakpoint was hit, or the thread exited. The manager attempts to parse information for the + * reasons it understands and provides it in e.g., a {@link GdbBreakpointHitReason}. If GDB provides + * a reason that is not understood by the manager, then {@link GdbReason.Reasons#UNKNOWN} is given. + * If no reason is provided, then {@link GdbReason.Reasons#NONE} is given. + */ +public interface GdbReason { + /** + * A map of reason strings to reason classes + */ + static final Map> TYPES = + new ImmutableMap.Builder>() + .put("signal-received", GdbSignalReceivedReason::new) + .put("breakpoint-hit", GdbBreakpointHitReason::new) + .put("end-stepping-range", GdbEndSteppingRangeReason::new) + .put("exited", GdbExitedReason::new) + .put("exited-normally", GdbExitNormallyReason::new) + .build(); + + /** + * Reasons other than those given by GDB + */ + enum Reasons implements GdbReason { + /** + * No reason was given + */ + NONE("No reason"), + /** + * A reason was given, but the manager does not understand it + */ + UNKNOWN("Unknown"); + + final String desc; + + private Reasons(String desc) { + this.desc = desc; + } + + @Override + public String desc() { + return desc; + } + } + + /** + * Process the parsed reason information to get the reason for a state change + * + * @param info the parsed information, i.e., the map containing "{@code reason={...}}" + * @return the reason + */ + static GdbReason getReason(GdbMiFieldList info) { + String reasonStr = info.getString("reason"); + if (reasonStr == null) { + return Reasons.NONE; + } + Function cons = TYPES.get(reasonStr); + if (cons == null) { + Msg.warn(GdbReason.class, "Unknown stop reason: " + reasonStr); + return Reasons.UNKNOWN; + } + return cons.apply(info); + } + + public String desc(); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbSignalReceivedReason.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbSignalReceivedReason.java new file mode 100644 index 0000000000..cc7737f116 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/reason/GdbSignalReceivedReason.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.reason; + +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +/** + * The inferior stopped because it has received a signal + */ +public class GdbSignalReceivedReason implements GdbReason { + private final String signalName; + + public GdbSignalReceivedReason(GdbMiFieldList info) { + this.signalName = info.getString("signal-name"); + } + + /** + * Get the (POSIX) name of the signal received + * + * @return the signal name + */ + public String getSignalName() { + return signalName; + } + + @Override + public String desc() { + return "Signalled with " + getSignalName(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImpl.java new file mode 100644 index 0000000000..653387aca7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImpl.java @@ -0,0 +1,149 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import agent.gdb.manager.*; +import agent.gdb.manager.impl.cmd.GdbCommandError; +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.AbstractDebuggerObjectModel; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibility; +import ghidra.dbg.target.TargetObject; +import ghidra.program.model.address.*; + +public class GdbModelImpl extends AbstractDebuggerObjectModel { + // TODO: Need some minimal memory modeling per architecture on the model/agent side. + // The model must convert to and from Ghidra's address space names + protected static final String SPACE_NAME = "ram"; + + protected static T translateEx(Throwable ex) { + Throwable t = AsyncUtils.unwrapThrowable(ex); + if (t instanceof GdbCommandError) { + GdbCommandError err = (GdbCommandError) t; + throw new DebuggerUserException(err.getInfo().getString("msg")); + } + return ExceptionUtils.rethrow(ex); + } + + // Don't make this static, so each model has a unique "GDB" space + protected final AddressSpace space = + new GenericAddressSpace(SPACE_NAME, 64, AddressSpace.TYPE_RAM, 0); + protected final AddressFactory addressFactory = + new DefaultAddressFactory(new AddressSpace[] { space }); + + protected final GdbManager gdb; + protected boolean noStarti = false; + protected final GdbModelTargetSession session; + + protected final CompletableFuture completedSession; + protected final GdbStateListener gdbExitListener = this::checkExited; + + public GdbModelImpl() { + this.gdb = GdbManager.newInstance(); + this.session = new GdbModelTargetSession(this); + + this.completedSession = CompletableFuture.completedFuture(session); + + gdb.addStateListener(gdbExitListener); + } + + @Override + public AddressSpace getAddressSpace(String name) { + if (!SPACE_NAME.equals(name)) { + return null; + } + return space; + } + + // TODO: Place make this a model method? + public AddressFactory getAddressFactory() { + return addressFactory; + } + + protected void checkExited(GdbState state, GdbCause cause) { + switch (state) { + case NOT_STARTED: { + break; + } + case STARTING: { + break; + } + case RUNNING: { + session.invalidateMemoryAndRegisterCaches(); + session.setAccessibility(TargetAccessibility.INACCESSIBLE); + break; + } + case STOPPED: { + session.setAccessibility(TargetAccessibility.ACCESSIBLE); + break; + } + case EXIT: { + try { + terminate(); + } + catch (IOException e) { + throw new AssertionError(e); + } + break; + } + } + } + + public CompletableFuture startGDB(String gdbCmd, String[] args) { + try { + gdb.start(gdbCmd, args); + return gdb.runRC(); + } + catch (IOException e) { + return CompletableFuture.failedFuture(e); + } + } + + public void consoleLoop() throws IOException { + gdb.consoleLoop(); + } + + public void terminate() throws IOException { + session.invalidateSubtree("GDB is terminating"); + gdb.terminate(); + } + + @Override + public CompletableFuture fetchModelRoot() { + return completedSession; + } + + @Override + public boolean isAlive() { + return gdb.getState().isAlive(); + } + + @Override + public CompletableFuture close() { + try { + terminate(); + return AsyncUtils.NIL; + } + catch (Throwable t) { + return CompletableFuture.failedFuture(t); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImplUtils.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImplUtils.java new file mode 100644 index 0000000000..4ba31bda2a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImplUtils.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.GdbInferior; +import ghidra.async.AsyncUtils; +import ghidra.dbg.util.ShellUtils; + +public enum GdbModelImplUtils { + ; + public static CompletableFuture launch(GdbModelImpl impl, GdbInferior inferior, + List args) { + return inferior.fileExecAndSymbols(args.get(0)).thenCompose(__ -> { + return inferior.setVar("args", ShellUtils.generateLine(args.subList(1, args.size()))); + }).thenCompose(__ -> { + if (impl.noStarti) { + return inferior.console("start"); + } + return inferior.console("starti").thenApply(___ -> true).exceptionally(e -> { + impl.noStarti = true; + return false; + }).thenCompose(success -> { + if (success) { + return AsyncUtils.NIL; + } + return inferior.console("start"); + }); + }); + } + + public static V noDupMerge(V first, V second) { + throw new AssertionError(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelSelectableObject.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelSelectableObject.java new file mode 100644 index 0000000000..a14b616dff --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelSelectableObject.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.attributes.TargetObjectRef; + +interface GdbModelSelectableObject extends TargetObjectRef { + CompletableFuture select(); +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetAttachable.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetAttachable.java new file mode 100644 index 0000000000..d4ae5440ce --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetAttachable.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.gdb.manager.GdbProcessThreadGroup; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetAttachable; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; + +public class GdbModelTargetAttachable + extends DefaultTargetObject + implements TargetAttachable { + protected static final String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid"; + // TODO: DESCRIPTION, TYPE, USER? + + protected static String indexAttachable(GdbProcessThreadGroup process) { + return PathUtils.makeIndex(process.getPid()); + } + + protected static String keyAttachable(GdbProcessThreadGroup process) { + return PathUtils.makeKey(indexAttachable(process)); + } + + protected static String computeDisplay(GdbProcessThreadGroup process) { + return String.format("%d %s", process.getPid(), process.getDescription()); + } + + protected final long pid; + protected final String display; + + public GdbModelTargetAttachable(GdbModelImpl impl, GdbModelTargetAvailableContainer parent, + GdbProcessThreadGroup process) { + super(impl, parent, keyAttachable(process), "Attachable"); + this.pid = process.getPid(); + this.display = computeDisplay(process); + + this.changeAttributes(List.of(), List.of(), Map.of( // + PID_ATTRIBUTE_NAME, pid, // + DISPLAY_ATTRIBUTE_NAME, display, // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + public long getPid() { + return pid; + } + + @Override + public String getDisplay() { + return display; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetAvailableContainer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetAvailableContainer.java new file mode 100644 index 0000000000..097f151fea --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetAvailableContainer.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.gdb.manager.GdbProcessThreadGroup; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.util.datastruct.WeakValueHashMap; + +public class GdbModelTargetAvailableContainer + extends DefaultTargetObject { + protected final GdbModelImpl impl; + + protected final Map attachablesById = + new WeakValueHashMap<>(); + + public GdbModelTargetAvailableContainer(GdbModelTargetSession session) { + super(session.impl, session, "Available", "AvailableContainer"); + this.impl = session.impl; + changeAttributes(List.of(), Map.of( + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.SOLICITED // + ), "Initialized"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return impl.gdb.listAvailableProcesses().thenAccept(list -> { + List available; + synchronized (this) { + // NOTE: If more details added to entries, should clear attachablesById + available = list.stream() + .map(this::getTargetAttachable) + .collect(Collectors.toList()); + } + setElements(available, "Refreshed"); + }); + } + + protected synchronized GdbModelTargetAttachable getTargetAttachable( + GdbProcessThreadGroup process) { + return attachablesById.computeIfAbsent(process.getPid(), + i -> new GdbModelTargetAttachable(impl, this, process)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointContainer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointContainer.java new file mode 100644 index 0000000000..19112c8a83 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointContainer.java @@ -0,0 +1,164 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.stream.Collectors; + +import agent.gdb.manager.GdbCause; +import agent.gdb.manager.GdbEventsListenerAdapter; +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import agent.gdb.manager.breakpoint.GdbBreakpointType; +import ghidra.async.AsyncFence; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetBreakpointContainer; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.program.model.address.AddressRange; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +public class GdbModelTargetBreakpointContainer + extends DefaultTargetObject + implements TargetBreakpointContainer, + GdbEventsListenerAdapter { + + protected static final TargetBreakpointKindSet SUPPORTED_KINDS = + TargetBreakpointKindSet.of(TargetBreakpointKind.values()); + + protected final GdbModelImpl impl; + + protected final Map specsByNumber = + new WeakValueHashMap<>(); + + public GdbModelTargetBreakpointContainer(GdbModelTargetSession session) { + super(session.impl, session, "Breakpoints", "BreakpointContainer"); + this.impl = session.impl; + + impl.gdb.addEventsListener(this); + + changeAttributes(List.of(), Map.of( // + // TODO: Seems terrible to duplicate this static attribute on each instance + SUPPORTED_BREAK_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS // + ), "Initialized"); + } + + @Override + public void breakpointCreated(GdbBreakpointInfo info, GdbCause cause) { + changeElements(List.of(), List.of(getTargetBreakpointSpec(info)), "Created"); + } + + @Override + public void breakpointModified(GdbBreakpointInfo newInfo, GdbBreakpointInfo oldInfo, + GdbCause cause) { + getTargetBreakpointSpec(oldInfo).updateInfo(oldInfo, newInfo, "Modified"); + } + + protected void breakpointHit(long bpId, GdbModelTargetStackFrame frame) { + GdbModelTargetBreakpointSpec spec = getTargetBreakpointSpecIfPresent(bpId); + if (spec == null) { + Msg.error(this, "Stopped for breakpoint unknown to the agent: " + bpId + " (pc=" + + frame.getProgramCounter() + ")"); + return; + } + + GdbModelTargetBreakpointLocation eb = spec.findEffective(frame); + if (eb == null) { + Msg.warn(this, "Stopped for a breakpoint whose location is unknown to the agent: " + + spec + " (pc=" + frame.getProgramCounter() + ")"); + //return; // Not idea, but eb == null should be fine, since the spec holds the actions + } + listeners.fire(TargetBreakpointListener.class) + .breakpointHit(this, frame.thread, frame, spec, eb); + spec.breakpointHit(frame, eb); + } + + @Override + public void breakpointDeleted(GdbBreakpointInfo info, GdbCause cause) { + synchronized (this) { + specsByNumber.remove(info.getNumber()); + } + changeElements(List.of(GdbModelTargetBreakpointSpec.indexBreakpoint(info)), List.of(), + "Deleted"); + } + + protected CompletableFuture doPlaceBreakpoint(Set kinds, + Function> placer) { + AsyncFence fence = new AsyncFence(); + if (kinds.contains(TargetBreakpointKind.READ) && + kinds.contains(TargetBreakpointKind.WRITE)) { + fence.include(placer.apply(GdbBreakpointType.ACCESS_WATCHPOINT)); + } + else if (kinds.contains(TargetBreakpointKind.READ)) { + fence.include(placer.apply(GdbBreakpointType.READ_WATCHPOINT)); + } + else if (kinds.contains(TargetBreakpointKind.WRITE)) { + fence.include(placer.apply(GdbBreakpointType.HW_WATCHPOINT)); + } + if (kinds.contains(TargetBreakpointKind.EXECUTE)) { + fence.include(placer.apply(GdbBreakpointType.HW_BREAKPOINT)); + } + if (kinds.contains(TargetBreakpointKind.SOFTWARE)) { + fence.include(placer.apply(GdbBreakpointType.BREAKPOINT)); + } + return fence.ready().exceptionally(GdbModelImpl::translateEx); + } + + @Override + public CompletableFuture placeBreakpoint(String expression, + Set kinds) { + return doPlaceBreakpoint(kinds, t -> impl.gdb.insertBreakpoint(expression, t)); + } + + @Override + public CompletableFuture placeBreakpoint(AddressRange range, + Set kinds) { + // TODO: Consider how to translate address spaces + long offset = range.getMinAddress().getOffset(); + int len = (int) range.getLength(); + return doPlaceBreakpoint(kinds, t -> impl.gdb.insertBreakpoint(offset, len, t)); + } + + public synchronized GdbModelTargetBreakpointSpec getTargetBreakpointSpec( + GdbBreakpointInfo info) { + return specsByNumber.computeIfAbsent(info.getNumber(), + i -> new GdbModelTargetBreakpointSpec(this, info)); + } + + public synchronized GdbModelTargetBreakpointSpec getTargetBreakpointSpecIfPresent(long number) { + return specsByNumber.get(number); + } + + protected void updateUsingBreakpoints(Map byNumber) { + List specs; + synchronized (this) { + specs = byNumber.values() + .stream() + .map(this::getTargetBreakpointSpec) + .collect(Collectors.toList()); + } + setElements(specs, "Refreshed"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (!refresh) { + updateUsingBreakpoints(impl.gdb.getKnownBreakpoints()); + } + return impl.gdb.listBreakpoints().thenAccept(this::updateUsingBreakpoints); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointLocation.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointLocation.java new file mode 100644 index 0000000000..64d6301bc4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointLocation.java @@ -0,0 +1,143 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.gdb.manager.breakpoint.GdbBreakpointLocation; +import agent.gdb.manager.parsing.GdbCValueParser; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; +import generic.Unique; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.attributes.TargetObjectRefList; +import ghidra.dbg.attributes.TargetObjectRefList.DefaultTargetObjectRefList; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.target.*; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; + +public class GdbModelTargetBreakpointLocation + extends DefaultTargetObject + implements TargetBreakpointLocation { + protected static String indexLocation(GdbBreakpointLocation loc) { + return PathUtils.makeIndex(loc.getSub()); + } + + protected static String keyLocation(GdbBreakpointLocation loc) { + return PathUtils.makeKey(indexLocation(loc)); + } + + protected final GdbModelImpl impl; + protected final GdbBreakpointLocation loc; + + protected Address address; + protected Integer length; + protected final TargetObjectRefList affects; + protected String display; + + public GdbModelTargetBreakpointLocation(GdbModelTargetBreakpointSpec spec, + GdbBreakpointLocation loc) { + super(spec.impl, spec, keyLocation(loc), "EffectiveBreakpoint"); + this.impl = spec.impl; + this.loc = loc; + + this.affects = doGetAffects(); + if (!spec.info.getType().isWatchpoint()) { + this.address = doGetAddress(); + this.length = 1; + doChangeAttributes("Initialized"); + } + assert !this.affects.isEmpty(); + } + + protected void doChangeAttributes(String reason) { + this.changeAttributes(List.of(), + Map.of(SPEC_ATTRIBUTE_NAME, parent, AFFECTS_ATTRIBUTE_NAME, affects, + ADDRESS_ATTRIBUTE_NAME, address, LENGTH_ATTRIBUTE_NAME, length, + DISPLAY_ATTRIBUTE_NAME, display = computeDisplay(), UPDATE_MODE_ATTRIBUTE_NAME, + TargetUpdateMode.FIXED // + ), reason); + } + + /** + * Initialize watchpoint attributes via expression evaluation + * + * This has to be async because it involves interacting with GDB. GDB does not give the address + * or location information for location-specified watchpoints. Instead we take the expression + * and ask GDB to evaluate its address and size. + * + * @return a future which completes when the (watchpoint) location has been initialized. + */ + protected CompletableFuture initWpt() { + assert loc.getAddr() == null; + String what = parent.info.getWhat(); + if (!what.startsWith(GdbBreakpointLocation.WATCHPOINT_LOCATION_PREFIX)) { + throw new AssertionError("non-location location"); + } + String exp = what.substring(GdbBreakpointLocation.WATCHPOINT_LOCATION_PREFIX.length()); + GdbModelTargetInferior inf = Unique.assertOne(affects); + String addrSizeExp = String.format("{(long long)&(%s), (long long)sizeof(%s)}", exp, exp); + return inf.inferior.evaluate(addrSizeExp).thenAccept(result -> { + List vals; + try { + vals = GdbCValueParser.parseArray(result).expectLongs(); + } + catch (GdbParseError e) { + throw new AssertionError("Unexpected result type: " + result, e); + } + if (vals.size() != 2) { + throw new AssertionError("Unexpected result count: " + result); + } + + address = impl.space.getAddress(vals.get(0)); + length = vals.get(1).intValue(); + doChangeAttributes("Initialized"); + }); + } + + protected String computeDisplay() { + return String.format("%d.%d %s", parent.info.getNumber(), loc.getSub(), address); + } + + protected Address doGetAddress() { + return impl.space.getAddress(loc.addrAsLong()); + } + + @Override + public Integer getLength() { + return length; + } + + protected TargetObjectRefList doGetAffects() { + return loc.getInferiorIds() + .stream() + .map(impl.session.inferiors::getTargetInferior) + .collect(Collectors.toCollection(DefaultTargetObjectRefList::new)); + } + + @Override + public TargetObjectRefList getAffects() { + return affects; + } + + @Override + public TypedTargetObjectRef> getSpecification() { + return parent; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointSpec.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointSpec.java new file mode 100644 index 0000000000..e0e1cd5c83 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointSpec.java @@ -0,0 +1,246 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import agent.gdb.manager.breakpoint.GdbBreakpointLocation; +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet; +import ghidra.dbg.util.CollectionUtils.Delta; +import ghidra.dbg.util.PathUtils; +import ghidra.util.Msg; +import ghidra.util.datastruct.ListenerSet; +import ghidra.util.datastruct.WeakValueHashMap; + +public class GdbModelTargetBreakpointSpec extends + DefaultTargetObject, GdbModelTargetBreakpointContainer> + implements TargetBreakpointSpec, + TargetDeletable { + + protected static String indexBreakpoint(GdbBreakpointInfo info) { + return PathUtils.makeIndex(info.getNumber()); + } + + protected static String keyBreakpoint(GdbBreakpointInfo info) { + return PathUtils.makeKey(indexBreakpoint(info)); + } + + protected final GdbModelImpl impl; + protected final long number; + protected GdbBreakpointInfo info; + protected boolean enabled; + protected String expression; + protected String display; + protected TargetBreakpointKindSet kinds; + + protected final Map breaksBySub = + new WeakValueHashMap<>(); + protected final ListenerSet actions = + new ListenerSet<>(TargetBreakpointAction.class) { + // Use strong references on actions + protected Map createMap() { + return Collections.synchronizedMap(new LinkedHashMap<>()); + }; + }; + + public GdbModelTargetBreakpointSpec(GdbModelTargetBreakpointContainer breakpoints, + GdbBreakpointInfo info) { + super(breakpoints.impl, breakpoints, keyBreakpoint(info), "BreakpointSpec"); + this.impl = breakpoints.impl; + this.number = info.getNumber(); + + updateInfo(null, info, "Created").exceptionally(ex -> { + Msg.info(this, "Initial breakpoint info update failed", ex); + return null; + }); + } + + @Override + public CompletableFuture delete() { + return impl.gdb.deleteBreakpoints(number); + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public String getExpression() { + return expression; + } + + protected TargetBreakpointKindSet computeKinds(GdbBreakpointInfo from) { + switch (from.getType()) { + case BREAKPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.SOFTWARE); + case HW_BREAKPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.EXECUTE); + case HW_WATCHPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.WRITE); + case READ_WATCHPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.READ); + case ACCESS_WATCHPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.READ, + TargetBreakpointKind.WRITE); + default: + return TargetBreakpointKindSet.of(); + } + } + + @Override + public TargetBreakpointKindSet getKinds() { + return kinds; + } + + @Override + public void addAction(TargetBreakpointAction action) { + actions.add(action); + } + + @Override + public void removeAction(TargetBreakpointAction action) { + actions.remove(action); + } + + protected CompletableFuture getInfo(boolean refresh) { + if (!refresh) { + return CompletableFuture.completedFuture(impl.gdb.getKnownBreakpoints().get(number)); + } + return impl.gdb.listBreakpoints() + .thenApply(__ -> impl.gdb.getKnownBreakpoints().get(number)); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getInfo(refresh).thenCompose(i -> { + return updateInfo(info, i, "Refreshed"); + }); + } + + @Override + public CompletableFuture disable() { + return impl.gdb.disableBreakpoints(number); + } + + @Override + public CompletableFuture enable() { + return impl.gdb.enableBreakpoints(number); + } + + protected CompletableFuture updateInfo(GdbBreakpointInfo oldInfo, + GdbBreakpointInfo newInfo, String reason) { + if (newInfo.getType().isWatchpoint()) { + return updateWptInfo(oldInfo, newInfo, reason); + } + else { + return updateBktpInfo(oldInfo, newInfo, reason); + } + } + + protected void updateAttributesFromInfo(String reason) { + Delta delta = changeAttributes(List.of(), Map.of( + ENABLED_ATTRIBUTE_NAME, enabled = info.isEnabled(), + EXPRESSION_ATTRIBUTE_NAME, expression = info.getOriginalLocation(), + KINDS_ATTRIBUTE_NAME, kinds = computeKinds(info), + DISPLAY_ATTRIBUTE_NAME, updateDisplay() // + ), reason); + // TODO: These attribute-specific conveniences should be done by DTO. + if (delta.added.containsKey(ENABLED_ATTRIBUTE_NAME)) { + listeners.fire(TargetBreakpointSpecListener.class).breakpointToggled(this, enabled); + } + if (delta.added.containsKey(DISPLAY_ATTRIBUTE_NAME)) { + listeners.fire.displayChanged(this, display); + } + } + + protected synchronized List setInfoAndComputeEffectives( + GdbBreakpointInfo oldInfo, GdbBreakpointInfo newInfo) { + assert oldInfo == this.info; + this.info = newInfo; + List effectives = newInfo.getLocations() + .stream() + .map(this::getTargetEffectiveBreakpoint) + .collect(Collectors.toList()); + breaksBySub.keySet() + .retainAll( + effectives.stream().map(e -> e.loc.getSub()).collect(Collectors.toSet())); + return effectives; + } + + protected CompletableFuture updateBktpInfo(GdbBreakpointInfo oldInfo, + GdbBreakpointInfo newInfo, String reason) { + List effectives = + setInfoAndComputeEffectives(oldInfo, newInfo); + updateAttributesFromInfo(reason); + setElements(effectives, reason); + return AsyncUtils.NIL; + } + + protected CompletableFuture updateWptInfo(GdbBreakpointInfo oldInfo, + GdbBreakpointInfo newInfo, String reason) { + List effectives = + setInfoAndComputeEffectives(oldInfo, newInfo); + updateAttributesFromInfo(reason); + assert effectives.size() == 1; + return effectives.get(0).initWpt().thenAccept(__ -> { + setElements(effectives, reason); + }); + } + + protected GdbModelTargetBreakpointLocation findEffective(GdbModelTargetStackFrame frame) { + for (GdbModelTargetBreakpointLocation bp : breaksBySub.values()) { + // TODO: Is this necessary? + /*if (bp.range.contains(frame.pc)) { + continue; + }*/ + if (!bp.affects.contains(frame.inferior)) { + continue; + } + return bp; + } + return null; + } + + protected void breakpointHit(GdbModelTargetStackFrame frame, + GdbModelTargetBreakpointLocation eb) { + actions.fire.breakpointHit(this, frame.thread, frame, eb); + } + + public synchronized GdbModelTargetBreakpointLocation getTargetEffectiveBreakpoint( + GdbBreakpointLocation loc) { + return breaksBySub.computeIfAbsent(loc.getSub(), + i -> new GdbModelTargetBreakpointLocation(this, loc)); + } + + protected String updateDisplay() { + display = String.format("%d breakpoint %s %s %s %s", + info.getNumber(), info.getDisp(), info.isEnabled() ? "y" : "n", info.getAddress(), + info.getWhat()); + return display; + } + + @Override + public String getDisplay() { + return display; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetEnvironment.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetEnvironment.java new file mode 100644 index 0000000000..62aa4d7277 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetEnvironment.java @@ -0,0 +1,181 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncFence; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetEnvironment; +import ghidra.dbg.target.TargetObject; +import ghidra.util.Msg; + +public class GdbModelTargetEnvironment + extends DefaultTargetObject + implements TargetEnvironment { + + protected final GdbModelImpl impl; + + protected String arch = ""; + protected String os = ""; + protected String endian = ""; + + public GdbModelTargetEnvironment(GdbModelTargetInferior inferior) { + super(inferior.impl, inferior, "Environment", "Environment"); + this.impl = inferior.impl; + + changeAttributes(List.of(), Map.of( + DEBUGGER_ATTRIBUTE_NAME, impl.session.debugger, + ARCH_ATTRIBUTE_NAME, "(unknown)", + OS_ATTRIBUTE_NAME, "(unknown)", + ENDIAN_ATTRIBUTE_NAME, "(unknown)", + VISIBLE_ARCH_ATTRIBUTE_NAME, "(unknown)", + VISIBLE_OS_ATTRIBUTE_NAME, "(unknown)", + VISIBLE_ENDIAN_ATTRIBUTE_NAME, "(unknown)", + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.UNSOLICITED // Attributes may still change + ), "Initialized"); + refresh(); + } + + protected CompletableFuture refreshArchitecture() { + /** + * GDB/MI is less informative than I'd like here. If the architecture is unset (i.e., + * "auto"), then using -gdb-show architecture gives no fields at all. If the user sets it to + * "auto", then it just says value="auto". Useless. The same issue applies to the + * =cmd-param-changed event. The console, while not readily machine consumable, provides the + * information I need. Unfortunately, when the "auto" architecture changes, there is no + * notification. The best I can do is refresh when an inferior is started. In 8.0, the auto + * architecture is actually updated on "file", but it doesn't really matter until the target + * is running, anyway. + */ + /** + * TODO: This assumes the relevant inferior is the current one, or that the current + * inferior's environment is the same as the one starting, which I think is Good Enough. + * Attempting to switch inferiors while something is starting is a no-go, anyway. If there + * is a known thread -- unlikely for an inferior that is just starting -- we could use the + * --thread parameter. + */ + return impl.gdb.consoleCapture("show architecture").thenAccept(out -> { + String[] tokens = out.split("\\s+"); + @SuppressWarnings("hiding") + String arch = tokens[tokens.length - 1].trim(); + if (arch.endsWith(")")) { + arch = arch.substring(0, arch.length() - 1); + } + // e.g., The target architecture is set automatically (currently i386) + // e.g., The target architecture is assumed to be i386 + // TODO: I don't have a way to detect if this parsing strategy fails. + // TODO: I could search using a list of support architectures + // Use "set architecture" to get "Valid arguments" + // But, that may also be (perhaps more) version dependent + this.arch = arch; + }).exceptionally(e -> { + Msg.error(this, "Could not get target architecture", e); + return null; + }); + } + + protected CompletableFuture refreshOS() { + /** + * GDB/MI is similarly un-informative when os is "auto". See comments in + * refreshArchitecture. + */ + /** + * TODO: Ditto the "current inferior" issue as refreshArchitecture + */ + return impl.gdb.consoleCapture("show os").thenAccept(out -> { + String[] tokens = out.split("\n")[0].split("\\s+"); + @SuppressWarnings("hiding") + String os = tokens[tokens.length - 1].trim(); + if (os.endsWith(".")) { + os = os.substring(0, os.length() - 1); + } + if (os.endsWith(")")) { + os = os.substring(0, os.length() - 1); + } + if (os.startsWith("\"") && os.endsWith("\"")) { + os = os.substring(1, os.length() - 1); + } + // e.g., The current OS ABI is "auto" (currently "GNU/Linux"). + // ... The default OS ABI is "GNU/Linux". + // e.g., The current OS ABI is "GNU/Linux". + // TODO: I don't have a way to detect if this parsing strategy fails. + // TODO: Use "set os" to get "Valid arguments"? + // Would need to ignore "auto", "default", and "none"? + this.os = os; + }).exceptionally(e -> { + Msg.error(this, "Could not get target os", e); + return null; + }); + } + + protected CompletableFuture refreshEndian() { + // TODO: This duplicates GdbInferiorImpl.syncEndianness.... + return impl.gdb.consoleCapture("show endian").thenAccept(out -> { + if (out.toLowerCase().contains("little endian")) { + endian = "little"; + } + else if (out.toLowerCase().contains("big endian")) { + endian = "big"; + } + else { + endian = "(unknown)"; + } + }).exceptionally(e -> { + Msg.error(this, "Could not get target endian", e); + return null; + }); + } + + protected CompletableFuture refresh() { + AsyncFence fence = new AsyncFence(); + fence.include(refreshArchitecture()); + fence.include(refreshOS()); + fence.include(refreshEndian()); + return fence.ready().thenAccept(__ -> { + changeAttributes(List.of(), Map.of( + ARCH_ATTRIBUTE_NAME, arch, + OS_ATTRIBUTE_NAME, os, + ENDIAN_ATTRIBUTE_NAME, endian, + VISIBLE_ARCH_ATTRIBUTE_NAME, arch, + VISIBLE_OS_ATTRIBUTE_NAME, os, + VISIBLE_ENDIAN_ATTRIBUTE_NAME, endian // + ), "Refreshed"); + }); + } + + @Override + public String getDebugger() { + return impl.session.debugger; + } + + @Override + public String getArchitecture() { + return arch; + } + + @Override + public String getOperatingSystem() { + return os; + } + + @Override + public String getEndian() { + return endian; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferior.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferior.java new file mode 100644 index 0000000000..9b3baf303f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferior.java @@ -0,0 +1,260 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.GdbManager.ExecSuffix; +import ghidra.async.AsyncFence; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.error.DebuggerModelNoSuchPathException; +import ghidra.dbg.error.DebuggerModelTypeException; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.util.PathUtils; +import ghidra.lifecycle.Internal; +import ghidra.util.Msg; + +public class GdbModelTargetInferior + extends DefaultTargetObject implements // + TargetProcess, // + TargetAggregate, // + TargetExecutionStateful, // + TargetAttacher, // + TargetDeletable, // + TargetDetachable, // + TargetKillable, // + TargetCmdLineLauncher, // + TargetResumable, // + TargetSteppable, // + GdbModelSelectableObject { + + public static final String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid"; + public static final String EXIT_CODE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "exit_code"; + + protected static String indexInferior(int inferiorId) { + return PathUtils.makeIndex(inferiorId); + } + + protected static String indexInferior(GdbInferior inferior) { + return indexInferior(inferior.getId()); + } + + protected static String keyInferior(GdbInferior inferior) { + return PathUtils.makeKey(indexInferior(inferior)); + } + + protected final GdbModelImpl impl; + protected final GdbInferior inferior; + protected String display; + + protected final GdbModelTargetEnvironment environment; + protected final GdbModelTargetProcessMemory memory; + protected final GdbModelTargetModuleContainer modules; + protected final GdbModelTargetRegisterContainer registers; + protected final GdbModelTargetThreadContainer threads; + + public GdbModelTargetInferior(GdbModelTargetInferiorContainer inferiors, GdbInferior inferior) { + super(inferiors.impl, inferiors, keyInferior(inferior), "Inferior"); + this.impl = inferiors.impl; + this.inferior = inferior; + + this.environment = new GdbModelTargetEnvironment(this); + this.memory = new GdbModelTargetProcessMemory(this); + this.modules = new GdbModelTargetModuleContainer(this); + this.registers = new GdbModelTargetRegisterContainer(this); + this.threads = new GdbModelTargetThreadContainer(this); + + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, TargetExecutionState.INACTIVE, // + DISPLAY_ATTRIBUTE_NAME, updateDisplay(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, TargetCmdLineLauncher.PARAMETERS, // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED, // + environment.getName(), environment, // + memory.getName(), memory, // + modules.getName(), modules, // + registers.getName(), registers, // + threads.getName(), threads // + ), "Initialized"); + } + + @Override + public CompletableFuture launch(List args) { + return GdbModelImplUtils.launch(impl, inferior, args); + } + + @Override + public CompletableFuture resume() { + return inferior.cont(); + } + + protected ExecSuffix convertToGdb(TargetStepKind kind) { + switch (kind) { + case FINISH: + return ExecSuffix.FINISH; + case INTO: + return ExecSuffix.STEP_INSTRUCTION; + case LINE: + return ExecSuffix.STEP; + case OVER: + return ExecSuffix.NEXT_INSTRUCTION; + case OVER_LINE: + return ExecSuffix.NEXT; + case RETURN: + return ExecSuffix.RETURN; + case UNTIL: + return ExecSuffix.UNTIL; + default: + throw new AssertionError(); + } + } + + @Override + public CompletableFuture step(TargetStepKind kind) { + switch (kind) { + case SKIP: + throw new UnsupportedOperationException(kind.name()); + case ADVANCE: // Why no exec-advance in GDB/MI? + // TODO: This doesn't work, since advance requires a parameter + return inferior.console("advance"); + default: + return inferior.step(convertToGdb(kind)); + } + } + + @Override + public CompletableFuture kill() { + return inferior.kill(); + } + + @Override + public CompletableFuture attach(TypedTargetObjectRef> ref) { + impl.assertMine(TargetObjectRef.class, ref); + // NOTE: These can change at any time. Just use the path to derive the target PID + if (!Objects.equals(PathUtils.parent(ref.getPath()), impl.session.available.getPath())) { + throw new DebuggerModelTypeException( + "Target of attach must be a child of " + impl.session.available.getPath()); + } + long pid; + try { + pid = Long.parseLong(ref.getIndex()); + } + catch (IllegalArgumentException e) { + throw new DebuggerModelNoSuchPathException("Badly-formatted PID", e); + } + return attach(pid); + } + + @Override + public CompletableFuture attach(long pid) { + return inferior.attach(pid).thenApply(__ -> null); + } + + @Override + public CompletableFuture detach() { + return inferior.detach(); + } + + @Override + public CompletableFuture delete() { + return inferior.remove(); + } + + protected CompletableFuture inferiorStarted(Long pid) { + AsyncFence fence = new AsyncFence(); + fence.include(modules.refresh()); + fence.include(registers.refresh()); + fence.include(environment.refresh()); + return fence.ready().thenAccept(__ -> { + if (pid != null) { + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE, // + PID_ATTRIBUTE_NAME, pid, // + DISPLAY_ATTRIBUTE_NAME, updateDisplay() // + ), "Started"); + } + else { + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE, // + DISPLAY_ATTRIBUTE_NAME, updateDisplay() // + ), "Started"); + } + listeners.fire(TargetExecutionStateListener.class) + .executionStateChanged(this, TargetExecutionState.ALIVE); + }); + } + + protected void inferiorExited(Long exitCode) { + if (exitCode != null) { + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, TargetExecutionState.TERMINATED, // + EXIT_CODE_ATTRIBUTE_NAME, exitCode, // + DISPLAY_ATTRIBUTE_NAME, updateDisplay() // + ), "Exited"); + } + else { + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, TargetExecutionState.TERMINATED, // + DISPLAY_ATTRIBUTE_NAME, updateDisplay() // + ), "Exited"); + } + listeners.fire(TargetExecutionStateListener.class) + .executionStateChanged(this, TargetExecutionState.TERMINATED); + } + + protected void updateDisplayAttribute() { + changeAttributes(List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, updateDisplay() // + ), "Display changed"); + } + + protected String updateDisplay() { + if (inferior.getPid() == null) { + return display = String.format("%d - ", inferior.getId()); + } + return display = String.format("%d - %s - %s", inferior.getId(), inferior.getDescriptor(), + inferior.getExecutable()); + } + + @Override + public String getDisplay() { + return display; + } + + protected void invalidateMemoryAndRegisterCaches() { + memory.invalidateMemoryCaches(); + threads.invalidateRegisterCaches(); + } + + protected void updateMemory() { + // This is a little ew. Wish I didn't have to list regions every STOP + memory.update().exceptionally(ex -> { + Msg.error(this, "Could not update process memory mappings", ex); + return null; + }); + } + + @Override + @Internal + public CompletableFuture select() { + return inferior.select(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferiorContainer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferiorContainer.java new file mode 100644 index 0000000000..2541b094ab --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferiorContainer.java @@ -0,0 +1,257 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.gdb.manager.*; +import agent.gdb.manager.reason.*; +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetEventScope.TargetEventScopeListener; +import ghidra.dbg.target.TargetEventScope.TargetEventType; +import ghidra.dbg.target.TargetExecutionStateful; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +public class GdbModelTargetInferiorContainer + extends DefaultTargetObject + implements GdbEventsListenerAdapter { + protected final GdbModelImpl impl; + + protected final Map inferiorsById = new WeakValueHashMap<>(); + + public GdbModelTargetInferiorContainer(GdbModelTargetSession session) { + super(session.impl, session, "Inferiors", "InferiorContainer"); + this.impl = session.impl; + + impl.gdb.addEventsListener(this); + } + + @Override + public void inferiorAdded(GdbInferior inferior, GdbCause cause) { + GdbModelTargetInferior inf = getTargetInferior(inferior); + changeElements(List.of(), List.of(inf), "Added"); + } + + @Override + public void inferiorStarted(GdbInferior inf, GdbCause cause) { + GdbModelTargetInferior inferior = getTargetInferior(inf); + // TODO: Move PROCESS_CREATED here to restore proper order of event reporting + // Pending some client-side changes to handle architecture selection, though. + inferior.inferiorStarted(inf.getPid()).thenAccept(__ -> { + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event( + parent, null, TargetEventType.PROCESS_CREATED, "Inferior " + inf.getId() + + " started " + inf.getExecutable() + " pid=" + inf.getPid(), + List.of(inferior)); + }).exceptionally(ex -> { + Msg.error(this, "Could not notify inferior started", ex); + return null; + }); + } + + @Override + public void inferiorExited(GdbInferior inf, GdbCause cause) { + GdbModelTargetInferior inferior = getTargetInferior(inf); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, null, TargetEventType.PROCESS_EXITED, + "Inferior " + inf.getId() + " exited code=" + inf.getExitCode(), + List.of(inferior)); + inferior.inferiorExited(inf.getExitCode()); + } + + @Override + public void inferiorRemoved(int inferiorId, GdbCause cause) { + synchronized (this) { + inferiorsById.remove(inferiorId); + } + changeElements(List.of(GdbModelTargetInferior.indexInferior(inferiorId)), List.of(), + "Removed"); + } + + protected void gatherThreads(List into, + GdbModelTargetInferior inferior, Collection from) { + for (GdbThread t : from) { + GdbModelTargetThread p = inferior.threads.getTargetThread(t); + if (p != null) { + into.add(p); + } + } + } + + @Override + public void inferiorStateChanged(GdbInferior inf, Collection threads, GdbState state, + GdbThread thread, GdbCause cause, GdbReason reason) { + // Desired order of updates: + // 1. TargetEvent emitted + // 2. Thread states/stacks updated + // 3. Memory regions updated (Ew) + GdbModelTargetInferior inferior = getTargetInferior(inf); + GdbModelTargetThread targetThread = + thread == null ? null : inferior.threads.getTargetThread(thread); + if (state == GdbState.RUNNING) { + inferior.changeAttributes(List.of(), Map.of( // + TargetExecutionStateful.STATE_ATTRIBUTE_NAME, TargetExecutionState.RUNNING // + ), reason.desc()); + List params = new ArrayList<>(); + gatherThreads(params, inferior, threads); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, targetThread, TargetEventType.RUNNING, "Running", params); + } + if (state != GdbState.STOPPED) { + inferior.threads.threadsStateChanged(threads, state, reason); + return; + } + if (reason instanceof GdbBreakpointHitReason) { + GdbBreakpointHitReason bptHit = (GdbBreakpointHitReason) reason; + List params = new ArrayList<>(); + GdbModelTargetBreakpointSpec spec = + parent.breakpoints.getTargetBreakpointSpecIfPresent(bptHit.getBreakpointId()); + if (spec != null) { + params.add(spec); + } + gatherThreads(params, inferior, threads); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, targetThread, TargetEventType.BREAKPOINT_HIT, bptHit.desc(), + params); + } + else if (reason instanceof GdbEndSteppingRangeReason) { + List params = new ArrayList<>(); + gatherThreads(params, inferior, threads); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, targetThread, TargetEventType.STEP_COMPLETED, reason.desc(), + params); + } + else if (reason instanceof GdbSignalReceivedReason) { + GdbSignalReceivedReason signal = (GdbSignalReceivedReason) reason; + List params = new ArrayList<>(); + params.add(signal.getSignalName()); + gatherThreads(params, inferior, threads); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, targetThread, TargetEventType.SIGNAL, reason.desc(), params); + } + else { + List params = new ArrayList<>(); + gatherThreads(params, inferior, threads); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, targetThread, TargetEventType.STOPPED, reason.desc(), params); + } + // This will update stacks of newly-STOPPED threads + inferior.changeAttributes(List.of(), Map.of( // + TargetExecutionStateful.STATE_ATTRIBUTE_NAME, TargetExecutionState.STOPPED // + ), reason.desc()); + inferior.threads.threadsStateChanged(threads, state, reason); + // Ew. I wish I didn't have to, but there doesn't seem to be a "(un)mapped" event + inferior.updateMemory(); + } + + @Override + public void threadCreated(GdbThread thread, GdbCause cause) { + GdbModelTargetInferior inferior = getTargetInferior(thread.getInferior()); + GdbModelTargetThread targetThread = inferior.threads.threadCreated(thread); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, targetThread, TargetEventType.THREAD_CREATED, + "Thread " + thread.getId() + " started", List.of(targetThread)); + } + + @Override + public void threadExited(int threadId, GdbInferior inf, GdbCause cause) { + GdbModelTargetInferior inferior = getTargetInferior(inf); + GdbModelTargetThread targetThread = inferior.threads.getTargetThreadIfPresent(threadId); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, targetThread, TargetEventType.THREAD_EXITED, + "Thread " + threadId + " exited", List.of(targetThread)); + inferior.threads.threadExited(threadId); + } + + @Override + public void libraryLoaded(GdbInferior inf, String name, GdbCause cause) { + GdbModelTargetInferior inferior = getTargetInferior(inf); + GdbModelTargetModule module = inferior.modules.libraryLoaded(name); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, null, TargetEventType.MODULE_LOADED, "Library " + name + " loaded", + List.of(module)); + } + + @Override + public void libraryUnloaded(GdbInferior inf, String name, GdbCause cause) { + GdbModelTargetInferior inferior = getTargetInferior(inf); + GdbModelTargetModule module = inferior.modules.getTargetModuleIfPresent(name); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, null, TargetEventType.MODULE_UNLOADED, + "Library " + name + " unloaded", List.of(module)); + inferior.modules.libraryUnloaded(name); + } + + private void updateUsingInferiors(Map byIID) { + List inferiors; + synchronized (this) { + inferiors = + byIID.values().stream().map(this::getTargetInferior).collect(Collectors.toList()); + } + setElements(inferiors, "Refreshed"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (!refresh) { + updateUsingInferiors(impl.gdb.getKnownInferiors()); + return AsyncUtils.NIL; + } + return impl.gdb.listInferiors().thenAccept(this::updateUsingInferiors); + } + + // NOTE: Does no good to override fetchElement + // Cache should be kept in sync all the time, anyway + + public synchronized GdbModelTargetInferior getTargetInferior(int id) { + return inferiorsById.computeIfAbsent(id, + i -> new GdbModelTargetInferior(this, impl.gdb.getKnownInferiors().get(id))); + } + + public synchronized GdbModelTargetInferior getTargetInferior(GdbInferior inferior) { + GdbModelTargetInferior modelInferior = inferiorsById.get(inferior.getId()); + if (modelInferior != null) { + modelInferior.updateDisplayAttribute(); + } + else { + modelInferior = new GdbModelTargetInferior(this, inferior); + inferiorsById.put(inferior.getId(), modelInferior); + } + return modelInferior; + } + + protected void invalidateMemoryAndRegisterCaches() { + for (GdbModelTargetInferior inf : inferiorsById.values()) { + inf.invalidateMemoryAndRegisterCaches(); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetMemoryRegion.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetMemoryRegion.java new file mode 100644 index 0000000000..8dbd00fb9d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetMemoryRegion.java @@ -0,0 +1,115 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.gdb.manager.impl.GdbMemoryMapping; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.target.*; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.*; + +public class GdbModelTargetMemoryRegion + extends DefaultTargetObject + implements TargetMemoryRegion { + protected static final String OBJFILE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "objfile"; + protected static final String OFFSET_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "offset"; + + protected static String indexRegion(GdbMemoryMapping mapping) { + return mapping.getStart().toString(16); + } + + protected static String keyRegion(GdbMemoryMapping mapping) { + return PathUtils.makeKey(indexRegion(mapping)); + } + + protected static String computeDisplay(GdbMemoryMapping mapping) { + // NOTE: This deviates from GDB's table display, as it'd be confusing in isolation + if (mapping.getObjfile() == null || mapping.getObjfile().length() == 0) { + return String.format("[0x%x-0x%x] (no file)", mapping.getStart(), mapping.getEnd()); + } + return String.format("[0x%x-0x%x] %s(0x%x)", mapping.getStart(), mapping.getEnd(), + mapping.getObjfile(), mapping.getOffset()); + } + + protected AddressRangeImpl range; + protected final String display; + + public GdbModelTargetMemoryRegion(GdbModelTargetProcessMemory memory, + GdbMemoryMapping mapping) { + super(memory.impl, memory, keyRegion(mapping), "MemoryRegion"); + try { + Address min = memory.impl.getAddressFactory() + .getDefaultAddressSpace() + .getAddress(mapping.getStart().toString(16)); + this.range = new AddressRangeImpl(min, mapping.getSize().longValue()); + } + catch (AddressFormatException | AddressOverflowException e) { + throw new AssertionError(e); + } + changeAttributes(List.of(), Map.of(MEMORY_ATTRIBUTE_NAME, memory, // + RANGE_ATTRIBUTE_NAME, range, // + READABLE_ATTRIBUTE_NAME, isReadable(), // + WRITABLE_ATTRIBUTE_NAME, isWritable(), // + EXECUTABLE_ATTRIBUTE_NAME, isExecutable(), // + OBJFILE_ATTRIBUTE_NAME, mapping.getObjfile(), // + OFFSET_ATTRIBUTE_NAME, mapping.getOffset().longValue(), // + DISPLAY_ATTRIBUTE_NAME, display = computeDisplay(mapping), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + protected boolean isSame(GdbMemoryMapping mapping) { + // Hacky, but effective + // TODO: Check/update permissions? + return display.equals(computeDisplay(mapping)); + } + + @Override + public AddressRange getRange() { + return range; + } + + @Override + public String getDisplay() { + return display; + } + + @Override + public TypedTargetObjectRef> getMemory() { + return parent; + } + + @Override + public boolean isReadable() { + // It can be done if debugging locally on Linux, by reading /proc/[PID]/maps + // The sections listing will give the initial protections. + return true; // TODO + } + + @Override + public boolean isWritable() { + return true; // TODO + } + + @Override + public boolean isExecutable() { + return true; // TODO + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetModule.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetModule.java new file mode 100644 index 0000000000..c9f26a2e21 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetModule.java @@ -0,0 +1,95 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.GdbModule; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.*; +import ghidra.util.Msg; + +public class GdbModelTargetModule + extends DefaultTargetObject + implements TargetModule { + protected static String indexModule(GdbModule module) { + return module.getName(); + } + + protected static String keyModule(GdbModule module) { + return PathUtils.makeKey(indexModule(module)); + } + + protected final GdbModelImpl impl; + protected final GdbInferior inferior; + protected final GdbModule module; + + protected final GdbModelTargetSectionContainer sections; + protected final GdbModelTargetSymbolContainer symbols; + // TODO Types? See GDB's ptype, but that'll require some C parsing + + public GdbModelTargetModule(GdbModelTargetModuleContainer modules, GdbModule module) { + super(modules.impl, modules, keyModule(module), "Module"); + this.impl = modules.impl; + this.inferior = modules.inferior; + this.module = module; + + this.sections = new GdbModelTargetSectionContainer(this); + this.symbols = new GdbModelTargetSymbolContainer(this); + + changeAttributes(List.of(), + Map.of(sections.getName(), sections, symbols.getName(), symbols, + MODULE_NAME_ATTRIBUTE_NAME, module.getName(), UPDATE_MODE_ATTRIBUTE_NAME, + TargetUpdateMode.FIXED, DISPLAY_ATTRIBUTE_NAME, module.getName() // + ), "Initialized"); + } + + public CompletableFuture init() { + return sections.requestElements(true).exceptionally(ex -> { + Msg.error(this, "Could not initialize module sections and base", ex); + return null; + }); + } + + @Override + public String getDisplay() { + return module.getName(); + } + + protected AddressRange doGetRange() { + Long base = module.getKnownBase(); + Long max = module.getKnownMax(); + max = max == null ? base : max - 1; // GDB gives end+1 + if (base == null) { + Address addr = impl.space.getMinAddress(); + return new AddressRangeImpl(addr, addr); + } + return new AddressRangeImpl(impl.space.getAddress(base), impl.space.getAddress(max)); + } + + public void sectionsRefreshed() { + AddressRange range = doGetRange(); + changeAttributes(List.of(), Map.of(RANGE_ATTRIBUTE_NAME, range, // + VISIBLE_RANGE_ATTRIBUTE_NAME, range // + ), "Sections Refreshed"); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetModuleContainer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetModuleContainer.java new file mode 100644 index 0000000000..8955240019 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetModuleContainer.java @@ -0,0 +1,124 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.GdbModule; +import ghidra.async.AsyncFence; +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetModuleContainer; +import ghidra.lifecycle.Internal; +import ghidra.util.Msg; + +public class GdbModelTargetModuleContainer + extends DefaultTargetObject + implements TargetModuleContainer { + // NOTE: -file-list-shared-libraries omits the main module and system-supplied DSO. + + protected final GdbModelImpl impl; + protected final GdbInferior inferior; + + // TODO: Is it possible to load the same object twice? + protected final Map modulesByName = new HashMap<>(); + + public GdbModelTargetModuleContainer(GdbModelTargetInferior inferior) { + super(inferior.impl, inferior, "Modules", "ModuleContainer"); + this.impl = inferior.impl; + this.inferior = inferior.inferior; + } + + @Internal + public GdbModelTargetModule libraryLoaded(String name) { + GdbModule mod = Objects.requireNonNull(inferior.getKnownModules().get(name)); + GdbModelTargetModule module = getTargetModule(mod); + changeElements(List.of(), List.of(module), "Loaded"); + return module; + } + + @Internal + public void libraryUnloaded(String name) { + synchronized (this) { + modulesByName.remove(name); + } + changeElements(List.of(name), List.of(), "Unloaded"); + } + + @Override + public boolean supportsSyntheticModules() { + return false; + } + + @Override + public CompletableFuture> addSyntheticModule(String name) { + throw new DebuggerUserException("GDB Does not support synthetic modules"); + } + + protected CompletableFuture updateUsingModules(Map byName) { + List modules; + synchronized (this) { + modules = byName.values() + .stream() + .map(this::getTargetModule) + .collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (GdbModelTargetModule mod : modules) { + fence.include(mod.init()); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), modules, "Refreshed"); + }); + } + + @Override + protected CompletableFuture requestElements(boolean refresh) { + // Ignore 'refresh' because inferior.getKnownModules may exclude executable + return doRefresh(); + } + + protected CompletableFuture doRefresh() { + return inferior.listModules().thenCompose(byName -> { + modulesByName.keySet().retainAll(byName.keySet()); + return updateUsingModules(byName); + }); + } + + protected synchronized GdbModelTargetModule getTargetModule(GdbModule module) { + return modulesByName.computeIfAbsent(module.getName(), + n -> new GdbModelTargetModule(this, module)); + } + + public synchronized GdbModelTargetModule getTargetModuleIfPresent(String name) { + return modulesByName.get(name); + } + + public CompletableFuture refresh() { + if (!isObserved()) { + return AsyncUtils.NIL; + } + return doRefresh().exceptionally(ex -> { + Msg.error(this, "Problem refreshing inferior's modules", ex); + return null; + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetProcessMemory.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetProcessMemory.java new file mode 100644 index 0000000000..604faf721e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetProcessMemory.java @@ -0,0 +1,141 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import com.google.common.collect.Range; + +import agent.gdb.manager.GdbInferior; +import agent.gdb.manager.impl.GdbMemoryMapping; +import agent.gdb.manager.impl.cmd.GdbCommandError; +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.error.DebuggerMemoryAccessException; +import ghidra.dbg.target.TargetMemory; +import ghidra.program.model.address.*; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +public class GdbModelTargetProcessMemory + extends DefaultTargetObject + implements TargetMemory { + + protected final GdbModelImpl impl; + protected final GdbInferior inferior; + + protected final Map regionsByStart = + new WeakValueHashMap<>(); + + public GdbModelTargetProcessMemory(GdbModelTargetInferior inferior) { + super(inferior.impl, inferior, "Memory", "ProcessMemory"); + this.impl = inferior.impl; + this.inferior = inferior.inferior; + } + + protected void updateUsingMappings(Map byStart) { + List regions; + synchronized (this) { + regions = + byStart.values().stream().map(this::getTargetRegion).collect(Collectors.toList()); + } + setElements(regions, "Refreshed"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + // Can't use refresh getKnownMappings is only populated by listMappings + if (inferior.getPid() == null) { + setElements(List.of(), "Refreshed (while no process)"); + return AsyncUtils.NIL; + } + return inferior.listMappings().thenAccept(this::updateUsingMappings); + } + + protected synchronized GdbModelTargetMemoryRegion getTargetRegion(GdbMemoryMapping mapping) { + GdbModelTargetMemoryRegion region = regionsByStart.get(mapping.getStart()); + if (region != null && region.isSame(mapping)) { + return region; + } + region = new GdbModelTargetMemoryRegion(this, mapping); + regionsByStart.put(mapping.getStart(), region); + return region; + } + + @Override + public CompletableFuture readMemory(Address address, int length) { + ByteBuffer buf = ByteBuffer.allocate(length); + long offset = address.getOffset(); + AddressRange range; + try { + range = new AddressRangeImpl(address, length); + } + catch (AddressOverflowException e) { + throw new IllegalArgumentException("address,length", e); + } + return inferior.readMemory(offset, buf).thenApply(set -> { + Range r = set.rangeContaining(offset); + if (r == null) { + throw new DebuggerMemoryAccessException("Cannot read at " + address); + } + byte[] content = + Arrays.copyOf(buf.array(), (int) (r.upperEndpoint() - r.lowerEndpoint())); + listeners.fire(TargetMemoryListener.class).memoryUpdated(this, address, content); + return content; + }).exceptionally(e -> { + e = AsyncUtils.unwrapThrowable(e); + if (e instanceof GdbCommandError) { + GdbCommandError gce = (GdbCommandError) e; + e = new DebuggerMemoryAccessException( + "Cannot read at " + address + ": " + gce.getInfo().getString("msg")); + listeners.fire(TargetMemoryListener.class) + .memoryReadError(this, range, (DebuggerMemoryAccessException) e); + } + if (e instanceof DebuggerMemoryAccessException) { + listeners.fire(TargetMemoryListener.class) + .memoryReadError(this, range, (DebuggerMemoryAccessException) e); + } + return ExceptionUtils.rethrow(e); + }); + } + + @Override + public CompletableFuture writeMemory(Address address, byte[] data) { + return inferior.writeMemory(address.getOffset(), ByteBuffer.wrap(data)).thenAccept(__ -> { + listeners.fire(TargetMemoryListener.class).memoryUpdated(this, address, data); + }); + } + + protected void invalidateMemoryCaches() { + listeners.fire.invalidateCacheRequested(this); + } + + protected CompletableFuture update() { + if (!isObserved()) { + return AsyncUtils.NIL; + } + return fetchElements(true).exceptionally(e -> { + Msg.error(this, "Could not update memory regions " + this + " on STOPPED"); + return null; + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetRegister.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetRegister.java new file mode 100644 index 0000000000..b17c7e2458 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetRegister.java @@ -0,0 +1,72 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.gdb.manager.GdbRegister; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetRegister; +import ghidra.dbg.util.PathUtils; + +public class GdbModelTargetRegister + extends DefaultTargetObject + implements TargetRegister { + + protected static String indexRegister(GdbRegister register) { + String name = register.getName(); + if ("".equals(name)) { + return "UNNAMED," + register.getNumber(); + } + return name; + } + + protected static String keyRegister(GdbRegister register) { + return PathUtils.makeKey(indexRegister(register)); + } + + protected final GdbModelImpl impl; + protected final GdbRegister register; + + protected final int bitLength; + + public GdbModelTargetRegister(GdbModelTargetRegisterContainer registers, GdbRegister register) { + super(registers.impl, registers, keyRegister(register), "Register"); + this.impl = registers.impl; + this.register = register; + + this.bitLength = register.getSize() * 8; + + changeAttributes(List.of(), Map.of( + CONTAINER_ATTRIBUTE_NAME, registers, + LENGTH_ATTRIBUTE_NAME, bitLength, + DISPLAY_ATTRIBUTE_NAME, register.getName(), + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED), + "Initialized"); + } + + @Override + public int getBitLength() { + return bitLength; + } + + @Override + public String getDisplay() { + return register.getName(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetRegisterContainer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetRegisterContainer.java new file mode 100644 index 0000000000..e2315a31d2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetRegisterContainer.java @@ -0,0 +1,91 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.gdb.manager.*; +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetRegisterContainer; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +public class GdbModelTargetRegisterContainer + extends DefaultTargetObject + implements TargetRegisterContainer { + + protected final GdbModelImpl impl; + protected final GdbInferior inferior; + + protected final Map registersByNumber = + new WeakValueHashMap<>(); + + public GdbModelTargetRegisterContainer(GdbModelTargetInferior inferior) { + super(inferior.impl, inferior, "Registers", "RegisterContainer"); + this.impl = inferior.impl; + this.inferior = inferior.inferior; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (!refresh) { + return completeUsingThreads(inferior.getKnownThreads()); + } + return doRefresh(); + } + + protected CompletableFuture doRefresh() { + return inferior.listThreads().thenCompose(this::completeUsingThreads); + } + + protected CompletableFuture completeUsingThreads(Map byId) { + if (byId.isEmpty()) { + setElements(List.of(), "Refreshed (with no thread)"); + return AsyncUtils.NIL; + } + GdbThread thread = byId.values().iterator().next(); + return thread.listRegisters().thenAccept(regs -> { + List registers; + synchronized (this) { // calls getTargetRegister + // No stale garbage. New architecture may re-use numbers, so clear cache out! + registersByNumber.clear(); + registers = regs.stream().map(this::getTargetRegister).collect(Collectors.toList()); + } + // TODO: Equality only considers paths, i.e., name. If a name is re-used, the old + // stuff has to go. Not sure how to accomplish that, yet. + setElements(registers, "Refreshed"); + }); + } + + protected synchronized GdbModelTargetRegister getTargetRegister(GdbRegister register) { + return registersByNumber.computeIfAbsent(register.getNumber(), + n -> new GdbModelTargetRegister(this, register)); + } + + public CompletableFuture refresh() { + if (!isObserved()) { + return AsyncUtils.NIL; + } + return doRefresh().exceptionally(ex -> { + Msg.error(this, "Problem refreshing inferior's register descriptions", ex); + return null; + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSection.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSection.java new file mode 100644 index 0000000000..325ac7c272 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSection.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.gdb.manager.GdbModuleSection; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.target.*; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.*; + +public class GdbModelTargetSection + extends DefaultTargetObject + implements TargetSection { + protected static String indexSection(GdbModuleSection section) { + return section.getName(); + } + + protected static String keySection(GdbModuleSection section) { + return PathUtils.makeKey(indexSection(section)); + } + + protected final GdbModelImpl impl; + protected final GdbModuleSection section; + + protected final GdbModelTargetModule module; + protected final AddressRange range; + + public GdbModelTargetSection(GdbModelTargetSectionContainer sections, + GdbModelTargetModule module, GdbModuleSection section) { + super(sections.impl, sections, keySection(section), "Section"); + this.impl = sections.impl; + this.section = section; + + this.module = module; + this.range = doGetRange(); + this.changeAttributes(List.of(), + Map.of(MODULE_ATTRIBUTE_NAME, module, RANGE_ATTRIBUTE_NAME, range, + VISIBLE_RANGE_ATTRIBUTE_NAME, range, DISPLAY_ATTRIBUTE_NAME, section.getName(), + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public TypedTargetObjectRef> getModule() { + return module; + } + + protected AddressRange doGetRange() { + if (section.getVmaStart() == section.getVmaEnd()) { + return null; // zero-length range + } + Address min = impl.space.getAddress(section.getVmaStart()); + // Ghidra ranges are inclusive at the end. GDB's are not. + Address max = impl.space.getAddress(section.getVmaEnd() - 1); + return new AddressRangeImpl(min, max); + } + + @Override + public AddressRange getRange() { + return range; + } + + @Override + public String getDisplay() { + return section.getName(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSectionContainer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSectionContainer.java new file mode 100644 index 0000000000..2393e010f9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSectionContainer.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.gdb.manager.GdbModuleSection; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.util.datastruct.WeakValueHashMap; + +public class GdbModelTargetSectionContainer + extends DefaultTargetObject { + protected final GdbModelImpl impl; + protected final GdbModelTargetModule module; + + protected final Map sectionsByName = new WeakValueHashMap<>(); + + public GdbModelTargetSectionContainer(GdbModelTargetModule module) { + super(module.impl, module, "Sections", "SectionContainer"); + this.impl = module.impl; + this.module = module; + } + + protected void updateUsingSections(Map byName) { + List sections; + synchronized (this) { + sections = byName.values() + .stream() + .filter(s -> s.getAttributes().contains("ALLOC")) + .map(this::getTargetSection) + .collect(Collectors.toList()); + } + setElements(sections, "Refreshed"); + changeAttributes(List.of(), Map.of( + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Refreshed"); + parent.sectionsRefreshed(); // recompute base + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + // getKnownSections is not guaranteed to be populated + // listSections is cached by manager, so just use it always + return module.module.listSections().thenAccept(this::updateUsingSections); + } + + protected synchronized GdbModelTargetSection getTargetSection(String name) { + return sectionsByName.computeIfAbsent(name, + n -> new GdbModelTargetSection(this, module, + module.module.getKnownSections().get(name))); + } + + protected synchronized GdbModelTargetSection getTargetSection(GdbModuleSection section) { + return sectionsByName.computeIfAbsent(section.getName(), + n -> new GdbModelTargetSection(this, module, section)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSession.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSession.java new file mode 100644 index 0000000000..0e66deb92d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSession.java @@ -0,0 +1,260 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.*; +import ghidra.async.AsyncUtils; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.agent.AbstractTargetObject; +import ghidra.dbg.agent.DefaultTargetModelRoot; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.util.PathUtils; +import ghidra.util.Msg; + +public class GdbModelTargetSession extends DefaultTargetModelRoot implements // + TargetAccessConditioned, + TargetAttacher, + TargetFocusScope, + TargetInterpreter, + TargetInterruptible, + TargetCmdLineLauncher, + TargetEventScope, + GdbConsoleOutputListener, + GdbEventsListenerAdapter { + protected static final String GDB_PROMPT = "(gdb)"; + + protected final GdbModelImpl impl; + protected String display = "GNU gdb (GDB)"; + + protected final GdbModelTargetInferiorContainer inferiors; + protected final GdbModelTargetAvailableContainer available; + protected final GdbModelTargetBreakpointContainer breakpoints; + + private TargetAccessibility accessibility = TargetAccessibility.ACCESSIBLE; + protected GdbModelSelectableObject focus; + + protected String debugger = "gdb"; // Used by GdbModelTargetEnvironment + + public GdbModelTargetSession(GdbModelImpl impl) { + super(impl, "Session"); + this.impl = impl; + + this.inferiors = new GdbModelTargetInferiorContainer(this); + this.available = new GdbModelTargetAvailableContainer(this); + this.breakpoints = new GdbModelTargetBreakpointContainer(this); + + changeAttributes(List.of(), Map.of( // + inferiors.getName(), inferiors, // + available.getName(), available, // + breakpoints.getName(), breakpoints, // + ACCESSIBLE_ATTRIBUTE_NAME, accessibility == TargetAccessibility.ACCESSIBLE, // + PROMPT_ATTRIBUTE_NAME, GDB_PROMPT, // + DISPLAY_ATTRIBUTE_NAME, display, // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, TargetCmdLineLauncher.PARAMETERS, // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + impl.gdb.addEventsListener(this); + impl.gdb.addConsoleOutputListener(this); + + getVersion(); + } + + protected void getVersion() { + impl.gdb.waitForPrompt().thenCompose(__ -> { + return impl.gdb.consoleCapture("show version"); + }).thenAccept(out -> { + debugger = out; + changeAttributes(List.of(), + Map.of(DISPLAY_ATTRIBUTE_NAME, display = out.split("\n")[0].strip() // + ), "Version refreshed"); + }).exceptionally(e -> { + Msg.error(this, "Could not get GDB version", e); + debugger = "gdb"; + return null; + }); + } + + @Override + public String getDisplay() { + return display; + } + + @Override + public void output(GdbManager.Channel gdbChannel, String out) { + TargetConsole.Channel dbgChannel; + switch (gdbChannel) { + case STDOUT: + dbgChannel = TargetConsole.Channel.STDOUT; + break; + case STDERR: + dbgChannel = TargetConsole.Channel.STDERR; + break; + default: + throw new AssertionError(); + } + listeners.fire(TargetInterpreterListener.class).consoleOutput(this, dbgChannel, out); + + } + + @Override + public void inferiorSelected(GdbInferior inferior, GdbCause cause) { + if (inferior.getKnownThreads().isEmpty()) { + GdbModelTargetInferior inf = inferiors.getTargetInferior(inferior); + setFocus(inf); + } + // Otherwise, we'll presumably get the =thread-selected event + } + + @Override + public void threadSelected(GdbThread thread, GdbStackFrame frame, GdbCause cause) { + GdbModelTargetInferior inf = inferiors.getTargetInferior(thread.getInferior()); + GdbModelTargetThread t = inf.threads.getTargetThread(thread); + if (frame == null) { + setFocus(t); + return; + } + GdbModelTargetStackFrame f = t.stack.getTargetFrame(frame); + setFocus(f); + } + + public void setAccessibility(TargetAccessibility accessibility) { + synchronized (attributes) { + if (this.accessibility == accessibility) { + return; + } + this.accessibility = accessibility; + changeAttributes(List.of(), Map.of( // + ACCESSIBLE_ATTRIBUTE_NAME, accessibility == TargetAccessibility.ACCESSIBLE // + ), "Accessibility changed"); + } + listeners.fire(TargetAccessibilityListener.class).accessibilityChanged(this, accessibility); + } + + @Override + public TargetAccessibility getAccessibility() { + return accessibility; + } + + @Override + public CompletableFuture launch(List args) { + // TODO: Find first unused inferior? + return impl.gdb.addInferior().thenCompose(inf -> { + return GdbModelImplUtils.launch(impl, inf, args); + }); + } + + @Override + public CompletableFuture attach(TypedTargetObjectRef> ref) { + getModel().assertMine(TargetObjectRef.class, ref); + List tPath = ref.getPath(); + return impl.fetchModelObject(tPath).thenCompose(obj -> { + GdbModelTargetAttachable attachable = (GdbModelTargetAttachable) DebuggerObjectModel + .requireIface(TargetAttachable.class, obj, tPath); + // TODO: Find first unused inferior? + return impl.gdb.addInferior().thenCompose(inf -> { + return inf.attach(attachable.pid).thenApply(__ -> null); + }); + }); + } + + @Override + public CompletableFuture attach(long pid) { + // TODO: Find first unused inferior? + return impl.gdb.addInferior().thenCompose(inf -> { + return inf.attach(pid).thenApply(__ -> null); + }); + } + + @Override + public CompletableFuture interrupt() { + try { + impl.gdb.sendInterruptNow(); + } + catch (IOException e) { + Msg.error(this, "Could not interrupt", e); + } + return AsyncUtils.NIL; + } + + @Override + public CompletableFuture execute(String cmd) { + return impl.gdb.console(cmd).exceptionally(GdbModelImpl::translateEx); + } + + @Override + public CompletableFuture executeCapture(String cmd) { + return impl.gdb.consoleCapture(cmd).exceptionally(GdbModelImpl::translateEx); + } + + @Override + public CompletableFuture requestFocus(TargetObjectRef ref) { + impl.assertMine(TargetObjectRef.class, ref); + /** + * Yes, this is pointless, since I'm the root, but do it right (TM), since this may change + * or be used as an example for other implementations. + */ + if (!PathUtils.isAncestor(this.getPath(), ref.getPath())) { + throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope"); + } + return ref.fetch().thenCompose(obj -> { + TargetObject cur = obj; + while (cur != null) { + if (cur instanceof GdbModelSelectableObject) { + GdbModelSelectableObject sel = (GdbModelSelectableObject) cur; + return sel.select(); + } + if (cur instanceof AbstractTargetObject) { + AbstractTargetObject def = (AbstractTargetObject) cur; + cur = def.getImplParent(); + continue; + } + throw new AssertionError(); + } + return AsyncUtils.NIL; + }); + } + + protected void invalidateMemoryAndRegisterCaches() { + inferiors.invalidateMemoryAndRegisterCaches(); + } + + protected void setFocus(GdbModelSelectableObject sel) { + boolean doFire; + synchronized (this) { + doFire = !Objects.equals(this.focus, sel); + this.focus = sel; + } + if (doFire) { + changeAttributes(List.of(), Map.of( // + FOCUS_ATTRIBUTE_NAME, focus // + ), "Focus changed"); + listeners.fire(TargetFocusScopeListener.class).focusChanged(this, sel); + } + } + + @Override + public GdbModelSelectableObject getFocus() { + return focus; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStack.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStack.java new file mode 100644 index 0000000000..b10095117d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStack.java @@ -0,0 +1,91 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.gdb.manager.GdbStackFrame; +import agent.gdb.manager.GdbThread; +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetStack; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +public class GdbModelTargetStack + extends DefaultTargetObject + implements TargetStack { + protected final GdbModelImpl impl; + protected final GdbModelTargetInferior inferior; + protected final GdbThread thread; + + protected final Map framesByLevel = new WeakValueHashMap<>(); + + public GdbModelTargetStack(GdbModelTargetThread thread, GdbModelTargetInferior inferior) { + super(thread.impl, thread, "Stack", "Stack"); + this.impl = thread.impl; + this.inferior = inferior; + this.thread = thread.thread; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return thread.listStackFrames().thenAccept(f -> { + List frames; + synchronized (this) { + frames = f.stream().map(this::getTargetFrame).collect(Collectors.toList()); + } + // TODO: This might be a case where "move" is useful + setElements(frames, "Refreshed"); + }); + } + + protected synchronized GdbModelTargetStackFrame getTargetFrame(GdbStackFrame frame) { + return framesByLevel.compute(frame.getLevel(), (l, f) -> { + if (f == null) { + return new GdbModelTargetStackFrame(this, parent, inferior, frame); + } + f.setFrame(frame); + return f; + }); + } + + protected void invalidateRegisterCaches() { + for (GdbModelTargetStackFrame frame : framesByLevel.values()) { + frame.invalidateRegisterCaches(); + } + } + + /** + * Re-fetch the stack frames, generating events for updates + * + *

+ * GDB doesn't produce stack change events, but they should only ever happen by running a + * target. Thus, every time we're STOPPED, this method should be called. + */ + protected CompletableFuture update() { + if (!isObserved()) { + return AsyncUtils.NIL; + } + return fetchElements(true).exceptionally(e -> { + Msg.error(this, "Could not update stack " + this + " on STOPPED"); + return null; + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrame.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrame.java new file mode 100644 index 0000000000..3d887bd106 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrame.java @@ -0,0 +1,175 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.math.BigInteger; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.*; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.error.DebuggerRegisterAccessException; +import ghidra.dbg.target.*; +import ghidra.dbg.util.ConversionUtils; +import ghidra.dbg.util.PathUtils; +import ghidra.lifecycle.Internal; +import ghidra.program.model.address.Address; +import ghidra.util.Msg; + +public class GdbModelTargetStackFrame extends DefaultTargetObject + implements TargetStackFrame, + TargetRegisterBank, GdbModelSelectableObject { + public static final String FUNC_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "function"; + public static final String FROM_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "from"; // TODO + + protected static String indexFrame(GdbStackFrame frame) { + return PathUtils.makeIndex(frame.getLevel()); + } + + protected static String keyFrame(GdbStackFrame frame) { + return PathUtils.makeKey(indexFrame(frame)); + } + + protected static String computeDisplay(GdbStackFrame frame) { + // TODO: Alternative formats when function is not known? + return String.format("#%d 0x%s in %s ()", frame.getLevel(), frame.getAddress().toString(16), + frame.getFunction()); + } + + protected final GdbModelImpl impl; + protected final GdbModelTargetThread thread; + protected final GdbModelTargetInferior inferior; + + private Set lastRead; + + protected GdbStackFrame frame; + protected Address pc; + protected String func; + protected String display; + + private GdbModelTargetStackFrameRegisterContainer registers; + + public GdbModelTargetStackFrame(GdbModelTargetStack stack, GdbModelTargetThread thread, + GdbModelTargetInferior inferior, GdbStackFrame frame) { + super(stack.impl, stack, keyFrame(frame), "StackFrame"); + this.impl = stack.impl; + this.thread = thread; + this.inferior = inferior; + + this.registers = new GdbModelTargetStackFrameRegisterContainer(this); + + changeAttributes(List.of(), List.of( // + registers // + ), Map.of( // + DESCRIPTIONS_ATTRIBUTE_NAME, getDescriptions(), // + DISPLAY_ATTRIBUTE_NAME, display = computeDisplay(frame), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + setFrame(frame); + } + + @Override + public GdbModelTargetRegisterContainer getDescriptions() { + return inferior.registers; + } + + @Override + public CompletableFuture> readRegistersNamed( + Collection names) { + return inferior.registers.fetchElements().thenCompose(regs -> { + Set toRead = new LinkedHashSet<>(); + for (String regname : names) { + GdbModelTargetRegister reg = regs.get(regname); + if (reg == null) { + throw new DebuggerRegisterAccessException("No such register: " + regname); + } + toRead.add(reg.register); + } + this.lastRead = toRead; + return frame.readRegisters(toRead); + }).thenApply(vals -> { + Map result = new LinkedHashMap<>(); + Map values = new LinkedHashMap<>(); + for (Map.Entry ent : vals.entrySet()) { + GdbRegister reg = ent.getKey(); + String regName = reg.getName(); + BigInteger val = ent.getValue(); + if (val == null) { + Msg.warn(this, "Register " + regName + " value came back null."); + continue; + } + byte[] bytes = ConversionUtils.bigIntegerToBytes(reg.getSize(), val); + values.put(reg, val); + result.put(regName, bytes); + } + registers.setValues(values); + changeAttributes(List.of(), List.of( // + registers // + ), Map.of(), "Refreshed"); + listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, result); + return result; + }); + } + + @Override + public CompletableFuture writeRegistersNamed(Map values) { + return inferior.registers.fetchElements().thenCompose(regs -> { + Map toWrite = new LinkedHashMap<>(); + for (Map.Entry ent : values.entrySet()) { + String regname = ent.getKey(); + GdbModelTargetRegister reg = regs.get(regname); + if (reg == null) { + throw new DebuggerRegisterAccessException("No such register: " + regname); + } + BigInteger val = new BigInteger(1, ent.getValue()); + toWrite.put(reg.register, val); + } + return frame.writeRegisters(toWrite); + }).thenAccept(__ -> { + listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, values); + }); + } + + protected void setFrame(GdbStackFrame frame) { + this.pc = impl.space.getAddress(frame.getAddress().longValue()); + this.func = frame.getFunction(); + // TODO: module? "from" + this.frame = frame; + + changeAttributes(List.of(), List.of( // + registers // + ), Map.of( // + PC_ATTRIBUTE_NAME, pc, // + FUNC_ATTRIBUTE_NAME, func, // + DISPLAY_ATTRIBUTE_NAME, display = computeDisplay(frame) // + ), "Refreshed"); + } + + protected void invalidateRegisterCaches() { + listeners.fire.invalidateCacheRequested(this); + } + + @Override + @Internal + public CompletableFuture select() { + return frame.select(); + } + + public CompletableFuture listRegisters() { + GdbRegisterSet set = new GdbRegisterSet(lastRead); + return CompletableFuture.completedFuture(set); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrameRegister.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrameRegister.java new file mode 100644 index 0000000000..b6ad446be1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrameRegister.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +import agent.gdb.manager.GdbRegister; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetRegister; +import ghidra.dbg.util.PathUtils; + +public class GdbModelTargetStackFrameRegister + extends DefaultTargetObject + implements TargetRegister { + + protected static String indexRegister(GdbRegister register) { + String name = register.getName(); + if ("".equals(name)) { + return "UNNAMED," + register.getNumber(); + } + return name; + } + + protected static String keyRegister(GdbRegister register) { + return PathUtils.makeKey(indexRegister(register)); + } + + protected final GdbModelImpl impl; + protected final GdbRegister register; + + protected final int bitLength; + private BigInteger value; + + public GdbModelTargetStackFrameRegister(GdbModelTargetStackFrameRegisterContainer registers, + GdbRegister register) { + super(registers.impl, registers, keyRegister(register), "Register"); + this.impl = registers.impl; + this.register = register; + + this.bitLength = register.getSize() * 8; + + changeAttributes(List.of(), Map.of( // + CONTAINER_ATTRIBUTE_NAME, registers, // + LENGTH_ATTRIBUTE_NAME, bitLength, // + DISPLAY_ATTRIBUTE_NAME, register.getName(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public int getBitLength() { + return bitLength; + } + + @Override + public String getDisplay() { + return getCachedAttribute(DISPLAY_ATTRIBUTE_NAME).toString(); + } + + public void setModified(boolean modified) { + changeAttributes(List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, modified // + ), "Refreshed"); + if (modified) { + listeners.fire.displayChanged(this, getDisplay()); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrameRegisterContainer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrameRegisterContainer.java new file mode 100644 index 0000000000..677e170dc5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetStackFrameRegisterContainer.java @@ -0,0 +1,108 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.gdb.manager.GdbRegister; +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetRegisterContainer; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +public class GdbModelTargetStackFrameRegisterContainer + extends DefaultTargetObject + implements TargetRegisterContainer { + + protected final GdbModelImpl impl; + protected final GdbModelTargetStackFrame frame; + protected final GdbModelTargetThread thread; + + protected final Map registersByNumber = + new WeakValueHashMap<>(); + + public GdbModelTargetStackFrameRegisterContainer(GdbModelTargetStackFrame frame) { + super(frame.impl, frame, "Registers", "StackFrameRegisterContainer"); + this.impl = frame.impl; + this.frame = frame; + this.thread = frame.thread; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return doRefresh(); + } + + protected CompletableFuture doRefresh() { + return completeUsingThread(); + } + + protected CompletableFuture completeUsingThread() { + return frame.listRegisters().thenAccept(regs -> { + List registers; + synchronized (this) { // calls getTargetRegister + // No stale garbage. New architecture may re-use numbers, so clear cache out! + registersByNumber.clear(); + registers = regs.stream().map(this::getTargetRegister).collect(Collectors.toList()); + } + // TODO: Equality only considers paths, i.e., name. If a name is re-used, the old + // stuff has to go. Not sure how to accomplish that, yet. + setElements(registers, "Refreshed"); + }); + } + + protected synchronized GdbModelTargetStackFrameRegister getTargetRegister( + GdbRegister register) { + return registersByNumber.computeIfAbsent(register.getNumber(), + n -> new GdbModelTargetStackFrameRegister(this, register)); + } + + public CompletableFuture refresh() { + if (!isObserved()) { + return AsyncUtils.NIL; + } + return doRefresh().exceptionally(ex -> { + Msg.error(this, "Problem refreshing frame's register descriptions", ex); + return null; + }); + } + + public void setValues(Map values) { + for (GdbRegister gdbreg : values.keySet()) { + GdbModelTargetStackFrameRegister reg = registersByNumber.get(gdbreg.getNumber()); + if (reg == null) { + return; + } + String value = values.get(gdbreg).toString(16); + String oldval = (String) reg.getCachedAttributes().get(VALUE_ATTRIBUTE_NAME); + reg.changeAttributes(List.of(), Map.of( // + VALUE_ATTRIBUTE_NAME, value // + ), "Refreshed"); + if (values.get(gdbreg).longValue() != 0) { + String newval = reg.getName() + " : " + value; + reg.changeAttributes(List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, newval // + ), "Refreshed"); + reg.setModified(!value.equals(oldval)); + } + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSymbol.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSymbol.java new file mode 100644 index 0000000000..86a0f52e26 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSymbol.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.gdb.manager.impl.GdbMinimalSymbol; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetSymbol; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; + +public class GdbModelTargetSymbol + extends DefaultTargetObject + implements TargetSymbol { + protected static String indexSymbol(GdbMinimalSymbol symbol) { + return symbol.getName(); + } + + protected static String keySymbol(GdbMinimalSymbol symbol) { + return PathUtils.makeKey(indexSymbol(symbol)); + } + + protected final boolean constant; + protected final Address value; + protected final int size; + + public GdbModelTargetSymbol(GdbModelTargetSymbolContainer symbols, GdbMinimalSymbol symbol) { + super(symbols.impl, symbols, keySymbol(symbol), "Symbol"); + this.constant = false; + this.value = symbols.impl.space.getAddress(symbol.getAddress()); + this.size = 0; + + changeAttributes(List.of(), Map.of( + // TODO: DATA_TYPE + VALUE_ATTRIBUTE_NAME, value, SIZE_ATTRIBUTE_NAME, size, DISPLAY_ATTRIBUTE_NAME, + symbol.getName(), UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public boolean isConstant() { + return constant; + } + + @Override + public Address getValue() { + return value; + } + + @Override + public long getSize() { + return size; + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSymbolContainer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSymbolContainer.java new file mode 100644 index 0000000000..2824b2070f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSymbolContainer.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.gdb.manager.impl.GdbMinimalSymbol; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetSymbolNamespace; +import ghidra.util.datastruct.WeakValueHashMap; + +public class GdbModelTargetSymbolContainer + extends DefaultTargetObject + implements TargetSymbolNamespace { + protected final GdbModelImpl impl; + protected final GdbModelTargetModule module; + + protected final Map symbolsByName = new WeakValueHashMap<>(); + + public GdbModelTargetSymbolContainer(GdbModelTargetModule module) { + super(module.impl, module, "Symbols", "SymbolContainer"); + this.impl = module.impl; + this.module = module; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return module.module.listMinimalSymbols().thenAccept(byName -> { + List symbols; + synchronized (this) { + symbols = byName.values() + .stream() + .map(this::getTargetSymbol) + .collect(Collectors.toList()); + } + setElements(symbols, "Refreshed"); + changeAttributes(List.of(), Map.of( + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Refreshed"); + }); + } + + protected synchronized GdbModelTargetSymbol getTargetSymbol(GdbMinimalSymbol symbol) { + return symbolsByName.computeIfAbsent(symbol.getName(), + n -> new GdbModelTargetSymbol(this, symbol)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThread.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThread.java new file mode 100644 index 0000000000..9b82326012 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThread.java @@ -0,0 +1,229 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.*; +import agent.gdb.manager.GdbManager.ExecSuffix; +import agent.gdb.manager.impl.GdbFrameInfo; +import agent.gdb.manager.impl.GdbThreadInfo; +import agent.gdb.manager.reason.GdbBreakpointHitReason; +import agent.gdb.manager.reason.GdbReason; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.*; +import ghidra.dbg.util.PathUtils; +import ghidra.lifecycle.Internal; +import ghidra.util.Msg; + +public class GdbModelTargetThread + extends DefaultTargetObject implements + TargetThread, TargetExecutionStateful, + TargetSteppable, GdbModelSelectableObject { + protected static final TargetStepKindSet SUPPORTED_KINDS = TargetStepKindSet.of( // + TargetStepKind.ADVANCE, TargetStepKind.FINISH, TargetStepKind.LINE, TargetStepKind.OVER, + TargetStepKind.OVER_LINE, TargetStepKind.RETURN, TargetStepKind.UNTIL); + + protected static String indexThread(int threadId) { + return PathUtils.makeIndex(threadId); + } + + protected static String indexThread(GdbThread thread) { + return indexThread(thread.getId()); + } + + protected static String keyThread(GdbThread thread) { + return PathUtils.makeKey(indexThread(thread)); + } + + protected final GdbModelImpl impl; + protected final GdbThread thread; + private GdbInferior inferior; + protected String display; + protected String shortDisplay; + protected GdbThreadInfo info; + + protected final GdbModelTargetStack stack; + + public GdbModelTargetThread(GdbModelTargetThreadContainer threads, + GdbModelTargetInferior inferior, GdbThread thread) { + super(threads.impl, threads, keyThread(thread), "Thread"); + this.impl = threads.impl; + this.inferior = inferior.inferior; + this.thread = thread; + + this.stack = new GdbModelTargetStack(this, inferior); + + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, convertState(thread.getState()), // + SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS, // + DISPLAY_ATTRIBUTE_NAME, display = computeDisplay(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED, // + stack.getName(), stack // + ), "Initialized"); + + updateInfo().exceptionally(ex -> { + Msg.error(this, "Could not initialize thread info"); + return null; + }); + } + + private CompletableFuture updateInfo() { + return thread.getInfo().thenAccept(res -> { + this.info = res; + changeAttributes(List.of(), Map.of( // + SHORT_DISPLAY_ATTRIBUTE_NAME, shortDisplay = computeShortDisplay(), // + DISPLAY_ATTRIBUTE_NAME, display = computeDisplay() // + ), "Initialized"); + listeners.fire.displayChanged(this, getDisplay()); + }); + } + + protected String computeDisplay() { + StringBuilder sb = new StringBuilder(); + if (info != null) { + sb.append(shortDisplay); + sb.append(" "); + //sb.append(info.getTargetId()); + //sb.append(" "); + sb.append(info.getInferiorName()); + sb.append(" "); + sb.append(info.getState()); + sb.append(" "); + List frames = info.getFrames(); + if (!frames.isEmpty()) { + GdbFrameInfo frame = frames.get(0); + sb.append("at 0x"); + sb.append(frame.getAddr()); + sb.append(" in "); + sb.append(frame.getFunc()); + } + return sb.toString(); + } + sb.append(thread.getId()); + sb.append(" "); + sb.append(stack.inferior.inferior.getDescriptor()); + sb.append(" "); + sb.append(stack.inferior.inferior.getExecutable()); + GdbModelTargetStackFrame top = stack.framesByLevel.get(0); + if (top == null) { + return sb.toString(); + } + sb.append(" 0x"); + sb.append(top.frame.getAddress().toString(16)); + sb.append(" in "); + sb.append(top.frame.getFunction()); + sb.append(" ()"); + return sb.toString(); + } + + protected String computeShortDisplay() { + StringBuilder sb = new StringBuilder(); + sb.append("["); + sb.append(inferior.getId()); + sb.append("."); + sb.append(info.getId()); + if (info.getTid() != null) { + sb.append(":"); + sb.append(info.getTid()); + } + sb.append("]"); + return sb.toString(); + } + + protected TargetExecutionState convertState(GdbState state) { + switch (state) { + case RUNNING: + return TargetExecutionState.RUNNING; + case STOPPED: + default: + return TargetExecutionState.STOPPED; + } + } + + protected void threadStateChanged(GdbState state, GdbReason reason) { + if (state == GdbState.STOPPED) { + updateStack(); // NB: Callee handles errors + } + TargetExecutionState targetState = convertState(state); + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, targetState // + ), reason.desc()); + listeners.fire(TargetExecutionStateListener.class).executionStateChanged(this, targetState); + + if (reason instanceof GdbBreakpointHitReason) { + GdbBreakpointHitReason bpHit = (GdbBreakpointHitReason) reason; + GdbStackFrame frame = bpHit.getFrame(thread); + GdbModelTargetStackFrame f = stack.getTargetFrame(frame); + long bpId = bpHit.getBreakpointId(); + impl.session.breakpoints.breakpointHit(bpId, f); + } + } + + protected ExecSuffix convertToGdb(TargetStepKind kind) { + switch (kind) { + case FINISH: + return ExecSuffix.FINISH; + case INTO: + return ExecSuffix.STEP_INSTRUCTION; + case LINE: + return ExecSuffix.STEP; + case OVER: + return ExecSuffix.NEXT_INSTRUCTION; + case OVER_LINE: + return ExecSuffix.NEXT; + case RETURN: + return ExecSuffix.RETURN; + case UNTIL: + return ExecSuffix.UNTIL; + default: + throw new AssertionError(); + } + } + + @Override + public CompletableFuture step(TargetStepKind kind) { + switch (kind) { + case SKIP: + throw new UnsupportedOperationException(kind.name()); + case ADVANCE: // Why no exec-advance in GDB/MI? + // TODO: This doesn't work, since advance requires a parameter + return thread.console("advance"); + default: + return thread.step(convertToGdb(kind)); + } + } + + protected void invalidateRegisterCaches() { + stack.invalidateRegisterCaches(); + } + + protected CompletableFuture updateStack() { + Msg.debug(this, "Updating stack for " + this); + return stack.update().thenCompose(__ -> updateInfo()).exceptionally(ex -> { + Msg.error(this, "Could not update stack for thread " + this, ex); + return null; + }); + } + + @Override + @Internal + public CompletableFuture select() { + return thread.select(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThreadContainer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThreadContainer.java new file mode 100644 index 0000000000..8b6edcb26c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThreadContainer.java @@ -0,0 +1,102 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.gdb.manager.*; +import agent.gdb.manager.reason.GdbReason; +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.util.datastruct.WeakValueHashMap; + +// TODO: Should TargetThreadContainer be a thing? +public class GdbModelTargetThreadContainer + extends DefaultTargetObject { + protected final GdbModelImpl impl; + protected final GdbInferior inferior; + + protected final Map threadsById = new WeakValueHashMap<>(); + + public GdbModelTargetThreadContainer(GdbModelTargetInferior inferior) { + super(inferior.impl, inferior, "Threads", "ThreadContainer"); + this.impl = inferior.impl; + this.inferior = inferior.inferior; + } + + public GdbModelTargetThread threadCreated(GdbThread thread) { + // TODO: Can I get a better reason? + GdbModelTargetThread targetThread = getTargetThread(thread); + changeElements(List.of(), List.of(targetThread), "Created"); + return targetThread; + } + + public void threadStateChanged(GdbThread thread, GdbState state, GdbReason reason) { + getTargetThread(thread).threadStateChanged(state, reason); + } + + public void threadsStateChanged(Collection threads, GdbState state, + GdbReason reason) { + for (GdbThread thread : threads) { + threadStateChanged(thread, state, reason); + } + } + + public void threadExited(int threadId) { + synchronized (this) { + threadsById.remove(threadId); + } + changeElements(List.of(GdbModelTargetThread.indexThread(threadId)), List.of(), "Exited"); + } + + protected void updateUsingThreads(Map byTID) { + List threads; + synchronized (this) { + threads = + byTID.values().stream().map(this::getTargetThread).collect(Collectors.toList()); + } + setElements(threads, "Refreshed"); + } + + @Override + protected CompletableFuture requestElements(boolean refresh) { + if (!refresh) { + updateUsingThreads(inferior.getKnownThreads()); + return AsyncUtils.NIL; + } + return inferior.listThreads().thenAccept(byTID -> { + threadsById.keySet().retainAll(byTID.keySet()); + updateUsingThreads(byTID); + }); + } + + public synchronized GdbModelTargetThread getTargetThread(GdbThread thread) { + return threadsById.computeIfAbsent(thread.getId(), + i -> new GdbModelTargetThread(this, parent, thread)); + } + + public synchronized GdbModelTargetThread getTargetThreadIfPresent(int threadId) { + return threadsById.get(threadId); + } + + protected void invalidateRegisterCaches() { + for (GdbModelTargetThread thread : threadsById.values()) { + thread.invalidateRegisterCaches(); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/resources/session.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/resources/session.py new file mode 100644 index 0000000000..4576a6372c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/resources/session.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +""" +This script starts a new session on the given TTY + +The first parameter is the TTY file name. The remaining parameters +specify the image path and parameters of the session leader process. + +A few stack overflow questions, the Linux man pages, and a good +tutorial on TTYs, PTYs, jobs, process groups, and sessions is needed to +understand everything that is going on here. Nevertheless, the +operative lines are commented in the hopes it will help readers find +their way. +""" +from __future__ import print_function + +import os +import sys + + +def main(): + # Parse the arguments + ptypath = sys.argv[1] + args = sys.argv[2:] + + try: + # This tells Linux to make this process the leader of a new session. + os.setsid() + except OSError as e: + # This error occurs if we are already a session leader. Unlikely.... + print("Warning: setsid failed with EPERM") + if e.errno != 1: + raise e + + # Open the TTY. On Linux, the first TTY opened since becoming a session + # leader becomes the session's controlling TTY. Other platforms, e.g., BSD + # may require an explicit IOCTL. + fd = os.open(ptypath, os.O_RDWR) + + # Copy stderr to a backup descriptor, in case something goes wrong. + bk = fd + 1 + os.dup2(2, bk) + + # Copy the TTY fd over all standard streams. This effectively redirects + # the leader's standard streams to the TTY. + os.dup2(fd, 0) + os.dup2(fd, 1) + os.dup2(fd, 2) + + # At this point, we are the session leader and the named TTY is the + # controlling PTY. + # Now, exec the specified image with arguments as the session leader. + # Recall, this replaces the image of this process. + try: + os.execvp(args[0], args) + except: + # Something went wrong. Put stderr back, and report the error. + os.dup2(bk, 2) + raise + + +if __name__ == '__main__': + main() diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/sh/execjar.sh b/Ghidra/Debug/Debugger-agent-gdb/src/main/sh/execjar.sh new file mode 100644 index 0000000000..10a2adf955 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/sh/execjar.sh @@ -0,0 +1,7 @@ +#!/usr/bin/bash +# This clever bit can be prepended to a JAR to make it self-executable + +set -e + +java -jar "$0" +exit diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/ffi/linux/PtyTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/ffi/linux/PtyTest.java new file mode 100644 index 0000000000..f0d7da4f6a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/ffi/linux/PtyTest.java @@ -0,0 +1,209 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.ffi.linux; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.*; +import java.util.*; + +import org.junit.Ignore; +import org.junit.Test; + +import ghidra.dbg.testutil.DummyProc; + +public class PtyTest { + @Test + public void testOpenClosePty() throws IOException { + Pty pty = Pty.openpty(); + pty.close(); + } + + @Test + public void testMasterToSlave() throws IOException { + try (Pty pty = Pty.openpty()) { + PrintWriter writer = new PrintWriter(pty.getMaster().getOutputStream()); + BufferedReader reader = + new BufferedReader(new InputStreamReader(pty.getSlave().getInputStream())); + + writer.println("Hello, World!"); + writer.flush(); + assertEquals("Hello, World!", reader.readLine()); + } + } + + @Test + public void testSlaveToMaster() throws IOException { + try (Pty pty = Pty.openpty()) { + PrintWriter writer = new PrintWriter(pty.getSlave().getOutputStream()); + BufferedReader reader = + new BufferedReader(new InputStreamReader(pty.getMaster().getInputStream())); + + writer.println("Hello, World!"); + writer.flush(); + assertEquals("Hello, World!", reader.readLine()); + } + } + + @Test + public void testSessionBash() throws IOException, InterruptedException { + try (Pty pty = Pty.openpty()) { + Process bash = pty.getSlave().session(new String[] { DummyProc.which("bash") }, null); + pty.getMaster().getOutputStream().write("exit\n".getBytes()); + assertEquals(0, bash.waitFor()); + } + } + + @Test + public void testForkIntoNonExistent() throws IOException, InterruptedException { + try (Pty pty = Pty.openpty()) { + Process dies = pty.getSlave().session(new String[] { "thisHadBetterNotExist" }, null); + /** + * NOTE: Java subprocess dies with code 1 on unhandled exception. TODO: Is there a nice + * way to distinguish whether the code is from java or the execed image? + */ + assertEquals(1, dies.waitFor()); + } + } + + public Thread pump(InputStream is, OutputStream os) { + Thread t = new Thread(() -> { + byte[] buf = new byte[1024]; + while (true) { + int len; + try { + len = is.read(buf); + os.write(buf, 0, len); + } + catch (IOException e) { + throw new AssertionError(e); + } + } + }); + t.setDaemon(true); + t.start(); + return t; + } + + public BufferedReader loggingReader(InputStream is) { + return new BufferedReader(new InputStreamReader(is)) { + @Override + public String readLine() throws IOException { + String line = super.readLine(); + System.out.println("log: " + line); + return line; + } + }; + } + + public Thread runExitCheck(int expected, Process proc) { + Thread exitCheck = new Thread(() -> { + while (true) { + try { + assertEquals("Early exit with wrong code", expected, proc.waitFor()); + return; + } + catch (InterruptedException e) { + System.err.println("Exit check interrupted"); + } + } + }); + exitCheck.setDaemon(true); + exitCheck.start(); + return exitCheck; + } + + @Test + public void testSessionBashEchoTest() throws IOException, InterruptedException { + Map env = new HashMap<>(); + env.put("PS1", "BASH:"); + env.put("PROMPT_COMMAND", ""); + try (Pty pty = Pty.openpty()) { + PtyMaster master = pty.getMaster(); + PrintWriter writer = new PrintWriter(master.getOutputStream()); + BufferedReader reader = loggingReader(master.getInputStream()); + Process bash = pty.getSlave().session(new String[] { DummyProc.which("bash") }, env); + runExitCheck(3, bash); + + writer.println("echo test"); + writer.flush(); + String line; + do { + line = reader.readLine(); + } + while (!"test".equals(line)); + + writer.println("exit 3"); + writer.flush(); + + assertTrue(Set.of("BASH:exit 3", "exit 3").contains(reader.readLine())); + + assertEquals(3, bash.waitFor()); + } + } + + @Test + @Ignore("Some extra bash kruft is sneaking in, and I don't know how") + public void testSessionBashInterruptCat() throws IOException, InterruptedException { + Map env = new HashMap<>(); + env.put("PS1", "BASH:"); + try (Pty pty = Pty.openpty()) { + PtyMaster master = pty.getMaster(); + PrintWriter writer = new PrintWriter(master.getOutputStream()); + BufferedReader reader = loggingReader(master.getInputStream()); + Process bash = pty.getSlave().session(new String[] { DummyProc.which("bash") }, env); + runExitCheck(3, bash); + + writer.println("echo test"); + writer.flush(); + String line; + do { + line = reader.readLine(); + } + while (!"test".equals(line)); + + writer.println("cat"); + writer.flush(); + assertTrue(Set.of("BASH:cat", "cat").contains(reader.readLine())); + + writer.println("Hello, cat!"); + writer.flush(); + assertEquals("Hello, cat!", reader.readLine()); // echo back + assertEquals("Hello, cat!", reader.readLine()); // cat back + + writer.write(3); // should interrupt + writer.flush(); + do { + line = reader.readLine(); + } + while (!"^C".equals(line)); + writer.println("echo test"); + writer.flush(); + + do { + line = reader.readLine(); + } + while (!"test".equals(line)); + + writer.println("exit 3"); + writer.flush(); + assertTrue(Set.of("BASH:exit 3", "exit 3").contains(reader.readLine())); + + assertEquals(3, bash.waitFor()); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/GdbTableTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/GdbTableTest.java new file mode 100644 index 0000000000..b21737e6f6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/GdbTableTest.java @@ -0,0 +1,177 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager; + +import static org.junit.Assert.*; + +import java.util.*; +import java.util.function.Consumer; + +import org.junit.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import agent.gdb.manager.GdbTable; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; + +public class GdbTableTest { + protected GdbMiFieldList buildFieldList(Consumer conf) { + GdbMiFieldList.Builder builder = GdbMiFieldList.builder(); + conf.accept(builder); + return builder.build(); + } + + protected Map buildMap(Consumer> conf) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + conf.accept(builder); + return builder.build(); + } + + protected List buildList(Consumer> conf) { + ImmutableList.Builder builder = ImmutableList.builder(); + conf.accept(builder); + return builder.build(); + } + + protected GdbTable buildTestTable() { + return new GdbTable(buildFieldList((data) -> { + data.add("nr_rows", "3"); + data.add("nr_cols", "2"); + data.add("hdr", buildList((hdr) -> { + hdr.add(buildFieldList((col) -> { + col.add("col_name", "c0"); + col.add("colhdr", "First Column"); + })); + hdr.add(buildFieldList((col) -> { + col.add("col_name", "c1"); + col.add("colhdr", "Second Column"); + })); + })); + data.add("body", buildFieldList((body) -> { + body.add("item", buildFieldList((item) -> { + item.add("c0", "Col1Row1"); + item.add("c1", "Col2Row1"); + })); + body.add("item", buildFieldList((item) -> { + item.add("c1", "Col2Row2"); + item.add("c0", "Col1Row2"); + })); + body.add("item", buildFieldList((item) -> { + item.add("c0", "Col1Row3"); + item.add("c1", "Col2Row3"); + })); + })); + }), "item"); + } + + @Test + public void testBuildGdbTable() { + buildTestTable(); + } + + @Test + public void testColumns() { + GdbTable table = buildTestTable(); + assertEquals(2, table.columns().size()); + assertEquals(new LinkedHashSet<>(Arrays.asList("First Column", "Second Column")), + table.columns().keySet()); + } + + @Test + public void testRowCount() { + GdbTable table = buildTestTable(); + assertEquals(3, table.rows().size()); + assertNotNull(table.rows().get(0)); + assertNotNull(table.rows().get(1)); + assertNotNull(table.rows().get(2)); + try { + table.rows().get(3); + fail(); + } + catch (IndexOutOfBoundsException e) { + // pass + } + } + + @Test + public void testRowContents() { + GdbTable table = buildTestTable(); + Map row; + + row = table.rows().get(0); + assertEquals(2, row.size()); + assertEquals(new HashSet<>(Arrays.asList("First Column", "Second Column")), row.keySet()); + assertEquals("Col1Row1", row.get("First Column")); + assertEquals("Col2Row1", row.get("Second Column")); + + row = table.rows().get(1); + assertEquals(2, row.size()); + assertEquals(new HashSet<>(Arrays.asList("First Column", "Second Column")), row.keySet()); + assertEquals("Col1Row2", row.get("First Column")); + assertEquals("Col2Row2", row.get("Second Column")); + + row = table.rows().get(2); + assertEquals(2, row.size()); + assertEquals(new HashSet<>(Arrays.asList("First Column", "Second Column")), row.keySet()); + assertEquals("Col1Row3", row.get("First Column")); + assertEquals("Col2Row3", row.get("Second Column")); + } + + @Test + public void testRowIterator() { + GdbTable table = buildTestTable(); + List> copied = new ArrayList<>(); + for (Map row : table.rows()) { + copied.add(new LinkedHashMap<>(row)); + } + assertEquals(buildList((expTable) -> { + expTable.add(buildMap((row) -> { + row.put("First Column", "Col1Row1"); + row.put("Second Column", "Col2Row1"); + })); + expTable.add(buildMap((row) -> { + row.put("First Column", "Col1Row2"); + row.put("Second Column", "Col2Row2"); + })); + expTable.add(buildMap((row) -> { + row.put("First Column", "Col1Row3"); + row.put("Second Column", "Col2Row3"); + })); + }), copied); + } + + @Test + public void testColumnIterator() { + GdbTable table = buildTestTable(); + Map> copied = new LinkedHashMap<>(); + for (Map.Entry> col : table.columns().entrySet()) { + copied.put(col.getKey(), new ArrayList<>(col.getValue())); + } + assertEquals(buildMap((expTable) -> { + expTable.put("First Column", buildList((col) -> { + col.add("Col1Row1"); + col.add("Col1Row2"); + col.add("Col1Row3"); + })); + expTable.put("Second Column", buildList((col) -> { + col.add("Col2Row1"); + col.add("Col2Row2"); + col.add("Col2Row3"); + })); + }), copied); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/AbstractGdbManagerTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/AbstractGdbManagerTest.java new file mode 100644 index 0000000000..f70af4de5f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/AbstractGdbManagerTest.java @@ -0,0 +1,396 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import static ghidra.dbg.testutil.DummyProc.run; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeFalse; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.*; +import java.util.concurrent.*; +import java.util.stream.Collectors; + +import org.junit.*; + +import com.google.common.collect.*; + +import agent.gdb.manager.*; +import agent.gdb.manager.GdbManager.ExecSuffix; +import agent.gdb.manager.breakpoint.GdbBreakpointInfo; +import ghidra.async.AsyncReference; +import ghidra.dbg.testutil.DummyProc; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; +import ghidra.util.Msg; +import ghidra.util.SystemUtilities; + +@Ignore("Need correct version for CI") +public abstract class AbstractGdbManagerTest extends AbstractGhidraHeadlessIntegrationTest { + protected static final long TIMEOUT_MILLISECONDS = + SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE; + + protected abstract CompletableFuture startManager(GdbManager manager); + + protected void stopManager() throws IOException { + // Nothing by default + } + + protected T waitOn(CompletableFuture future) throws Throwable { + try { + return future.get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); + } + catch (ExecutionException e) { + throw e.getCause(); + } + } + + @After + public void tearDownGdbManagerTest() throws IOException { + stopManager(); + } + + @Test + public void testAddInferior() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + GdbInferior inferior = waitOn(mgr.addInferior()); + assertEquals(2, inferior.getId()); + assertEquals(Set.of(1, 2), mgr.getKnownInferiors().keySet()); + } + } + + @Test + public void testRemoveInferior() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + GdbInferior inf = waitOn(mgr.addInferior()); + assertEquals(2, mgr.getKnownInferiors().size()); + waitOn(inf.remove()); + assertEquals(1, mgr.getKnownInferiors().size()); + assertEquals(1, mgr.currentInferior().getId()); + assertEquals(Set.of(1), mgr.getKnownInferiors().keySet()); + } + } + + @Test + public void testRemoveCurrentInferior() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + List selEvtIdsTemp = new ArrayList<>(); + AsyncReference, Void> selEvtIds = new AsyncReference<>(List.of()); + mgr.addEventsListener(new GdbEventsListenerAdapter() { + @Override + public void inferiorSelected(GdbInferior inferior, GdbCause cause) { + selEvtIdsTemp.add(inferior.getId()); + selEvtIds.set(List.copyOf(selEvtIdsTemp), null); + } + }); + waitOn(startManager(mgr)); + waitOn(selEvtIds.waitValue(List.of(1))); + waitOn(mgr.addInferior()); + assertEquals(2, mgr.getKnownInferiors().size()); + waitOn(mgr.currentInferior().remove()); + assertEquals(1, mgr.getKnownInferiors().size()); + assertEquals(2, mgr.currentInferior().getId()); + assertEquals(Set.of(2), mgr.getKnownInferiors().keySet()); + waitOn(selEvtIds.waitValue(List.of(1, 2))); + } + } + + @Test + public void testConsoleCapture() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + String out = waitOn(mgr.consoleCapture("echo test")); + assertEquals("test", out.trim()); + } + } + + @Test + public void testListInferiors() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + Map inferiors = waitOn(mgr.listInferiors()); + assertEquals(new HashSet<>(Arrays.asList(new Integer[] { 1 })), inferiors.keySet()); + } + } + + @Test + public void testListAvailableProcesses() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + List procs = waitOn(mgr.listAvailableProcesses()); + List pids = procs.stream().map(p -> p.getPid()).collect(Collectors.toList()); + assertTrue(pids.contains(1)); // Weak check, but on Linux, 1 (init) is always running + } + } + + @Test + public void testInfoOs() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + GdbTable infoThreads = waitOn(mgr.infoOs("threads")); + assertEquals(new LinkedHashSet<>(Arrays.asList("pid", "command", "tid", "core")), + infoThreads.columns().keySet()); + assertTrue(infoThreads.columns().get("command").contains("java")); + } + } + + @Test + public void testStart() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + waitOn(mgr.currentInferior().fileExecAndSymbols("/usr/bin/echo")); + waitOn(mgr.console("break main")); + GdbThread thread = waitOn(mgr.currentInferior().run()); + assertNotNull(thread.getInferior().getPid()); + } + } + + @Test + public void testAttachDetach() throws Throwable { + try (DummyProc echo = run("dd"); GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + Set threads = waitOn(mgr.currentInferior().attach(echo.pid)); + // Attach stops the process, so no need to wait for STOPPED or prompt + for (GdbThread t : threads) { + assertEquals(echo.pid, (long) t.getInferior().getPid()); + waitOn(t.detach()); + } + } + } + + @Test + @Ignore("At developer's desk only") + public void stressTestStartInterrupt() throws Throwable { + // Just re-run the testStartInterrupt test many,many times + for (int i = 0; i < 100; i++) { + testStartInterrupt(); + } + } + + @Test + public void testStartInterrupt() throws Throwable { + assumeFalse("I know no way to get this to pass with these conditions", + this instanceof JoinedGdbManagerTest); + try (GdbManager mgr = GdbManager.newInstance()) { + /* + * Not sure the details here, but it seems GDB will give ^running as soon as the process + * has started. I suspect there are some nuances between the time the process is started + * and the time its signal handlers are installed. It seems waiting for libc to load + * guarantees that GDB is ready to interrupt the process. + */ + CompletableFuture libcLoaded = new CompletableFuture<>(); + mgr.addEventsListener(new GdbEventsListenerAdapter() { + @Override + public void libraryLoaded(GdbInferior inferior, String name, GdbCause cause) { + if (name.contains("libc")) { + libcLoaded.complete(null); + } + } + }); + waitOn(startManager(mgr)); + waitOn(mgr.currentInferior().fileExecAndSymbols("/usr/bin/sleep")); + waitOn(mgr.currentInferior().console("set args 3")); + waitOn(mgr.currentInferior().run()); + waitOn(libcLoaded); + Thread.sleep(100); // TODO: Why? + Msg.debug(this, "Interrupting"); + waitOn(mgr.interrupt()); + Msg.debug(this, "Waiting for prompt"); + waitOn(mgr.waitForPrompt()); + Msg.debug(this, "Testing echo test"); + String out = waitOn(mgr.consoleCapture("echo test")); + // Check that we have a responsive console, now. + // Otherwise, the interrupt failed + assertEquals("test", out.trim()); + } + } + + @Test + public void testSetVarEvaluate() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + waitOn(mgr.currentInferior().fileExecAndSymbols("/usr/bin/echo")); + waitOn(mgr.insertBreakpoint("main")); + waitOn(mgr.currentInferior().run()); + waitOn(mgr.waitForState(GdbState.STOPPED)); + waitOn(mgr.waitForPrompt()); + waitOn(mgr.currentInferior().setVar("$rax=", "0xdeadbeef")); // Corrupts it + String val = waitOn(mgr.currentInferior().evaluate("$rax+1")); + assertEquals(0xdeadbeef + 1, Integer.parseUnsignedInt(val)); + } + } + + @Test + public void testSetVarGetVar() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + String val = waitOn(mgr.currentInferior().getVar("args")); + assertEquals(null, val); + waitOn(mgr.currentInferior().setVar("args", "test")); + val = waitOn(mgr.currentInferior().getVar("args")); + assertEquals("test", val); + } + } + + @Test + public void testInsertListDeleteBreakpoint() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + waitOn(mgr.currentInferior().fileExecAndSymbols("/usr/bin/echo")); + GdbBreakpointInfo breakpoint = waitOn(mgr.insertBreakpoint("main")); + Map bl = waitOn(mgr.listBreakpoints()); + assertEquals(Map.of(1L, breakpoint), bl); + waitOn(mgr.deleteBreakpoints(breakpoint.getNumber())); + bl = waitOn(mgr.listBreakpoints()); + assertEquals(Map.of(), bl); + } + } + + @Test + public void testListReadWriteReadRegisters() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + waitOn(mgr.currentInferior().fileExecAndSymbols("/usr/bin/echo")); + waitOn(mgr.insertBreakpoint("main")); + GdbThread thread = waitOn(mgr.currentInferior().run()); + waitOn(mgr.waitForState(GdbState.STOPPED)); + waitOn(mgr.waitForPrompt()); + GdbRegisterSet regs = waitOn(thread.listRegisters()); + Set toRead = new HashSet<>(); + toRead.add(regs.get("eflags")); + toRead.add(regs.get("rax")); + Map read = waitOn(thread.readRegisters(toRead)); + // Verify eflags is rendered numerically + assertNotNull(read.get(regs.get("eflags"))); + assertNotNull(read.get(regs.get("rax"))); + Map toWrite = new HashMap<>(); + // NOTE: Not all flags are mutable from user-space. + // Turns out GDB/MI does not honor this, but CLI does.... + toWrite.put(regs.get("eflags"), BigInteger.valueOf(0L)); + toWrite.put(regs.get("rax"), BigInteger.valueOf(0x1122334455667788L)); + waitOn(thread.writeRegisters(toWrite)); + toRead = new HashSet<>(); + toRead.add(regs.get("eflags")); + // Verify register structure is reflected in API + toRead.add(regs.get("eax")); + read = waitOn(thread.readRegisters(toRead)); + // IF and that other reserved bit cannot be cleared + // Verified the same behavior in vanilla GDB at the CLI. + assertEquals(0x202L, read.get(regs.get("eflags")).longValue()); + assertEquals(0x55667788L, read.get(regs.get("eax")).longValue()); + } + } + + @Test + public void testWriteReadMemory() throws Throwable { + ByteBuffer rBuf = ByteBuffer.allocate(1024); + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + waitOn(mgr.currentInferior().fileExecAndSymbols("/usr/bin/echo")); + waitOn(mgr.insertBreakpoint("main")); + GdbThread thread = waitOn(mgr.currentInferior().run()); + waitOn(mgr.waitForState(GdbState.STOPPED)); + waitOn(mgr.waitForPrompt()); + String str = waitOn(mgr.currentInferior().evaluate("(long)main")); + long addr = Long.parseLong(str); + ByteBuffer buf = ByteBuffer.allocate(1024); + buf.order(ByteOrder.LITTLE_ENDIAN); + for (int i = 0; i < 10; i++) { + buf.putInt(i); + } + buf.flip(); + waitOn(thread.writeMemory(addr, buf)); + RangeSet rng = waitOn(thread.readMemory(addr, rBuf)); + rBuf.flip(); + rBuf.order(ByteOrder.LITTLE_ENDIAN); + RangeSet exp = TreeRangeSet.create(); + exp.add(Range.closedOpen(addr, addr + 1024)); + assertEquals(exp, rng); + for (int i = 0; i < 10; i++) { + assertEquals(i, rBuf.getInt()); + } + } + } + + @Test + public void testContinue() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + waitOn(mgr.currentInferior().fileExecAndSymbols("/usr/bin/echo")); + waitOn(mgr.insertBreakpoint("main")); + GdbThread thread = waitOn(mgr.currentInferior().run()); + waitOn(mgr.waitForState(GdbState.STOPPED)); + waitOn(mgr.waitForPrompt()); + waitOn(thread.cont()); + waitOn(mgr.waitForState(GdbState.STOPPED)); + assertEquals(0L, (long) mgr.currentInferior().getExitCode()); + } + } + + @Test + public void testStep() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + waitOn(mgr.currentInferior().fileExecAndSymbols("/usr/bin/echo")); + waitOn(mgr.insertBreakpoint("main")); + GdbThread thread = waitOn(mgr.currentInferior().run()); + waitOn(mgr.waitForState(GdbState.STOPPED)); + waitOn(mgr.waitForPrompt()); + waitOn(thread.step(ExecSuffix.NEXT_INSTRUCTION)); + waitOn(mgr.waitForState(GdbState.STOPPED)); + assertNull(mgr.currentInferior().getExitCode()); + } + } + + @Test + public void testThreadSelect() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + waitOn(mgr.currentInferior().fileExecAndSymbols("/usr/bin/echo")); + waitOn(mgr.insertBreakpoint("main")); + GdbThread thread = waitOn(mgr.currentInferior().run()); + waitOn(mgr.waitForState(GdbState.STOPPED)); + waitOn(mgr.waitForPrompt()); + waitOn(thread.select()); + } + } + + @Test + public void testListFrames() throws Throwable { + try (GdbManager mgr = GdbManager.newInstance()) { + waitOn(startManager(mgr)); + waitOn(mgr.currentInferior().fileExecAndSymbols("/usr/bin/echo")); + waitOn(mgr.insertBreakpoint("main")); + GdbThread thread = waitOn(mgr.currentInferior().run()); + waitOn(mgr.waitForState(GdbState.STOPPED)); + waitOn(mgr.waitForPrompt()); + waitOn(mgr.insertBreakpoint("write")); + waitOn(mgr.currentInferior().cont()); + waitOn(mgr.waitForState(GdbState.STOPPED)); + List stack = waitOn(thread.listStackFrames()); + Msg.debug(this, "Got stack:"); + for (GdbStackFrame frame : stack) { + Msg.debug(this, " " + frame); + } + assertEquals("write", stack.get(0).getFunction()); + assertEquals("main", stack.get(stack.size() - 1).getFunction()); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/GdbCValueParserTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/GdbCValueParserTest.java new file mode 100644 index 0000000000..f34b93f5e9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/GdbCValueParserTest.java @@ -0,0 +1,238 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import agent.gdb.manager.parsing.GdbCValueParser; +import agent.gdb.manager.parsing.GdbCValueParser.*; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +public class GdbCValueParserTest { + @Test + public void testIntegerZero() throws GdbParseError { + assertEquals(GdbIntValue.valueOf(0), GdbCValueParser.parseValue("0")); + assertEquals(GdbIntValue.valueOf(0), GdbCValueParser.parseValue("00")); + assertEquals(GdbIntValue.valueOf(0), GdbCValueParser.parseValue("000")); + assertEquals(GdbIntValue.valueOf(0), GdbCValueParser.parseValue("0x0")); + assertEquals(GdbIntValue.valueOf(0), GdbCValueParser.parseValue("0x00")); + } + + @Test + public void testIntegerDec() throws GdbParseError { + assertEquals(GdbIntValue.valueOf(1), GdbCValueParser.parseValue("1")); + assertEquals(GdbIntValue.valueOf(10), GdbCValueParser.parseValue("10")); + assertEquals(GdbIntValue.valueOf(1234567890), GdbCValueParser.parseValue("1234567890")); + } + + @Test(expected = GdbParseError.class) + public void testIntegerDecErr() throws GdbParseError { + GdbCValueParser.parseValue("1f"); + } + + @Test + public void testIntegerHex() throws GdbParseError { + assertEquals(GdbIntValue.valueOf(1), GdbCValueParser.parseValue("0x1")); + assertEquals(GdbIntValue.valueOf(0x10), GdbCValueParser.parseValue("0x10")); + assertEquals(GdbIntValue.valueOf(0x123456789abcdef0L), + GdbCValueParser.parseValue("0x123456789abcdef0")); + } + + @Test(expected = GdbParseError.class) + public void testIntegerHexErr() throws GdbParseError { + GdbCValueParser.parseValue("0xfg"); + } + + @Test + public void testIntegerOct() throws GdbParseError { + assertEquals(GdbIntValue.valueOf(01), GdbCValueParser.parseValue("01")); + assertEquals(GdbIntValue.valueOf(010), GdbCValueParser.parseValue("010")); + assertEquals(GdbIntValue.valueOf(012345670), GdbCValueParser.parseValue("012345670")); + } + + @Test(expected = GdbParseError.class) + public void testIntegerOctErr() throws GdbParseError { + GdbCValueParser.parseValue("018"); + } + + @Test + public void testComposite() throws GdbParseError { + assertEquals(GdbCompositeValue.builder() + .put("a", GdbIntValue.valueOf(1)) + .put("b", GdbIntValue.valueOf(2)) + .put("c", GdbIntValue.valueOf(3)) + .build(), + GdbCValueParser.parseValue("{a=1,b=0x2,c=03}")); + } + + @Test + public void testCompositeSpaces() throws GdbParseError { + assertEquals(GdbCompositeValue.builder() + .put("a", GdbIntValue.valueOf(1)) + .put("b", GdbIntValue.valueOf(2)) + .put("c", GdbIntValue.valueOf(3)) + .build(), + GdbCValueParser.parseValue(" { a = 1 , b = 0x2 , c = 03 } ")); + } + + @Test(expected = GdbParseError.class) + public void testCompositeErrMissingClose() throws GdbParseError { + GdbCValueParser.parseValue("{a = 1, b = 0x2, c = 03"); + } + + @Test(expected = GdbParseError.class) + public void testCompositeErrMissingComma() throws GdbParseError { + GdbCValueParser.parseValue("{a = 1 b = 0x2, c = 03}"); + } + + @Test + public void testArray() throws GdbParseError { + assertEquals(GdbArrayValue.builder() + .add(GdbIntValue.valueOf(1)) + .add(GdbIntValue.valueOf(2)) + .add(GdbIntValue.valueOf(3)) + .build(), + GdbCValueParser.parseValue("{1,0x2,03}")); + } + + @Test + public void testArraySpaces() throws GdbParseError { + assertEquals(GdbArrayValue.builder() + .add(GdbIntValue.valueOf(1)) + .add(GdbIntValue.valueOf(2)) + .add(GdbIntValue.valueOf(3)) + .build(), + GdbCValueParser.parseValue(" { 1 , 0x2 , 03 } ")); + } + + @Test(expected = GdbParseError.class) + public void testArrayErrMissingClose() throws GdbParseError { + GdbCValueParser.parseValue("{1, 0x2, 03"); + } + + @Test(expected = GdbParseError.class) + public void testArrayErrMissingComma() throws GdbParseError { + GdbCValueParser.parseValue("{1 0x2, 03}"); + } + + @Test + public void testArrayWithRepeat() throws GdbParseError { + assertEquals(GdbArrayValue.builder() + .add(GdbIntValue.valueOf(1)) + .add(GdbIntValue.valueOf(2)) + .add(GdbIntValue.valueOf(2)) + .add(GdbIntValue.valueOf(2)) + .add(GdbIntValue.valueOf(2)) + .add(GdbIntValue.valueOf(2)) + .add(GdbIntValue.valueOf(3)) + .build(), + GdbCValueParser.parseValue("{1, 0x2 , 03}")); + } + + @Test + public void testNested() throws GdbParseError { + assertEquals(GdbCompositeValue.builder() + .put("a", GdbIntValue.valueOf(1)) + .put("b", GdbArrayValue.builder() + .add(GdbIntValue.valueOf(2)) + .add(GdbIntValue.valueOf(3)) + .build()) + .put("c", GdbCompositeValue.builder() + .put("d", GdbIntValue.valueOf(4)) + .put("e", GdbIntValue.valueOf(5)) + .build()) + .build(), + GdbCValueParser.parseValue("{a=1,b={2,3},c={d=4,e=5}}")); + } + + @Test(expected = GdbParseError.class) + public void testEmptyStringErr() throws GdbParseError { + GdbCValueParser.parseValue(""); + } + + @Test + public void testEmptyValue() throws GdbParseError { + assertTrue(GdbCValueParser.parseValue("{}").isEmpty()); + } + + @Test + public void testRegisterValue() throws GdbParseError { + String observed = "{" + + "v4_float = {0x0, 0x0, 0x0, 0x0}, " + + "v2_double = {0x0, 0x0}, " + + "v16_int8 = {0x0 }, " + + "v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, " + + "v4_int32 = {0x0, 0x0, 0x0, 0x0}, " + + "v2_int64 = {0x0, 0x0}, " + + "uint128 = 0x00000000000000000000000000000000" + + "}"; + assertEquals(GdbCompositeValue.builder() + .put("v4_float", GdbArrayValue.builder() + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .build()) + .put("v2_double", GdbArrayValue.builder() + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .build()) + .put("v16_int8", GdbArrayValue.builder() + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .build()) + .put("v8_int16", GdbArrayValue.builder() + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .build()) + .put("v4_int32", GdbArrayValue.builder() + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .build()) + .put("v2_int64", GdbArrayValue.builder() + .add(GdbIntValue.valueOf(0)) + .add(GdbIntValue.valueOf(0)) + .build()) + .put("uint128", GdbIntValue.valueOf(0)) + .build(), + GdbCValueParser.parseValue(observed)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/JoinedGdbManagerTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/JoinedGdbManagerTest.java new file mode 100644 index 0000000000..60cbdf4032 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/JoinedGdbManagerTest.java @@ -0,0 +1,77 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.io.*; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.ffi.linux.Pty; +import agent.gdb.manager.GdbManager; +import ghidra.util.Msg; + +public class JoinedGdbManagerTest extends AbstractGdbManagerTest { + protected class ReaderThread extends Thread { + @Override + public void run() { + BufferedReader reader = + new BufferedReader(new InputStreamReader(ptyUserGdb.getMaster().getInputStream())); + String line; + try { + while (gdb != null && null != (line = reader.readLine())) { + Msg.debug(this, "USERGDB: " + line); + } + } + catch (IOException e) { + Msg.debug(this, "Error reading USERGDB line: " + e); + } + } + } + + protected Pty ptyUserGdb; + protected Process gdb; + + @Override + protected CompletableFuture startManager(GdbManager manager) { + try { + ptyUserGdb = Pty.openpty(); + manager.start(null); + Msg.debug(this, "Starting GDB and invoking new-ui mi2 " + manager.getMi2PtyName()); + + gdb = ptyUserGdb.getSlave() + .session(new String[] { GdbManager.DEFAULT_GDB_CMD }, Map.of()); + new ReaderThread().start(); + PrintWriter gdbCmd = new PrintWriter(ptyUserGdb.getMaster().getOutputStream()); + gdbCmd.println("new-ui mi2 " + manager.getMi2PtyName()); + gdbCmd.flush(); + return manager.runRC(); + } + catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + protected void stopManager() throws IOException { + if (gdb != null) { + gdb.destroyForcibly(); + gdb = null; + } + if (ptyUserGdb != null) { + ptyUserGdb.close(); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/SpawnedGdbManagerTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/SpawnedGdbManagerTest.java new file mode 100644 index 0000000000..a4d2571ba5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/SpawnedGdbManagerTest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +import agent.gdb.manager.GdbManager; + +public class SpawnedGdbManagerTest extends AbstractGdbManagerTest { + @Override + protected CompletableFuture startManager(GdbManager manager) { + try { + manager.start(); + return manager.runRC(); + } + catch (IOException e) { + throw new AssertionError(e); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/parsing/GdbMiParserTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/parsing/GdbMiParserTest.java new file mode 100644 index 0000000000..8932a69d47 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/parsing/GdbMiParserTest.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.parsing; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.function.Consumer; +import java.util.regex.Pattern; + +import org.junit.Test; + +import agent.gdb.manager.parsing.GdbMiParser; +import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList; +import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError; + +public class GdbMiParserTest { + protected GdbMiFieldList buildFieldList(Consumer conf) { + GdbMiFieldList.Builder builder = GdbMiFieldList.builder(); + conf.accept(builder); + return builder.build(); + } + + @Test + public void testMatch() throws GdbParseError { + GdbMiParser parser = new GdbMiParser("Hello, World!"); + assertEquals("Hello", parser.match(Pattern.compile("\\w+"), true)); + assertEquals(",", parser.match(GdbMiParser.COMMA, true)); + } + + @Test + public void testParseString() throws GdbParseError { + GdbMiParser parser = new GdbMiParser("\"Hello, World!\\n\""); + assertEquals("Hello, World!\n", parser.parseString()); + } + + @Test + public void testParseList() throws GdbParseError { + GdbMiParser parser = new GdbMiParser("[\"Hello\",\"World\"]"); + assertEquals(Arrays.asList(new String[] { "Hello", "World" }), parser.parseList()); + } + + @Test + public void testParseMap() throws GdbParseError { + GdbMiParser parser = new GdbMiParser("{h=\"Hello\",w=\"World\"}"); + assertEquals(buildFieldList((exp) -> { + exp.add("h", "Hello"); + exp.add("w", "World"); + }), parser.parseMap()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/AbstractModelForGdbTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/AbstractModelForGdbTest.java new file mode 100644 index 0000000000..5bb95c761f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/AbstractModelForGdbTest.java @@ -0,0 +1,1022 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model; + +import static ghidra.dbg.testutil.DummyProc.run; +import static ghidra.dbg.testutil.DummyProc.which; +import static org.junit.Assert.*; + +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.junit.Ignore; +import org.junit.Test; + +import agent.gdb.manager.GdbManager; +import agent.gdb.model.EventSequenceListener.EventRecord; +import agent.gdb.model.impl.GdbModelTargetInferior; +import agent.gdb.model.impl.GdbModelTargetStackFrame; +import ghidra.async.AsyncReference; +import ghidra.async.AsyncUtils; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.DebugModelConventions.AllRequiredAccess; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.attributes.TargetObjectRefList; +import ghidra.dbg.error.DebuggerModelNoSuchPathException; +import ghidra.dbg.error.DebuggerModelTypeException; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.dbg.target.TargetConsole.Channel; +import ghidra.dbg.target.TargetEventScope.TargetEventType; +import ghidra.dbg.target.TargetFocusScope.TargetFocusScopeListener; +import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetObject.TargetObjectListener; +import ghidra.dbg.target.TargetSteppable.TargetStepKind; +import ghidra.dbg.testutil.DummyProc; +import ghidra.dbg.util.*; +import ghidra.program.model.address.Address; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; +import ghidra.util.*; + +@Ignore("Need correct version for CI") +public abstract class AbstractModelForGdbTest + extends AbstractGhidraHeadlessIntegrationTest implements DebuggerModelTestUtils { + protected static final Map AMD64_TEST_REG_VALUES = Map.of( // + "rax", NumericUtilities.convertStringToBytes("0123456789abcdef"), // + "ymm0", NumericUtilities.convertStringToBytes("" + // + "0123456789abcdef" + + "fedcba9876543210" + + "0011223344556677" + + "8899aabbccddeeff")); + protected static final long TIMEOUT_MILLISECONDS = + SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE; + + protected static Map hexlify(Map map) { + return map.entrySet() + .stream() + .collect(Collectors.toMap(Entry::getKey, + e -> NumericUtilities.convertBytesToString(e.getValue()))); + } + + public interface ModelHost extends AutoCloseable { + DebuggerObjectModel getModel(); + + CompletableFuture init(); + } + + protected abstract ModelHost modelHost(String gdbCmd) throws Exception; + + protected ModelHost modelHost() throws Exception { + return modelHost(GdbManager.DEFAULT_GDB_CMD); + } + + protected static class CatchOffThread implements AutoCloseable { + protected Throwable caught; + + void catching(Runnable runnable) { + try { + runnable.run(); + } + catch (Throwable e) { + caught = e; + } + } + + @Override + public void close() throws Exception { + if (caught != null) { + throw new AssertionError("Off-thread exception", caught); + } + } + } + + protected void init(ModelHost m) throws Throwable { + waitOn(m.init()); + } + + protected static boolean isTerminationError(Throwable ex) { + ex = AsyncUtils.unwrapThrowable(ex); + // TODO: Marshall this exception better via GADP + if (ex instanceof RuntimeException || ex instanceof IllegalStateException) { + if (ex.getMessage().contains("GDB is terminating") || + ex.getMessage().contains("Unknown: Unknown server-side error")) { + return true; + } + } + if (ex instanceof InterruptedException) { + return true; // TODO: This is way too broad + } + return false; + } + + protected static T ignoreTermination(Throwable t) { + Throwable ex = AsyncUtils.unwrapThrowable(t); + // TODO: Should it be an error if there's no exception here? + // As long as state is correct and root is invalid after? + if (isTerminationError(ex)) { + return null; // pass + } + throw new AssertionError(t); + } + + @Test + public void testInitFinish() throws Throwable { + try (ModelHost m = modelHost()) { + init(m); + } + } + + @Test + @Ignore("abstract test case is failing on off-thread exceptions") + public void testBadGdbCmd() throws Throwable { + try (ModelHost m = modelHost("/usr/bin/this_shouldnt_exit")) { + init(m); + // TODO: assert model state is TERMINATE or something + } + catch (Exception ex) { + ignoreTermination(ex); + } + } + + @Test + public void testNonExistentPathGivesNull() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + TargetObject obj = waitOn(model.fetchModelObject("Doesn't exist")); + assertNull(obj); + } + } + + @Test + public void testSessionLaunch() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferiors (before launch)..."); + Map inferiors = + waitOn(model.fetchObjectElements(List.of("Inferiors"))); + Msg.debug(this, "Inferiors before: " + inferiors); + assertEquals(1, inferiors.size()); + Msg.debug(this, "Finding TargetLauncher..."); + TargetLauncher launcher = suitable(TargetLauncher.tclass, root); + Msg.debug(this, "Launching..."); + waitOn(launcher.launch( + Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "/bin/echo Hello, World!"))); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferiors (after launch)..."); + inferiors = waitOn(model.fetchObjectElements(List.of("Inferiors"))); + Msg.debug(this, "Inferiors after: " + inferiors); + assertEquals(2, inferiors.size()); + } + } + + @Test + public void testInferiorLaunchParameters() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess rootAccess = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(rootAccess); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject(List.of("Inferiors", "[1]"))); + Msg.debug(this, "Tracking inferior access..."); + AllRequiredAccess infAccess = access(inferior); + Msg.debug(this, "Waiting for inferior access..."); + waitAcc(infAccess); + Msg.debug(this, "Reflecting parameters"); + TargetLauncher launcher = inferior.as(TargetLauncher.tclass); + for (ParameterDescription param : launcher.getParameters().values()) { + Msg.info(this, " Parameter: " + param); + } + waitOn(launcher.launch(Map.of("args", "/bin/echo Hello, World!"))); + + Msg.debug(this, "Waiting for session access (again)..."); + waitAcc(rootAccess); + Msg.debug(this, "Getting Inferiors (after launch)..."); + Map inferiors = + waitOn(model.fetchObjectElements(List.of("Inferiors"))); + Msg.debug(this, "Inferiors after: " + inferiors); + assertEquals(1, inferiors.size()); + } + } + + @Test + public void testInferiorLaunch() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess rootAccess = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(rootAccess); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject(List.of("Inferiors", "[1]"))); + Msg.debug(this, "Tracking inferior access..."); + AllRequiredAccess infAccess = access(inferior); + Msg.debug(this, "Waiting for inferior access..."); + waitAcc(infAccess); + Msg.debug(this, "Launching..."); + launch(inferior, + Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "/bin/echo Hello, World!")); + Msg.debug(this, "Waiting for session access (again)..."); + waitAcc(rootAccess); + Msg.debug(this, "Getting Inferiors (after launch)..."); + Map inferiors = + waitOn(model.fetchObjectElements(List.of("Inferiors"))); + Msg.debug(this, "Inferiors after: " + inferiors); + assertEquals(1, inferiors.size()); + } + } + + @Test + public void testListProcesses() throws Throwable { + try (DummyProc dd = run("dd"); ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Map available = + waitOn(model.fetchObjectElements(List.of("Available"))); + assertTrue(available.containsKey(Long.toString(dd.pid))); + } + } + + @Test + public void testSessionAttachKill() throws Throwable { + try (DummyProc dd = run("dd"); ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferiors (before attach)..."); + Map inferiors = + waitOn(model.fetchObjectElements(List.of("Inferiors"))); + Msg.debug(this, "Inferiors before: " + inferiors); + assertEquals(1, inferiors.size()); + Msg.debug(this, "Finding TargetAttacher..."); + TargetAttacher attacher = suitable(TargetAttacher.tclass, root); + Msg.debug(this, " Got TargetAttacher: " + attacher); + TargetAttachable attachable = + waitOn(model.fetchModelObject("Available", "[" + dd.pid + "]")) + .as(TargetAttachable.tclass); + Msg.debug(this, " Got Attachable: " + attachable); + Msg.debug(this, "Attaching..."); + waitOn(attacher.attach(attachable)); + Msg.debug(this, "Waiting for session access (again)..."); + waitAcc(access); + Msg.debug(this, "Getting Inferiors (after attach)..."); + inferiors = waitOn(model.fetchObjectElements(List.of("Inferiors"))); + Msg.debug(this, "Inferiors after: " + inferiors); + assertEquals(2, inferiors.size()); + Msg.debug(this, "Killing..."); + TargetKillable killable = + waitOn(inferiors.get("2").as(TargetKillable.tclass).fetch()); + waitOn(killable.kill()); + } + } + + @Test + public void testInferiorAttachKill() throws Throwable { + try (DummyProc dd = run("dd"); ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject(List.of("Inferiors", "[1]"))); + TargetAttacher attacher = inferior.as(TargetAttacher.tclass); + Msg.debug(this, "Waiting for session access (again)..."); + waitAcc(access); + Msg.debug(this, "Attaching..."); + waitOn(attacher.attach(dd.pid)); + Msg.debug(this, "Waiting for session access (again, again)..."); + waitAcc(access); + Msg.debug(this, "Getting Inferiors (after attach)..."); + Map inferiors = + waitOn(model.fetchObjectElements(List.of("Inferiors"))); + Msg.debug(this, "Inferiors after: " + inferiors); + assertEquals(1, inferiors.size()); + Msg.debug(this, "Killing..."); + TargetObject attached = waitOn(inferiors.get("1").fetch()); + TargetKillable killable = attached.as(TargetKillable.tclass); + waitOn(killable.kill()); + } + } + + @Test + public void testLaunchContExit() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject("Inferiors", "[1]")); + Msg.debug(this, "Launching..."); + launch(inferior, Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "echo Hello, World!")); + Msg.debug(this, "Waiting for session access (again)..."); + waitAcc(access); + Msg.debug(this, "Resuming..."); + resume(inferior); + Msg.debug(this, "Waiting for session access (after resume)..."); + waitAcc(access); + } + } + + @Test(expected = DebuggerModelNoSuchPathException.class) + public void testAttachNoObjectErr() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject("Inferiors", "[1]")); + Msg.debug(this, "Attaching to bogus path..."); + TargetAttacher attacher = inferior.as(TargetAttacher.tclass); + waitOn(attacher.attach(model.createRef("Available", "Process -1") + .as(TargetAttachable.tclass))); + } + } + + @Test(expected = DebuggerModelTypeException.class) + public void testAttachNonAttachableErr() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject("Inferiors", "[1]")); + Msg.debug(this, "Attaching to bogus path..."); + TargetAttacher attacher = inferior.as(TargetAttacher.tclass); + waitOn(attacher.attach(model.createRef("Available").as(TargetAttachable.tclass))); + fail("Exception expected"); + } + } + + @Test + @Ignore("for developer workstation") + public void stressTestExecute() throws Throwable { + for (int i = 0; i < 100; i++) { + testExecute(); + } + } + + @Test + public void testExecute() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AsyncReference lastOut = new AsyncReference<>(); + AllTargetObjectListenerAdapter l = new AllTargetObjectListenerAdapter() { + @Override + public void consoleOutput(TargetObject interpreter, Channel channel, + String out) { + Msg.debug(this, "Got " + channel + " output: " + out); + lastOut.set(out, null); + } + }; + + init(m); + Msg.debug(this, "Getting root object..."); + TargetObject root = root(model); + root.addListener(l); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Running command..."); + cli(root, "echo test"); + Msg.debug(this, "Waiting for expected output..."); + waitOn(lastOut.waitValue("test")); + } + } + + @Test + @Ignore("Abstract test case is failing on off-thread exceptions") + public void testExecuteQuit() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Quitting..."); + cli(root, "quit"); + // TODO: Assert model state is TERMINATED, or something + // (pending merging of DebuggerClient stuff into DebuggerObjectModel + // For now, best option is to assert root is invalid + assertFalse(root.isValid()); + } + catch (Throwable ex) { + ignoreTermination(ex); + } + } + + @Test + public void testExecuteCapture() throws Throwable { + try (ModelHost m = modelHost(); CatchOffThread offThread = new CatchOffThread()) { + DebuggerObjectModel model = m.getModel(); + + AllTargetObjectListenerAdapter l = new AllTargetObjectListenerAdapter() { + @Override + public void consoleOutput(TargetObject interpreter, Channel channel, + String out) { + Msg.debug(this, "Got " + channel + " output: " + out); + if (!out.contains("test")) { + return; + } + offThread.catching(() -> fail("Unexpected output:" + out)); + } + }; + + init(m); + Msg.debug(this, "Getting root object..."); + TargetObject root = root(model); + root.addListener(l); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Running command with capture..."); + String out = captureCli(root, "echo test"); + Msg.debug(this, "Captured: " + out); + assertEquals("test", out); + } + } + + @Test + public void testGetBreakKinds() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object..."); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Finding breakpoint container..."); + TargetBreakpointContainer breaks = suitable(TargetBreakpointContainer.tclass, root); + Msg.debug(this, "Got: " + breaks); + TargetBreakpointKindSet kinds = breaks.getSupportedBreakpointKinds(); + Msg.debug(this, "Supports: " + kinds); + assertEquals(4, kinds.size()); + } + } + + @Test + public void testPlaceBreakpoint() throws Throwable { + String specimen = which("expFork"); + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object..."); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Setting file to " + specimen + "..."); + cli(root, "file " + specimen); + Msg.debug(this, "Finding breakpoint container..."); + TargetBreakpointContainer breaks = suitable(TargetBreakpointContainer.tclass, root); + Msg.debug(this, "Placing breakpoint..."); + waitOn(breaks.placeBreakpoint("func", Set.of(TargetBreakpointKind.SOFTWARE))); + Msg.debug(this, "Getting breakpoint specs..."); + Map specs = waitOn(breaks.fetchElements()); + Msg.debug(this, "Got specs: " + specs); + assertEquals(1, specs.size()); + TargetBreakpointSpec spec = + waitOn(specs.get("1").as(TargetBreakpointSpec.tclass).fetch()); + Collection> ls = waitOn(spec.getLocations()); + Msg.debug(this, "Got locations: " + ls); + assertEquals(1, ls.size()); + TargetBreakpointLocation loc = ls.iterator().next(); + Address addr = loc.getAddress(); + Msg.debug(this, "Got address: " + addr); + TargetObjectRefList list = loc.getAffects(); + Msg.debug(this, "Got affects: " + list); + assertEquals(1, list.size()); + } + } + + @Test + public void testPlaceWatchpoint() throws Throwable { + String specimen = which("expTypes"); + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object..."); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Setting file to " + specimen + "..."); + cli(root, "file " + specimen); + Msg.debug(this, "Finding breakpoint container..."); + TargetBreakpointContainer breaks = suitable(TargetBreakpointContainer.tclass, root); + Msg.debug(this, "Placing breakpoint..."); + waitOn(breaks.placeBreakpoint("int_var", + Set.of(TargetBreakpointKind.READ, TargetBreakpointKind.WRITE))); + Msg.debug(this, "Getting breakpoint specs..."); + Map specs = waitOn(breaks.fetchElements()); + Msg.debug(this, "Got specs: " + specs); + assertEquals(1, specs.size()); + TargetBreakpointSpec spec = + waitOn(specs.get("1").as(TargetBreakpointSpec.tclass).fetch()); + Collection> ls = waitOn(spec.getLocations()); + Msg.debug(this, "Got locations: " + ls); + assertEquals(1, ls.size()); + TargetBreakpointLocation loc = ls.iterator().next(); + Address addr = loc.getAddress(); + Msg.debug(this, "Got address: " + addr); + assertNotNull(addr); + TargetObjectRefList list = loc.getAffects(); + Msg.debug(this, "Got affects: " + list); + assertEquals(1, list.size()); + } + } + + @Test + public void testExpFork() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + Set

locAddresses = new HashSet<>(); + Set locAffecteds = new HashSet<>(); + + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject("Inferiors", "[1]")); + Msg.debug(this, "Launching..."); + launch(inferior, Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, which("expFork"))); + Msg.debug(this, "Waiting for session access (again)..."); + waitAcc(access); + Msg.debug(this, "Setting to stay attached to forks"); + cli(root, "set detach-on-fork off"); + TargetBreakpointContainer breaks = + suitable(TargetBreakpointContainer.tclass, inferior); + Msg.debug(this, "Setting break on func"); + waitOn(breaks.placeBreakpoint("func", Set.of(TargetBreakpointKind.SOFTWARE))); + Msg.debug(this, "Resuming execution (first time)"); + resume(inferior); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Map inferiors = + waitOn(model.fetchObjectElements("Inferiors")); + Msg.debug(this, "After first break, inferiors are: " + inferiors); + assertEquals(2, inferiors.size()); + // NOTE: Breakpoint 1 was the temporary one on 'main' + Map ls = + waitOn(model.fetchObjectElements("Breakpoints", "[2]")); + Msg.debug(this, "Locations: " + ls); + assertEquals(2, ls.size()); + for (TargetObjectRef ref : ls.values()) { + TargetBreakpointLocation loc = + waitOn(ref.as(TargetBreakpointLocation.tclass).fetch()); + locAddresses.add(loc.getAddress()); + locAffecteds.addAll(loc.getAffects()); + } + Msg.debug(this, "Addresses: " + locAddresses + ", affected: " + locAffecteds); + assertEquals(1, locAddresses.size()); + assertEquals(Set.of(List.of("Inferiors", "[1]"), List.of("Inferiors", "[2]")), + locAffecteds.stream().map(TargetObjectRef::getPath).collect(Collectors.toSet())); + } + } + + @Test + @Ignore("Known issue") + public void testExpForkWithListeners() throws Throwable { + ElementTrackingListener infListener = + new ElementTrackingListener<>(TargetObject.class); + ElementTrackingListener> bkListener = + new ElementTrackingListener<>(TargetBreakpointSpec.tclass); + ElementTrackingListener> blListener = + new ElementTrackingListener<>(TargetBreakpointLocation.tclass); + EventSequenceListener evtListener = new EventSequenceListener(); + + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + Set
ebAddresses = new HashSet<>(); + Set ebAffecteds = new HashSet<>(); + + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session events and access..."); + root.addListener(evtListener); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject infCont = waitOn(model.fetchModelObject("Inferiors")); + Msg.debug(this, "Installing listener for inferiors"); + infCont.addListener(infListener); + Msg.debug(this, "Getting inferiors"); + Map inferiors = waitOn(infCont.fetchElements() + .thenCompose(DebugModelConventions::fetchAll)); + infListener.putAll(inferiors); + Msg.debug(this, "Launching..."); + launch(infListener.elements.get("1"), + Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, which("expFork"))); + Msg.debug(this, "Waiting for session access (again)..."); + waitAcc(access); + Msg.debug(this, "Setting to stay attached to forks"); + cli(root, "set detach-on-fork off"); + TargetBreakpointContainer bkCont = + suitable(TargetBreakpointContainer.tclass, infCont); + Msg.debug(this, "Installing listener for breakpoints"); + bkCont.addListener(bkListener); + Msg.debug(this, "Getting breakpoints"); + Map bkElems = waitOn(bkCont.fetchElements() + .thenCompose(DebugModelConventions::fetchAll)); + bkListener.putAll(bkElems); + Msg.debug(this, "Setting break on func"); + waitOn(bkCont.placeBreakpoint("func", Set.of(TargetBreakpointKind.SOFTWARE))); + Msg.debug(this, "Breakpoint elements: " + bkListener.elements); + TargetBreakpointSpec bk2 = + waitOn(bkListener.refElement("2").waitUntil(t -> t != null)); + Msg.debug(this, "Installing listener on Breakpoint 2"); + bk2.addListener(blListener); + Msg.debug(this, "Getting locations for 2"); + Map bk2ls = waitOn(bk2.fetchElements() + .thenCompose(DebugModelConventions::fetchAll)); + blListener.putAll(bk2ls); + Msg.debug(this, "Resuming execution (first time)"); + resume(infListener.elements.get("1")); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "After first break, inferiors are: " + infListener.elements); + waitOn(infListener.size.waitValue(2)); + assertEquals(2, infListener.elements.size()); + waitOn(blListener.size.waitValue(2)); + Msg.debug(this, "Locations: " + blListener.elements); + assertEquals(2, blListener.elements.size()); + for (TargetObject obj : blListener.elements.values()) { + TargetBreakpointLocation eb = obj.as(TargetBreakpointLocation.tclass); + ebAddresses.add(eb.getAddress()); + ebAffecteds.addAll(eb.getAffects()); + } + Msg.debug(this, "Addresses: " + ebAddresses + ", affected: " + ebAffecteds); + assertEquals(1, ebAddresses.size()); + assertEquals(Set.of(List.of("Inferiors", "[1]"), List.of("Inferiors", "[2]")), + ebAffecteds.stream().map(TargetObjectRef::getPath).collect(Collectors.toSet())); + + // Getting more precise than this could become fragile, as library paths vary + TargetEventType lastType = null; + List typesNoRepeat = new ArrayList<>(); + for (EventRecord rec : evtListener.events) { + if (rec.type == TargetEventType.RUNNING) { + continue; + } + if (rec.type == lastType) { + continue; + } + lastType = rec.type; + typesNoRepeat.add(lastType); + } + assertEquals(List.of( + TargetEventType.PROCESS_CREATED, + TargetEventType.THREAD_CREATED, + TargetEventType.MODULE_LOADED, + TargetEventType.BREAKPOINT_HIT, + TargetEventType.PROCESS_CREATED, + TargetEventType.THREAD_CREATED, + TargetEventType.MODULE_LOADED // + ), typesNoRepeat); + } + } + + @Test + public void testExpClone() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject("Inferiors", "[1]")); + Msg.debug(this, "Launching..."); + launch(inferior, + Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, which("expCloneExit"))); + Msg.debug(this, "Waiting for session access (again)..."); + waitAcc(access); + TargetBreakpointContainer breaks = + suitable(TargetBreakpointContainer.class, inferior); + Msg.debug(this, "Setting break on work"); + waitOn(breaks.placeBreakpoint("work", Set.of(TargetBreakpointKind.SOFTWARE))); + Msg.debug(this, "Resuming execution (first time)"); + resume(inferior); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Map threads = + waitOn(model.fetchObjectElements("Inferiors", "[1]", "Threads")); + Msg.debug(this, "After first break, threads are: " + threads); + assertEquals(2, threads.size()); + } + } + + @Test + public void testExpWrite() throws Throwable { + String expPrint = which("expPrint"); + final String toWrite = "Speak"; + final String expected = "Speak, World!"; + + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject("Inferiors", "[1]")); + Msg.debug(this, "Launching..."); + launch(inferior, Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expPrint)); + Msg.debug(this, "Waiting for session access (again)..."); + waitAcc(access); + Msg.debug(this, "Getting symbol to overwrite"); + TargetSymbol overwrite = waitOn(inferior.fetchSuccessor( + "Modules", "[" + expPrint + "]", "Symbols", "[overwrite]")).as(TargetSymbol.tclass); + Msg.debug(this, "Symbol 'overwrite' is at addr: " + overwrite.getValue()); + Msg.debug(this, "Getting Memory"); + TargetMemory memory = (TargetMemory) waitOn(inferior.fetchSuccessor("Memory")); + Msg.debug(this, "Writing"); + waitOn(memory.writeMemory(overwrite.getValue(), toWrite.getBytes())); + Msg.debug(this, "Getting thread (for stepping)"); + TargetObject thread = waitOn(inferior.fetchSuccessor("Threads", "[1]")); + Msg.debug(this, "Got: " + thread); + Msg.debug(this, "Stepping to clear caches..."); + step(thread, TargetStepKind.INTO); + Msg.debug(this, "Waiting for access..."); + waitAcc(access); + Msg.debug(this, "Reading back..."); + byte[] data = + waitOn(memory.readMemory(overwrite.getValue(), expected.getBytes().length)); + Msg.debug(this, "Read: " + new String(data)); + assertArrayEquals(expected.getBytes(), data); + resume(inferior); + Msg.debug(this, "Waiting for access (i.e., exit)..."); + waitAcc(access); + Msg.debug(this, "Getting exit code..."); + + long status = inferior.getTypedAttributeNowByName( + GdbModelTargetInferior.EXIT_CODE_ATTRIBUTE_NAME, Long.class, 0L); + assertEquals(toWrite.getBytes()[0], status); + } + } + + @Test + public void testStack() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject("Inferiors", "[1]")); + Msg.debug(this, "Launching..."); + launch(inferior, Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "echo Hello, World!")); + Msg.debug(this, "Waiting for session access (again)..."); + waitAcc(access); + Msg.debug(this, "Finding breakpoint container..."); + TargetBreakpointContainer breaks = suitable(TargetBreakpointContainer.tclass, root); + Msg.debug(this, "Placing breakpoint..."); + waitOn(breaks.placeBreakpoint("write", Set.of(TargetBreakpointKind.SOFTWARE))); + Msg.debug(this, "Resuming..."); + resume(inferior); + Msg.debug(this, "Waiting for session access (after resume)..."); + waitAcc(access); + Map frames = + waitOn(inferior.fetchSubElements("Threads", "[1]", "Stack") + .thenCompose(DebugModelConventions::fetchAll)); + Msg.debug(this, "Got stack:"); + for (Map.Entry ent : frames.entrySet()) { + TargetStackFrame frame = ent.getValue().as(TargetStackFrame.tclass); + Msg.debug(this, ent.getKey() + ": " + frame.getProgramCounter()); + } + assertEquals("write", frames.get("0") + .getTypedAttributeNowByName(GdbModelTargetStackFrame.FUNC_ATTRIBUTE_NAME, + String.class, null)); + assertEquals("main", frames.get("" + (frames.size() - 1)) + .getTypedAttributeNowByName(GdbModelTargetStackFrame.FUNC_ATTRIBUTE_NAME, + String.class, null)); + } + } + + @Test + public void testRegisters() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + Set> descs = new LinkedHashSet<>(); + + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject("Inferiors", "[1]")); + launch(inferior, Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "echo Hello, World!")); + Msg.debug(this, "Waiting for session access (again)..."); + waitAcc(access); + TargetRegisterBank bank = + waitOn(inferior.fetchSuccessor("Threads", "[1]", "Stack", "[0]")) + .as(TargetRegisterBank.tclass); + Msg.debug(this, "Got bank: " + bank); + Msg.debug(this, "Descriptions ref: " + bank.getDescriptions()); + TargetRegisterContainer cont = waitOn(bank.getDescriptions().fetch()); + Msg.debug(this, "Register descriptions: " + cont); + descs.addAll(waitOn(cont.getRegisters())); + Msg.debug(this, "Elements: "); + for (TargetRegister reg : descs) { + Msg.debug(this, " " + reg.getIndex() + ": " + reg.getBitLength()); + } + Map data = waitOn(bank.readRegisters(descs)); + Msg.debug(this, "Values: "); + for (Map.Entry ent : data.entrySet()) { + Msg.debug(this, " " + ent.getKey() + " = " + + NumericUtilities.convertBytesToString(ent.getValue())); + } + // TODO: Implement Environment, and port these tests + Msg.debug(this, "Writing two registers, general and vector"); + waitOn(bank.writeRegistersNamed(AMD64_TEST_REG_VALUES)); + Msg.debug(this, "Flushing cache"); + waitOn(bank.invalidateCaches()); + Msg.debug(this, "Re-reading values"); + // NOTE: Can't reliably step here since rax may be referenced by the instruction + data = waitOn(bank.readRegistersNamed(AMD64_TEST_REG_VALUES.keySet())); + assertEquals(hexlify(AMD64_TEST_REG_VALUES), hexlify(data)); + } + } + + @Test + public void testFocusInferiors() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + AsyncReference focus = new AsyncReference<>(); + AsyncReference inferiorCount = new AsyncReference<>(); + + TargetFocusScopeListener focusListener = new TargetFocusScopeListener() { + @Override + public void focusChanged(TargetFocusScope object, TargetObjectRef focused) { + focus.set(focused, null); + } + }; + + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + root.addListener(focusListener); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting inferiors container"); + TargetObject infCont = waitOn(model.fetchModelObject("Inferiors")); + + TargetObjectListener infListener = new TargetObjectListener() { + @Override + public void elementsChanged(TargetObject parent, Collection removed, + Map added) { + inferiorCount.set(infCont.getCachedElements().size(), null); + } + }; + + infCont.addListener(infListener); + Msg.debug(this, "Creating another inferior"); + cli(root, "add-inferior"); + waitOn(inferiorCount.waitValue(2)); + assertEquals(model.createRef("Inferiors", "[1]"), getFocus(root)); + focus(root, model.createRef("Inferiors", "[2]")); + assertEquals(model.createRef("Inferiors", "[2]"), getFocus(root)); + } + } + + @Test + public void testThreadFocusOnLaunch() throws Throwable { + try (ModelHost m = modelHost()) { + DebuggerObjectModel model = m.getModel(); + + Deque focusSeq = new LinkedList<>(); + AsyncReference focusSeqSize = new AsyncReference<>(); + + TargetFocusScopeListener focusListener = new TargetFocusScopeListener() { + @Override + public void focusChanged(TargetFocusScope object, TargetObjectRef focused) { + Msg.debug(this, "Focused: " + focused); + if (focused instanceof TargetProcess) { + return; + } + synchronized (focusSeq) { + focusSeq.add(focused); + focusSeqSize.set(focusSeq.size(), null); + } + } + }; + + init(m); + Msg.debug(this, "Getting root object"); + TargetObject root = root(model); + root.addListener(focusListener); + Msg.debug(this, "Tracking session access..."); + AllRequiredAccess access = access(root); + Msg.debug(this, "Waiting for session access..."); + waitAcc(access); + Msg.debug(this, "Getting Inferior 1..."); + TargetObject inferior = waitOn(model.fetchModelObject(List.of("Inferiors", "[1]"))); + Msg.debug(this, "Launching"); + launch(inferior, + Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "/bin/echo Hello, World!")); + waitOn(focusSeqSize.waitValue(1)); + assertEquals(model.createRef(PathUtils.parse("Inferiors[1].Threads[1].Stack[0]")), + focusSeq.peekLast()); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/EventSequenceListener.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/EventSequenceListener.java new file mode 100644 index 0000000000..8266257697 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/EventSequenceListener.java @@ -0,0 +1,88 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model; + +import java.util.*; + +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.target.TargetEventScope; +import ghidra.dbg.target.TargetEventScope.TargetEventScopeListener; +import ghidra.dbg.target.TargetEventScope.TargetEventType; +import ghidra.dbg.target.TargetThread; + +public class EventSequenceListener implements TargetEventScopeListener { + public static class EventRecord { + public final TargetEventScope object; + public final TypedTargetObjectRef> eventThread; + public final TargetEventType type; + public final String description; + public final List parameters; + + public EventRecord(TargetEventScope object, + TypedTargetObjectRef> eventThread, TargetEventType type, + String description, List parameters) { + this.object = object; + this.eventThread = eventThread; + this.type = type; + this.description = description; + this.parameters = parameters; + } + + @Override + public String toString() { + return String.format(" events = new ArrayList<>(); + + @Override + public void event(TargetEventScope object, + TypedTargetObjectRef> eventThread, TargetEventType type, + String description, List parameters) { + events.add(new EventRecord(object, eventThread, type, description, parameters)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/GadpForGdbTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/GadpForGdbTest.java new file mode 100644 index 0000000000..d3e445ee15 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/GadpForGdbTest.java @@ -0,0 +1,122 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; + +import agent.gdb.gadp.GdbGadpServer; +import agent.gdb.manager.GdbManager; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.gadp.client.GadpClient; +import ghidra.dbg.gadp.client.GadpClientTestHelper; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.util.Msg; + +public class GadpForGdbTest extends AbstractModelForGdbTest { + + class GdbGadpModelHost implements ModelHost { + final GdbGadpServer server; + final SocketAddress addr; + final AsynchronousSocketChannel socket; + final GadpClient client; + + GdbGadpModelHost(String gdbCmd) throws Exception { + server = GdbGadpServer.newInstance(new InetSocketAddress("localhost", 0)); + server.startGDB(gdbCmd, new String[] {}) + /*.get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS)*/; + addr = server.getLocalAddress(); + + socket = AsynchronousSocketChannel.open(); + client = new GadpClient("Test", socket); + } + + @Override + public CompletableFuture init() { + Msg.debug(this, "Connecting..."); + return AsyncUtils.completable(TypeSpec.VOID, socket::connect, addr).thenCompose(__ -> { + Msg.debug(this, "Negotiating..."); + return client.connect(); + }); + } + + @Override + public DebuggerObjectModel getModel() { + return client; + } + + @Override + public void close() throws Exception { + // Not too eww + Msg.debug(this, "Disconnecting..."); + try { + waitOn(client.close()); + } + catch (Exception e) { + throw e; + } + catch (Throwable e) { + throw new AssertionError(e); + } + server.terminate(); + } + } + + @Override + protected GdbGadpModelHost modelHost() throws Exception { + return modelHost(GdbManager.DEFAULT_GDB_CMD); + } + + @Override + protected GdbGadpModelHost modelHost(String gdbCmd) throws Exception { + return new GdbGadpModelHost(gdbCmd); + } + + @Test + public void testBadRequest() throws Throwable { + try (GdbGadpModelHost m = modelHost()) { + init(m); + Msg.debug(this, "Sending bogus message..."); + waitOn( + GadpClientTestHelper.sendChecked(m.client, Gadp.ErrorRequest.newBuilder(), null)); + fail("Exception expected"); + } + catch (AssertionError e) { + assertEquals( + "Client implementation sent an invalid request: " + + "BAD_REQUEST: Unrecognized request: ERROR_REQUEST", + e.getMessage()); + } + } + + @Test + public void testPing() throws Throwable { + try (GdbGadpModelHost m = modelHost()) { + waitOn(m.init()); + Msg.debug(this, "Pinging..."); + waitOn(m.client.ping("Hello, Ghidra Async Debugging!")); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/ModelForGdbTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/ModelForGdbTest.java new file mode 100644 index 0000000000..b854a2634e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/ModelForGdbTest.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.model; + +import java.util.concurrent.CompletableFuture; + +import agent.gdb.model.impl.GdbModelImpl; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.util.Msg; + +public class ModelForGdbTest extends AbstractModelForGdbTest { + + static class GdbGadpModelHost implements ModelHost { + final GdbModelImpl model; + final String gdbCmd; + + GdbGadpModelHost(String gdbCmd) { + model = new GdbModelImpl(); + this.gdbCmd = gdbCmd; + } + + @Override + public CompletableFuture init() { + Msg.debug(this, "Starting GDB..."); + return model.startGDB(gdbCmd, new String[] {}); + } + + @Override + public DebuggerObjectModel getModel() { + return model; + } + + @Override + public void close() throws Exception { + model.terminate(); + } + } + + @Override + protected ModelHost modelHost(String gdbCmd) throws Exception { + return new GdbGadpModelHost(gdbCmd); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/Module.manifest b/Ghidra/Debug/Debugger-gadp/Module.manifest new file mode 100644 index 0000000000..35e983519d --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/Module.manifest @@ -0,0 +1 @@ +MODULE FILE LICENSE: lib/protobuf-java-3.11.1.jar BSD diff --git a/Ghidra/Debug/Debugger-gadp/build.gradle b/Ghidra/Debug/Debugger-gadp/build.gradle new file mode 100644 index 0000000000..c4ca92a521 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/build.gradle @@ -0,0 +1,81 @@ + +/*plugins { + id 'com.google.protobuf' version '0.8.10' +}*/ + +apply from: "${rootProject.projectDir}/gradle/javaProject.gradle" +apply from: "${rootProject.projectDir}/gradle/jacocoProject.gradle" +apply from: "${rootProject.projectDir}/gradle/javaTestProject.gradle" +apply from: "${rootProject.projectDir}/gradle/distributableGhidraModule.gradle" + +apply plugin: 'eclipse' +eclipse.project.name = 'Debug Debugger-gadp' + +configurations { + allProtocArtifacts + protocArtifact +} + +def os = org.gradle.internal.os.OperatingSystem.current() + +dependencies { + allProtocArtifacts 'com.google.protobuf:protoc:3.11.1:windows-x86_64@exe' + allProtocArtifacts 'com.google.protobuf:protoc:3.11.1:linux-x86_64@exe' + allProtocArtifacts 'com.google.protobuf:protoc:3.11.1:osx-x86_64@exe' + + if (os.isWindows()) { + protocArtifact 'com.google.protobuf:protoc:3.11.1:windows-x86_64@exe' + } + if (os.isLinux()) { + protocArtifact 'com.google.protobuf:protoc:3.11.1:linux-x86_64@exe' + } + if (os.isMacOsX()) { + protocArtifact 'com.google.protobuf:protoc:3.11.1:osx-x86_64@exe' + } + + compile 'com.google.protobuf:protobuf-java:3.11.1' + compile project(':Framework-AsyncComm') + compile project(':Framework-Debugging') + compile project(':ProposedUtils') + + testCompile project(path: ':Framework-Debugging', configuration: 'testArtifacts') +} + +/*protobuf { + protoc { + artifact = 'com.google.protobuf:protoc:3.11.1' + } +}*/ + +task generateProto { + ext.srcdir = file("src/main/proto") + ext.src = fileTree(srcdir) { + include "**/*.proto" + } + ext.outdir = file("build/generated/source/proto/main/java") + outputs.dir(outdir) + inputs.files(src) + dependsOn(configurations.protocArtifact) + doLast { + def exe = configurations.protocArtifact.first() + if (!os.isWindows()) { + exe.setExecutable(true) + } + exec { + commandLine exe, "--java_out=$outdir", "-I$srcdir" + args src + } + } +} + +tasks.compileJava.dependsOn(tasks.generateProto) +tasks.eclipse.dependsOn(tasks.generateProto) +rootProject.tasks.prepDev.dependsOn(tasks.generateProto) + +sourceSets { + main { + java { + srcDir tasks.generateProto.outdir + } + } +} diff --git a/Ghidra/Debug/Debugger-gadp/certification.manifest b/Ghidra/Debug/Debugger-gadp/certification.manifest new file mode 100644 index 0000000000..69ff04b164 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/certification.manifest @@ -0,0 +1,6 @@ +##VERSION: 2.0 +##MODULE IP: BSD +.classpath||NONE||reviewed||END| +.project||NONE||reviewed||END| +Module.manifest||GHIDRA||||END| +build.gradle||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/GadpRegistry.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/GadpRegistry.java new file mode 100644 index 0000000000..37b9138182 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/GadpRegistry.java @@ -0,0 +1,132 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.apache.commons.collections4.BidiMap; +import org.apache.commons.collections4.bidimap.DualHashBidiMap; + +import com.google.protobuf.Message; + +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.gadp.client.*; +import ghidra.dbg.target.*; +import utilities.util.reflection.ReflectionUtilities; + +public enum GadpRegistry { + ; + + public interface InvocationBuilder { + Message.Builder buildMessage(String oid, Object[] args); + } + + public interface ServerInvoker { + CompletableFuture invoke(TargetObject object, M msg); + } + + public static final BidiMap> INTERFACE_REGISTRY = + new DualHashBidiMap<>(); + + public static final Map, Class> MIXIN_REGISTRY = + new HashMap<>(); + + public static void registerInterface(Class iface, + Class mixin) { + String name = DebuggerObjectModel.requireIfaceName(iface); + INTERFACE_REGISTRY.put(name, iface); + MIXIN_REGISTRY.put(iface, mixin); + } + + static { + registerInterface(TargetAccessConditioned.class, GadpClientTargetAccessConditioned.class); + registerInterface(TargetAggregate.class, GadpClientTargetAggregate.class); + registerInterface(TargetAttachable.class, GadpClientTargetAttachable.class); + registerInterface(TargetAttacher.class, GadpClientTargetAttacher.class); + registerInterface(TargetBreakpointContainer.class, + GadpClientTargetBreakpointContainer.class); + registerInterface(TargetBreakpointSpec.class, GadpClientTargetBreakpointSpec.class); + registerInterface(TargetDataTypeMember.class, GadpClientTargetDataTypeMember.class); + registerInterface(TargetDataTypeNamespace.class, GadpClientTargetDataTypeNamespace.class); + registerInterface(TargetDeletable.class, GadpClientTargetDeletable.class); + registerInterface(TargetDetachable.class, GadpClientTargetDetachable.class); + registerInterface(TargetBreakpointLocation.class, GadpClientTargetBreakpointLocation.class); + registerInterface(TargetEnvironment.class, GadpClientTargetEnvironment.class); + registerInterface(TargetEventScope.class, GadpClientTargetEventScope.class); + registerInterface(TargetExecutionStateful.class, GadpClientTargetExecutionStateful.class); + registerInterface(TargetFocusScope.class, GadpClientTargetFocusScope.class); + registerInterface(TargetInterpreter.class, GadpClientTargetInterpreter.class); + registerInterface(TargetInterruptible.class, GadpClientTargetInterruptible.class); + registerInterface(TargetKillable.class, GadpClientTargetKillable.class); + registerInterface(TargetLauncher.class, GadpClientTargetLauncher.class); + registerInterface(TargetMethod.class, GadpClientTargetMethod.class); + registerInterface(TargetMemory.class, GadpClientTargetMemory.class); + registerInterface(TargetMemoryRegion.class, GadpClientTargetMemoryRegion.class); + registerInterface(TargetModule.class, GadpClientTargetModule.class); + registerInterface(TargetModuleContainer.class, GadpClientTargetModuleContainer.class); + registerInterface(TargetNamedDataType.class, GadpClientTargetNamedDataType.class); + registerInterface(TargetProcess.class, GadpClientTargetProcess.class); + registerInterface(TargetRegister.class, GadpClientTargetRegister.class); + registerInterface(TargetRegisterBank.class, GadpClientTargetRegisterBank.class); + registerInterface(TargetRegisterContainer.class, GadpClientTargetRegisterContainer.class); + registerInterface(TargetResumable.class, GadpClientTargetResumable.class); + registerInterface(TargetSection.class, GadpClientTargetSection.class); + registerInterface(TargetStack.class, GadpClientTargetStack.class); + registerInterface(TargetStackFrame.class, GadpClientTargetStackFrame.class); + registerInterface(TargetSteppable.class, GadpClientTargetSteppable.class); + registerInterface(TargetSymbol.class, GadpClientTargetSymbol.class); + registerInterface(TargetSymbolNamespace.class, GadpClientTargetSymbolNamespace.class); + registerInterface(TargetThread.class, GadpClientTargetThread.class); + } + + public static List> getInterfacesByName( + Collection names) { + return names.stream() + .filter(INTERFACE_REGISTRY::containsKey) + .map(INTERFACE_REGISTRY::get) + .collect(Collectors.toList()); + } + + public static List> getMixinsByName(List names) { + return names.stream() + .filter(INTERFACE_REGISTRY::containsKey) + .map(INTERFACE_REGISTRY::get) + .map(MIXIN_REGISTRY::get) + .collect(Collectors.toList()); + } + + public static List> getMixins( + List> ifaces) { + return ifaces.stream().map(MIXIN_REGISTRY::get).collect(Collectors.toList()); + } + + public static List getInterfaceNames(TargetObject obj) { + List result = new ArrayList<>(); + for (Class parent : ReflectionUtilities.getAllParents(obj.getClass())) { + String name = getName(parent); + if (name != null) { + result.add(name); + } + } + return result; + } + + public static String getName(Class cls) { + return INTERFACE_REGISTRY.getKey(cls); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/GadpVersion.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/GadpVersion.java new file mode 100644 index 0000000000..d824edad45 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/GadpVersion.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp; + +import java.util.NoSuchElementException; + +import ghidra.dbg.gadp.protocol.Gadp; + +public enum GadpVersion { + VER1 { + @Override + public String getName() { + return "gadp1"; + } + }; + + public static Gadp.ConnectRequest.Builder makeRequest() { + Gadp.ConnectRequest.Builder req = Gadp.ConnectRequest.newBuilder(); + for (GadpVersion ver : values()) { + req.addVersion(ver.getName()); + } + return req; + } + + public static GadpVersion getByName(String name) { + for (GadpVersion ver : values()) { + if (name.equals(ver.getName())) { + return ver; + } + } + throw new NoSuchElementException("Unknown version: " + name); + } + + public abstract String getName(); +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/DelegateGadpClientTargetObject.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/DelegateGadpClientTargetObject.java new file mode 100644 index 0000000000..f54c2b91aa --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/DelegateGadpClientTargetObject.java @@ -0,0 +1,545 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.ref.Cleaner.Cleanable; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.gadp.GadpRegistry; +import ghidra.dbg.gadp.client.annot.GadpAttributeChangeCallback; +import ghidra.dbg.gadp.client.annot.GadpEventHandler; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.protocol.Gadp.EventNotification.EvtCase; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.memory.CachedMemory; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibility; +import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibilityListener; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointAction; +import ghidra.dbg.util.CollectionUtils.Delta; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.AddressSpace; +import ghidra.util.Msg; +import ghidra.util.datastruct.ListenerSet; +import utilities.util.ProxyUtilities; + +/** + * This class is meant to be used as a delegate to a composed proxy + */ +public class DelegateGadpClientTargetObject implements GadpClientTargetObject { + protected abstract static class GadpHandlerMap { + protected final Class annotationType; + protected final Class[] paramClasses; + protected final Map handles = new HashMap<>(); + + public GadpHandlerMap(Class annotationType, Class[] paramClasses) { + this.annotationType = annotationType; + this.paramClasses = paramClasses; + } + + protected abstract K getKey(A annot); + + protected void compose(GadpHandlerMap that) { + for (Map.Entry ent : that.handles.entrySet()) { + MethodHandle old = handles.put(ent.getKey(), ent.getValue()); + if (old != null) { + throw new AssertionError("Conflict over handler for " + ent.getKey() + + ": " + old + " and " + ent.getValue()); + } + } + } + + protected void register(K key, MethodHandle handle) { + MethodHandle old = handles.put(key, handle); + if (old != null) { + throw new AssertionError( + "Conflict over handler for " + key + ": " + old + " and " + handle); + } + } + + protected void registerInterface(Class iface) { + for (Method method : iface.getDeclaredMethods()) { + A annot = method.getDeclaredAnnotation(annotationType); + if (annot == null) { + continue; + } + if (!Arrays.equals(paramClasses, method.getParameterTypes())) { + throw new AssertionError("@" + annotationType.getSimpleName() + + " methods must have typed parameters: " + paramClasses); + } + MethodHandle handle; + try { + handle = ProxyUtilities.getSuperMethodHandle(method, LOOKUP); + } + catch (IllegalAccessException e) { + throw new AssertionError(e); + } + register(getKey(annot), handle); + } + } + + protected void handle(GadpClientTargetObject proxy, K key, Object... params) { + MethodHandle handle = handles.get(key); + if (handle == null) { + //Msg.info(this, "Received unknown handler key: " + key); + return; + } + try { + handle.bindTo(proxy).invokeWithArguments(params); + } + catch (Throwable e) { + Msg.error(this, "Problem processing key: " + key, e); + } + } + } + + protected static class GadpEventHandlerMap extends GadpHandlerMap { + protected static final Class[] PARAMETER_CLASSES = + new Class[] { Gadp.EventNotification.class }; + + public GadpEventHandlerMap(Set> ifaces) { + super(GadpEventHandler.class, PARAMETER_CLASSES); + for (Class iface : ifaces) { + registerInterface(iface); + } + } + + @Override + protected EvtCase getKey(GadpEventHandler annot) { + return annot.value(); + } + } + + protected static class GadpAttributeChangeCallbackMap + extends GadpHandlerMap { + protected static final Class[] PARAMETER_CLASSES = new Class[] { Object.class }; + + public GadpAttributeChangeCallbackMap(Set> ifaces) { + super(GadpAttributeChangeCallback.class, PARAMETER_CLASSES); + for (Class iface : ifaces) { + registerInterface(iface); + } + } + + @Override + protected String getKey(GadpAttributeChangeCallback annot) { + return annot.value(); + } + } + + protected static class ProxyState implements Runnable { + protected final GadpClient client; + protected final List path; + protected boolean valid = true; + + public ProxyState(GadpClient client, List path) { + this.client = client; + this.path = path; + } + + @Override + public void run() { + if (!valid) { + return; + } + client.unsubscribe(path).exceptionally(e -> { + Msg.error(this, "Could not unsubscribe from " + path + ": " + e); + return null; + }); + } + } + + protected static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + protected static final Map>, GadpEventHandlerMap> EVENT_HANDLER_MAPS_BY_COMPOSITION = + new HashMap<>(); + protected static final Map>, GadpAttributeChangeCallbackMap> ATTRIBUTE_CHANGE_CALLBACKS_MAPS_BY_COMPOSITION = + new HashMap<>(); + + protected static GadpClientTargetObject makeModelProxy(GadpClient client, List path, + String typeHint, List ifaceNames) { + List> ifaces = GadpRegistry.getInterfacesByName(ifaceNames); + List> mixins = GadpRegistry.getMixins(ifaces); + return new DelegateGadpClientTargetObject(client, path, typeHint, ifaceNames, ifaces, + mixins).proxy; + } + + protected final ProxyState state; + protected final int hash; + protected final Cleanable cleanable; + + private final GadpClientTargetObject proxy; + private final String typeHint; + private final List ifaceNames; + private final List> ifaces; + private final GadpEventHandlerMap eventHandlers; + private final GadpAttributeChangeCallbackMap attributeChangeCallbacks; + protected final ListenerSet listeners = + new ListenerSet<>(TargetObjectListener.class); + + // TODO: Use path element comparators? + protected final Map elements = new TreeMap<>(); + // TODO: Use path element comparators? + protected final Map attributes = new TreeMap<>(); + + protected Map memCache = null; // Becomes active if this is a TargetMemory + protected Map regCache = null; // Becomes active if this is a TargtRegisterBank + protected ListenerSet actions = null; // Becomes active is this is a TargetBreakpointSpec + + public DelegateGadpClientTargetObject(GadpClient client, List path, String typeHint, + List ifaceNames, List> ifaces, + List> mixins) { + this.state = new ProxyState(client, path); + this.hash = computeHashCode(); + this.cleanable = GadpClient.CLEANER.register(this, state); + + this.proxy = ProxyUtilities.composeOnDelegate(GadpClientTargetObject.class, + this, mixins, MethodHandles.lookup()); + this.typeHint = typeHint; + this.ifaceNames = ifaceNames; + this.ifaces = ifaces; + + Set> allMixins = new HashSet<>(mixins); + allMixins.add(GadpClientTargetObject.class); + this.eventHandlers = EVENT_HANDLER_MAPS_BY_COMPOSITION.computeIfAbsent(allMixins, + GadpEventHandlerMap::new); + this.attributeChangeCallbacks = + ATTRIBUTE_CHANGE_CALLBACKS_MAPS_BY_COMPOSITION.computeIfAbsent(allMixins, + GadpAttributeChangeCallbackMap::new); + } + + @Override + public boolean equals(Object obj) { + return doEquals(obj); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + return ""; + } + + @Override + public GadpClient getModel() { + return state.client; + } + + @Override + public List getProtocolID() { + return state.path; + } + + @Override + public List getPath() { + return state.path; + } + + @Override + public String getTypeHint() { + return typeHint; + } + + @Override + public Collection getInterfaceNames() { + return ifaceNames; + } + + @Override + public Collection> getInterfaces() { + return ifaces; + } + + @Override + public boolean isValid() { + return state.valid; + } + + @Override + public Map getCachedElements() { + synchronized (this.elements) { + return Map.copyOf(elements); + } + } + + @Override + public Map getCachedAttributes() { + synchronized (this.attributes) { + return Map.copyOf(attributes); + } + } + + @Override + public Object getCachedAttribute(String name) { + synchronized (attributes) { + return attributes.get(name); + } + } + + protected void putCachedProxy(String key, GadpClientTargetObject proxy) { + if (PathUtils.isIndex(key)) { + synchronized (elements) { + elements.put(PathUtils.parseIndex(key), proxy); + } + } + else { + synchronized (attributes) { + attributes.put(key, proxy); + } + } + } + + protected Optional cachedChild(String key) { + /** + * TODO: Object metadata which indicates whether the attributes/elements support + * subscription (push notifications). Otherwise, if the parent is cached, GADP will assume + * the server is sending updates. If the model actually requires pulling, the GADP client + * will not know, and will instead use its (likely stale) cache. + */ + assert key != null; + if (PathUtils.isIndex(key)) { + /** + * NOTE: I do not need to check the subscription level. The level has to do with + * including object info. Having OBJECT level is sufficient to have up-to-date keys. + */ + synchronized (elements) { + return Optional.ofNullable(elements.get(PathUtils.parseIndex(key))); + } + } + assert PathUtils.isName(key); + synchronized (attributes) { + return Optional.ofNullable(attributes.get(key)); + } + } + + /** + * {@inheritDoc} + * + * The delegate has to override defaults which introspect on, or otherwise would leak "this". + * "this" is the delegate; we must instead operate on the proxy. + */ + @Override + public > T as(Class iface) { + return DebuggerObjectModel.requireIface(iface, proxy, state.path); + } + + @Override + public CompletableFuture fetch() { + return CompletableFuture.completedFuture(proxy); + } + + @Override + public CompletableFuture fetchAttribute(String name) { + if (!PathUtils.isInvocation(name)) { + return GadpClientTargetObject.super.fetchAttribute(name); + } + return state.client.fetchModelValue(PathUtils.extend(state.path, name)); + } + + @Override + public void addListener(TargetObjectListener l) { + listeners.add(l); + } + + @Override + public void removeListener(TargetObjectListener l) { + listeners.remove(l); + } + + @Override + public DelegateGadpClientTargetObject getDelegate() { + return this; + } + + public void updateWithInfo(Gadp.ModelObjectInfo info) { + Map elements = + GadpValueUtils.getElementMap(this, info.getElementIndexList()); + Map attributes = + GadpValueUtils.getAttributeMap(this, info.getAttributeList()); + + Delta deltaE = setElements(elements); + Delta deltaA = setAttributes(attributes); + fireElementsChanged(deltaE); + fireAttributesChanged(deltaA); + } + + public void updateWithDelta(Gadp.ModelObjectDelta delta) { + Map elementsAdded = + GadpValueUtils.getElementMap(this, delta.getIndexAddedList()); + Map attributesAdded = + GadpValueUtils.getAttributeMap(this, delta.getAttributeAddedList()); + + Delta deltaE = + updateElements(Delta.create(delta.getIndexRemovedList(), elementsAdded)); + Delta deltaA = + updateAttributes(Delta.create(delta.getAttributeRemovedList(), attributesAdded)); + fireElementsChanged(deltaE); + fireAttributesChanged(deltaA); + } + + protected void handleEvent(Gadp.EventNotification notify) { + eventHandlers.handle(proxy, notify.getEvtCase(), notify); + } + + /** + * Translate the given attribute change into an interface-specific property change, if + * applicable, and invokes the appropriate listeners + * + * @implNote The model interface allow listening for attribute changes in general, as well as + * some interface-specific property changes. The convention in model is for such + * properties to be encoded as an attribute with a specified name, e.g., the + * 'accessible' attribute encodes the valcachedAue for + * {@link TargetAccessConditioned#getAccessibility()}, and changes will invoke + * {@link TargetAccessibilityListener#accessibilityChanged(TargetAccessConditioned, TargetAccessibility)}. + * Taking advantage of the general attribute getting/change-listening convention, GADP + * communicates only the attribute changes, and then invokes the corresponding + * interface-specific property change listeners on the client side. So long as the + * model implementation on the server side follows the convention, then the + * client-side proxies will obey the same. If not, then client-side behavior is + * undefined. + * + * @see GadpAttributeChangeCallback + * + * @param name the name of the attribute + * @param value the new value of the attribute + */ + protected void handleAttributeChange(String name, Object value) { + attributeChangeCallbacks.handle(proxy, name, value); + } + + protected Delta updateElements( + Delta delta) { + synchronized (this.elements) { + return delta.apply(elements); + } + } + + protected Delta setElements( + Map elements) { + synchronized (this.elements) { + return Delta.computeAndSet(this.elements, elements, Delta.SAME); + } + } + + protected Delta updateAttributes(Delta delta) { + synchronized (this.attributes) { + return delta.apply(attributes, Delta.EQUAL); + } + } + + protected Delta setAttributes(Map attributes) { + synchronized (this.attributes) { + return Delta.computeAndSet(this.attributes, attributes, Delta.EQUAL); + } + } + + protected void fireElementsChanged(Delta delta) { + if (!delta.isEmpty()) { + listeners.fire.elementsChanged(proxy, delta.getKeysRemoved(), delta.added); + } + } + + protected void fireAttributesChanged(Delta delta) { + if (!delta.isEmpty()) { + listeners.fire.attributesChanged(proxy, delta.getKeysRemoved(), delta.added); + for (Map.Entry a : delta.added.entrySet()) { + handleAttributeChange(a.getKey(), a.getValue()); + } + } + } + + protected void doInvalidateSubtree(String reason) { + state.client.invalidateSubtree(state.path, reason); + } + + protected void doInvalidate(String reason) { + state.valid = false; + listeners.fire.invalidated(proxy, reason); + } + + protected void assertValid() { + if (!state.valid) { + throw new IllegalStateException("Object is no longer valid: " + toString()); + } + } + + protected void doClearCaches() { + clearMemCacheEntries(); + clearRegisterCacheEntries(); + } + + @Override + public synchronized CompletableFuture invalidateCaches() { + assertValid(); + doClearCaches(); + return state.client.sendChecked(Gadp.CacheInvalidateRequest.newBuilder() + .setPath(GadpValueUtils.makePath(state.path)), + Gadp.CacheInvalidateReply.getDefaultInstance()).thenApply(rep -> null); + } + + protected synchronized CachedMemory getMemoryCache(AddressSpace space) { + GadpClientTargetMemory memory = (GadpClientTargetMemory) proxy; + if (memCache == null) { + memCache = new HashMap<>(); + } + return memCache.computeIfAbsent(space, + s -> new CachedMemory(memory.getRawReader(s), memory.getRawWriter(s))); + } + + protected synchronized void clearMemCacheEntries() { + if (memCache == null) { + return; + } + for (CachedMemory mem : memCache.values()) { + mem.clear(); + } + } + + protected synchronized Map getRegisterCache() { + if (regCache == null) { + regCache = Collections.synchronizedMap(new HashMap<>()); + } + return regCache; + } + + protected synchronized void clearRegisterCacheEntries() { + if (regCache != null) { + regCache.clear(); + } + } + + protected synchronized ListenerSet getActions(boolean createIfAbsent) { + if (actions == null && createIfAbsent) { + actions = new ListenerSet<>(TargetBreakpointAction.class) { + // Want strong references on actions + protected Map createMap() { + return Collections.synchronizedMap(new LinkedHashMap<>()); + }; + }; + } + return actions; + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClient.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClient.java new file mode 100644 index 0000000000..16ff6d44d2 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClient.java @@ -0,0 +1,822 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.io.EOFException; +import java.io.IOException; +import java.lang.ref.Cleaner; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import com.google.common.cache.RemovalNotification; +import com.google.protobuf.Message; +import com.google.protobuf.ProtocolStringList; + +import ghidra.async.*; +import ghidra.dbg.*; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.error.*; +import ghidra.dbg.gadp.GadpVersion; +import ghidra.dbg.gadp.error.*; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.protocol.Gadp.*; +import ghidra.dbg.gadp.util.*; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetObject.TargetUpdateMode; +import ghidra.dbg.util.PathUtils; +import ghidra.dbg.util.PathUtils.PathComparator; +import ghidra.lifecycle.Internal; +import ghidra.program.model.address.*; +import ghidra.util.*; +import ghidra.util.datastruct.ListenerSet; +import ghidra.util.datastruct.WeakValueTreeMap; +import ghidra.util.exception.DuplicateNameException; + +public class GadpClient implements DebuggerObjectModel { + protected static final Cleaner CLEANER = Cleaner.create(); + + protected static final int WARN_OUTSTANDING_REQUESTS = 10000; + // TODO: More sophisticated cache management + // TODO: Perhaps no cache at all, and rely totally on async notifications + protected static final int MAX_OUTSTANDING_REQUESTS = Integer.MAX_VALUE; + protected static final int REQUEST_TIMEOUT_MILLIS = Integer.MAX_VALUE; + + protected static final ProtobufOneofByTypeHelper MSG_HELPER = + ProtobufOneofByTypeHelper.create(Gadp.RootMessage.getDefaultInstance(), + Gadp.RootMessage.newBuilder(), "msg"); + + protected enum ChannelState { + INACTIVE { + @Override + public boolean isOpen() { + return false; + } + + @Override + public boolean isTerminate() { + return false; + } + }, + NEGOTIATING { + @Override + public boolean isOpen() { + return true; + } + + @Override + public boolean isTerminate() { + return false; + } + }, + ACTIVE { + @Override + public boolean isOpen() { + return true; + } + + @Override + public boolean isTerminate() { + return false; + } + }, + CLOSED { + @Override + public boolean isOpen() { + return false; + } + + @Override + public boolean isTerminate() { + return true; + } + }; + + public abstract boolean isOpen(); + + public abstract boolean isTerminate(); + } + + /** + * For diagnostic purposes, it's useful to keep unanswered requests is an easily-reachable place + * + *

+ * When not in release mode, this places the request into the future meant to complete with its + * reply. In release mode, the recorded request is left null in order to conserve memory. Thus, + * one should never access {@link #request} programmatically, except for diagnostics. The field + * is meant to be examined only from a Java debugger. + */ + protected static class RequestMemo extends CompletableFuture { + private static final boolean RECORD_REQUESTS = !SystemUtilities.isInReleaseMode(); + + @SuppressWarnings("unused") + private final RootMessage request; + + public RequestMemo(Gadp.RootMessage request) { + this.request = RECORD_REQUESTS ? request : null; + } + } + + protected static Gadp.RootMessage checkError(Gadp.RootMessage msg) { + Gadp.ErrorReply error = MSG_HELPER.expect(msg, Gadp.ErrorReply.getDefaultInstance()); + if (error != null) { + switch (error.getCode()) { + case NO_INTERFACE: + throw new DebuggerModelTypeException(error.getMessage()); + case NO_OBJECT: + throw new DebuggerModelNoSuchPathException(error.getMessage()); + case BAD_ARGUMENT: + throw new DebuggerIllegalArgumentException(error.getMessage()); + case MEMORY_ACCESS: + throw new DebuggerMemoryAccessException(error.getMessage()); + case REGISTER_ACCESS: + throw new DebuggerRegisterAccessException(error.getMessage()); + case BAD_ADDRESS: + throw new AssertionError( + "Client implementation sent an invalid address: " + error.getMessage()); + case BAD_REQUEST: + throw new AssertionError( + "Client implementation sent an invalid request: " + error.getMessage()); + case UNRECOGNIZED: + throw new AssertionError( + "Server replied with an error code unknown to the client: " + + error.getCodeValue() + ": " + error.getMessage()); + case USER_ERROR: + throw new DebuggerUserException(error.getMessage()); + case MODEL_ACCESS: + throw new DebuggerModelAccessException(error.getMessage()); + case UNKNOWN: + throw new RuntimeException("Unknown: " + error.getMessage()); + case NO_VERSION: + default: + throw new GadpErrorException(error.getCode(), error.getMessage()); + } + } + return msg; + } + + protected static T nullForNotExist(Throwable e) { + e = AsyncUtils.unwrapThrowable(e); + if (e instanceof DebuggerModelNoSuchPathException) { + return null; + } + return ExceptionUtils.rethrow(e); + } + + protected static GadpClientTargetObject targetObjectOrNull(Object value) { + if (value instanceof GadpClientTargetObject) { + return (GadpClientTargetObject) value; + } + return null; + } + + protected static class GadpAddressFactory extends DefaultAddressFactory { + protected GadpAddressFactory() { + super(new AddressSpace[] { + new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 0) }); + } + + @Override + public synchronized AddressSpace getAddressSpace(String name) { + AddressSpace space = super.getAddressSpace(name); + if (space != null) { + return space; + } + + space = new GenericAddressSpace(name, 64, AddressSpace.TYPE_RAM, getNumAddressSpaces(), + true); + try { + addAddressSpace(space); + } + catch (DuplicateNameException e) { + throw new AssertionError(e); + } + return space; + } + } + + protected class MessagePairingCache extends AsyncPairingCache { + protected final AsyncDebouncer cacheMonitor = + new AsyncDebouncer<>(AsyncTimer.DEFAULT_TIMER, 500); + + public MessagePairingCache() { + super(4, REQUEST_TIMEOUT_MILLIS, MAX_OUTSTANDING_REQUESTS); + cacheMonitor.addListener(this::cacheSettled); + } + + @Override + protected void resultRemoved(RemovalNotification rn) { + Msg.error(this, "Received message with unexpected sequence number: " + rn); + } + + @Override + public CompletableFuture waitOn(Integer key, + Function> futureFactory) { + cacheMonitor.contact(null); + return super.waitOn(key, futureFactory); + } + + @Override + protected void promiseRemoved( + RemovalNotification> rn) { + if (rn.wasEvicted()) { + String message = "Command with sequence number " + rn.getKey() + + " evicted because " + rn.getCause(); + Msg.error(this, message); + AsyncUtils.FRAMEWORK_EXECUTOR.execute(() -> { + rn.getValue().completeExceptionally(new TimeoutException(message)); + }); + } + cacheMonitor.contact(null); + } + + private void cacheSettled(Void __) { + int size = getUnpairedPromises().size(); + if (size == 0) { + Msg.info(this, "Unanswered request cache size has settled to 0. All is well."); + } + else { + Msg.info(this, "Unanswered request cache size has settled to " + size + + ". Chances are something's gone wrong."); + } + } + } + + protected final String description; + protected final AsyncProtobufMessageChannel messageChannel; + + protected AsyncReference channelState = + new AsyncReference<>(ChannelState.INACTIVE); + protected GadpVersion activeVersion = null; + + protected final ListenerSet listenersClient = + new ListenerSet<>(DebuggerModelListener.class); + protected final TriConsumer listenerForChannelState = + this::channelStateChanged; + protected final MessagePairingCache messageMatcher = new MessagePairingCache(); + protected final AtomicInteger sequencer = new AtomicInteger(); + + protected final NavigableMap, GadpClientTargetObject> modelProxies = + new WeakValueTreeMap<>(PathComparator.KEYED); + + /** + * Forget all values and rely on getCachedValue instead. This lazy map will just de-dup pending + * requests. Once received, the behavior depends on what we know about the parent object. If + * nothing is known about the parent object, we assume we cannot cache. + */ + protected final AsyncLazyMap, Object> valueRequests = + new AsyncLazyMap<>(new HashMap<>(), p -> doRequestValue(p, false)); + protected final AsyncLazyMap, GadpClientTargetObject> attrsRequests = + new AsyncLazyMap<>(new HashMap<>(), p -> doRequestAttributes(p, false)); + protected final AsyncLazyMap, GadpClientTargetObject> elemsRequests = + new AsyncLazyMap<>(new HashMap<>(), p -> doRequestElements(p, false)); + + protected final GadpAddressFactory factory = new GadpAddressFactory(); + + { + channelState.addChangeListener(listenerForChannelState); + + valueRequests.forgetValues((p, v) -> true); + elemsRequests.forgetValues(this::forgetElementsRequests); + attrsRequests.forgetValues(this::forgetAttributesRequests); + } + + public GadpClient(String description, AsynchronousByteChannel channel) { + this.description = description; + this.messageChannel = createMessageChannel(channel); + } + + protected AsyncProtobufMessageChannel createMessageChannel( + AsynchronousByteChannel channel) { + return new AsyncProtobufMessageChannel<>(channel); + } + + @Override + public String toString() { + return ""; + } + + protected void channelStateChanged(ChannelState old, ChannelState set, + DebuggerModelClosedReason reason) { + if (old == ChannelState.NEGOTIATING && set == ChannelState.ACTIVE) { + listenersClient.fire.modelOpened(); + } + else if (old == ChannelState.ACTIVE && set == ChannelState.CLOSED) { + listenersClient.fire.modelClosed(reason); + List copy; + synchronized (modelProxies) { + copy = List.copyOf(modelProxies.values()); + } + for (GadpClientTargetObject proxy : copy) { + proxy.getDelegate().doInvalidate("GADP Client disconnected"); + } + } + } + + protected CompletableFuture sendCommand(Message.Builder req) { + Gadp.RootMessage.Builder root = Gadp.RootMessage.newBuilder(); + MSG_HELPER.set(root, req); + int seqno = sequencer.getAndIncrement(); + root.setSequence(seqno); + // Need to get into the matcher before we send + RootMessage built = root.build(); + CompletableFuture waitOn = + messageMatcher.waitOn(seqno, k -> new RequestMemo(built)); + if (messageMatcher.getUnpairedPromises().size() > WARN_OUTSTANDING_REQUESTS) { + Msg.warn(this, "Unanswered request count exceeds " + WARN_OUTSTANDING_REQUESTS + + "! Chances are something's gone wrong."); + } + + checkOpen(); + return messageChannel.write(built).thenCompose(__ -> { + checkOpen(); + return waitOn; + }); + } + + protected M require(M example, Gadp.RootMessage msg) { + M inner = MSG_HELPER.expect(msg, example); + if (inner == null) { + throw new GadpMessageException("Reply " + msg + " does not contain expected field " + + MSG_HELPER.getFieldForTypeOf(example)); + } + return inner; + } + + protected CompletableFuture sendChecked(Message.Builder req, + M exampleRep) { + return sendCommand(req).thenApply(msg -> require(exampleRep, checkError(msg))); + } + + protected void receiveLoop() { + AsyncUtils.loop(TypeSpec.VOID, loop -> { + if (channelState.get().isTerminate()) { + loop.exit(); + return; + } + messageChannel.read(Gadp.RootMessage::parseFrom).handle(loop::consume); + }, TypeSpec.cls(Gadp.RootMessage.class), (msg, loop) -> { + loop.repeat(); // All async loop to continue while we process + + try { + Gadp.EventNotification notify = + MSG_HELPER.expect(msg, Gadp.EventNotification.getDefaultInstance()); + if (notify != null) { + processNotification(notify); + } + else { + messageMatcher.fulfill(msg.getSequence(), msg); + } + } + catch (Throwable e) { + Msg.error(this, "Error processing message: " + msg, e); + } + }).exceptionally(exc -> { + exc = AsyncUtils.unwrapThrowable(exc); + if (exc instanceof NotYetConnectedException) { + throw new AssertionError("Connect first, please", exc); + } + else if (exc instanceof EOFException) { + Msg.error(this, "Channel has no data remaining"); + } + else if (exc instanceof ClosedChannelException) { + Msg.error(this, "Channel is already closed"); + } + else if (exc instanceof CancelledKeyException) { + Msg.error(this, "Channel key is cancelled. Probably closed"); + } + else { + Msg.error(this, "Receive failed for an unknown reason", exc); + } + channelState.set(ChannelState.CLOSED, DebuggerModelClosedReason.abnormal(exc)); + return null; + }); + } + + protected void processNotification(Gadp.EventNotification notify) { + ProtocolStringList path = notify.getPath().getEList(); + GadpClientTargetObject obj = getCachedProxy(path); + if (obj == null) { + if (!hasPendingRequest(path)) { + /** + * For pending, I guess we just miss the event. NB: If it was a model event, then + * the pending subscribe reply ought to already reflect the update we're ignoring + * here. + */ + Msg.error(this, "Server sent notification for non-cached object: " + notify); + } + return; + } + obj.getDelegate().handleEvent(notify); + } + + @Override + public String getBrief() { + return description + " via GADP (" + channelState.get().name().toLowerCase() + ")"; + } + + @Override + public void addModelListener(DebuggerModelListener listener) { + listenersClient.add(listener); + } + + @Override + public void removeModelListener(DebuggerModelListener listener) { + listenersClient.remove(listener); + } + + public CompletableFuture connect() { + Gadp.ConnectRequest.Builder req = GadpVersion.makeRequest(); + if (channelState.get() != ChannelState.INACTIVE) { + throw new GadpIllegalStateException( + "Client has already connected or started connecting"); + } + Msg.trace(this, "Connecting"); + + channelState.set(ChannelState.NEGOTIATING, null); + + AsyncFence fence = new AsyncFence(); + fence.include(sendCommand(req).thenAccept(msg -> { + Gadp.ConnectReply rep = + require(Gadp.ConnectReply.getDefaultInstance(), checkError(msg)); + GadpVersion version = GadpVersion.getByName(rep.getVersion()); + synchronized (this) { + activeVersion = version; + } + channelState.set(ChannelState.ACTIVE, null); + // launches in background in parallel, with its own error reporting + // Thus, do not compose + receiveLoop(); + })); + /** + * receiveLoop is not yet running, so receive exactly the version reply in parallel. + * Otherwise, the above sendCommand can never complete. + */ + fence.include(messageChannel.read(Gadp.RootMessage::parseFrom).thenAccept(msg -> { + messageMatcher.fulfill(msg.getSequence(), msg); + })); + return fence.ready(); + } + + @Override + public CompletableFuture close() { + try { + messageChannel.close(); + channelState.set(ChannelState.CLOSED, DebuggerModelClosedReason.normal()); + return AsyncUtils.NIL; + } + catch (IOException e) { + return CompletableFuture.failedFuture(e); + } + } + + protected void checkOpen() { + if (!channelState.get().isOpen()) { + throw new GadpIllegalStateException("Channel is not open"); + } + } + + @Override + public boolean isAlive() { + return channelState.get() == ChannelState.ACTIVE; + } + + @Override + public CompletableFuture ping(String content) { + Gadp.PingRequest.Builder req = Gadp.PingRequest.newBuilder().setContent(content); + return sendChecked(req, Gadp.PingReply.getDefaultInstance()).thenAccept(rep -> { + if (!content.equals(rep.getContent())) { + throw new GadpMessageException( + "Data in ping was corrupt: req=" + req + ",rep=" + rep); + } + }); + } + + protected GadpClientTargetObject getCachedProxy(List path) { + synchronized (modelProxies) { + return modelProxies.get(path); + } + } + + protected GadpClientTargetObject removeCachedProxy(List path) { + synchronized (modelProxies) { + return modelProxies.remove(path); + } + } + + /** + * Check for a cached value + * + * This first checks if the given path is a cached object and returns it if so. Otherwise, it + * checks if the parent is cached and, if so, examines its cached children. + * + * Note, if {@code null} is returned, the cache has no knowledge of the given path; the server + * must be queried. If the returned optional has no value, the cache knows the path does not + * exist. + * + * @param path the path + * @return an optional value, or {@code null} if not cached + */ + protected Optional getCachedValue(List path) { + GadpClientTargetObject proxy = getCachedProxy(path); + if (proxy != null) { + return Optional.of(proxy); + } + List parentPath = PathUtils.parent(path); + if (parentPath == null) { + return null; + } + GadpClientTargetObject parent = getCachedProxy(parentPath); + if (parent == null) { + return null; + } + Optional val = parent.getDelegate().cachedChild(PathUtils.getKey(path)); + if (val == null) { + return null; + } + if (val.isEmpty()) { + if (PathUtils.isInvocation(PathUtils.getKey(path))) { + return null; + } + return val; + } + Object v = val.get(); + /** + * NOTE: val should not be a TargetObject, otherwise it should have hit via + * getCachedProxy(path). If this is a TargetObject, it's because the proxy was created + * between then and the call to cachedChild -- possible due to a race condition. In that + * case, it seems harmless to just return it anyway. + */ + if (v instanceof TargetObject) { + return val; + } + if (!(v instanceof TargetObjectRef)) { + return val; + } + TargetObjectRef r = (TargetObjectRef) v; + if (path.equals(r.getPath())) { + // An TargetObject is expected, but we only have the placeholder cached + return null; + } + // else a link, which we are not required to fetch + return val; + } + + protected void cacheInParent(List path, GadpClientTargetObject proxy) { + List parentPath = PathUtils.parent(path); + if (parentPath == null) { + return; + } + GadpClientTargetObject parent = modelProxies.get(parentPath); + if (parent == null) { + return; + } + parent.getDelegate().putCachedProxy(PathUtils.getKey(path), proxy); + } + + @Internal + public GadpClientTargetObject getProxyForInfo(ModelObjectInfo info) { + synchronized (modelProxies) { + return modelProxies.computeIfAbsent(info.getPath().getEList(), path -> { + GadpClientTargetObject proxy = DelegateGadpClientTargetObject.makeModelProxy(this, + path, info.getTypeHint(), info.getInterfaceList()); + cacheInParent(path, proxy); + return proxy; + }); + } + } + + @Internal + public TargetObjectRef getProxyOrStub(List path) { + GadpClientTargetObject cached = getCachedProxy(path); + if (cached != null) { + return cached; + } + return new GadpClientTargetObjectStub(this, path); + } + + protected CompletableFuture unsubscribe(List path) { + AsyncFence fence = new AsyncFence(); + cleanRequests(path); + fence.include(sendChecked( + Gadp.SubscribeRequest.newBuilder() + .setPath(GadpValueUtils.makePath(path)) + .setSubscribe(false), + Gadp.SubscribeReply.getDefaultInstance())); + return fence.ready(); + } + + protected void invalidateSubtree(List path, String reason) { + List> pathsToInvalidate = new ArrayList<>(); + List proxiesToInvalidate; + synchronized (modelProxies) { + // keySet is the only one which isn't a copy. + // TODO: Would rather iterate entries when AbstractWeakValueMap is fixed + for (List succPath : modelProxies.tailMap(path, true).keySet()) { + if (!PathUtils.isAncestor(path, succPath)) { + break; + } + pathsToInvalidate.add(succPath); + } + proxiesToInvalidate = + pathsToInvalidate.stream().map(modelProxies::remove).collect(Collectors.toList()); + } + for (GadpClientTargetObject proxy : proxiesToInvalidate) { + proxy.getDelegate().doInvalidate(reason); + } + } + + public boolean hasPendingRequest(List path) { + return valueRequests.containsKey(path) || attrsRequests.containsKey(path) || + elemsRequests.containsKey(path); + } + + public boolean hasPendingAttributes(List path) { + return attrsRequests.containsKey(path); + } + + public boolean hasPendingElements(List path) { + return elemsRequests.containsKey(path); + } + + protected void cleanRequests(List path) { + valueRequests.forget(path); + attrsRequests.forget(path); + elemsRequests.forget(path); + } + + @Override + public CompletableFuture fetchModelRoot() { + return fetchModelObject(List.of()); + } + + protected CompletableFuture doRequestValueWithObjectInfo( + List path, boolean refresh, + boolean fetchElements, boolean refreshElements, + boolean fetchAttributes, boolean refreshAttributes) { + CompletableFuture reply = sendChecked(Gadp.SubscribeRequest.newBuilder() + .setPath(GadpValueUtils.makePath(path)) + .setSubscribe(true) + .setRefresh(refresh) + .setFetchElements(fetchElements) + .setRefreshElements(refreshElements) + .setFetchAttributes(fetchAttributes) + .setRefreshAttributes(refreshAttributes), + Gadp.SubscribeReply.getDefaultInstance()); + return reply.thenApplyAsync(rep -> { // Async to avoid processing info with a lock + Gadp.Value value = rep.getValue(); + if (value.getSpecCase() == Gadp.Value.SpecCase.OBJECT_STUB) { + Msg.error(this, "Server responded to object request with a stub!"); + return null; + } + if (value.getSpecCase() != Gadp.Value.SpecCase.OBJECT_INFO) { + return GadpValueUtils.getValue(this, path, value); + } + Gadp.ModelObjectInfo info = value.getObjectInfo(); + GadpClientTargetObject proxy = getProxyForInfo(info); + proxy.getDelegate().updateWithInfo(info); + return proxy; + }, AsyncUtils.FRAMEWORK_EXECUTOR); + } + + protected CompletableFuture doRequestElements(List path, + boolean refresh) { + return doRequestValueWithObjectInfo(path, false, true, refresh, false, false) + .thenApply(GadpClient::targetObjectOrNull); + } + + protected CompletableFuture doRequestAttributes(List path, + boolean refresh) { + return doRequestValueWithObjectInfo(path, false, false, false, true, refresh) + .thenApply(GadpClient::targetObjectOrNull); + } + + protected boolean forgetElementsRequests(List path, GadpClientTargetObject proxy) { + return proxy == null || !proxy.isValid() || + proxy.getUpdateMode() == TargetUpdateMode.SOLICITED; + } + + protected CompletableFuture checkProcessedElemsReply(List path, + boolean refresh) { + if (refresh) { // NB: the map pre-tests the forget condition, too + elemsRequests.forget(path); + return elemsRequests.get(path, p -> doRequestElements(p, refresh)); + } + return elemsRequests.get(path); + } + + @Override + public CompletableFuture> fetchObjectElements( + List path, boolean refresh) { + CompletableFuture processedReply = + checkProcessedElemsReply(path, refresh); + return processedReply.thenApply(proxy -> { + if (proxy == null) { // The path doesn't exist, so return null, per the docs + return null; + } + return proxy.getCachedElements(); + }).exceptionally(GadpClient::nullForNotExist); + } + + protected boolean forgetAttributesRequests(List path, GadpClientTargetObject proxy) { + return proxy == null || !proxy.isValid(); + } + + protected CompletableFuture checkProcessedAttrsReply(List path, + boolean refresh) { + if (refresh) { + attrsRequests.forget(path); + return attrsRequests.get(path, p -> doRequestAttributes(p, refresh)); + } + return attrsRequests.get(path); + } + + @Override + public CompletableFuture> fetchObjectAttributes(List path, + boolean refresh) { + CompletableFuture processedReply = + checkProcessedAttrsReply(path, refresh); + return processedReply.thenApply(proxy -> { + if (proxy == null) { // The path doesn't exist, so return null, per the docs + return null; + } + return proxy.getCachedAttributes(); + }).exceptionally(GadpClient::nullForNotExist); + } + + @Override + public CompletableFuture fetchModelValue(List path, boolean refresh) { + /** + * NB. Not sure there's value in checking for element/attribute requests on the parent. May + * cull some requests, but the logic could get complicated. E.g., if we wait for it to + * complete, and it comes back a TargetObjectRef, well, we have to fetch anyway. For + * attributes, we'd also have to consider the case where it's absent, because it's a method + * invocation. + */ + if (!refresh) { + Optional cached = getCachedValue(path); + if (cached != null) { + Object val = cached.orElse(null); + if (!(val instanceof TargetObjectRef)) { + return CompletableFuture.completedFuture(val); + } + TargetObjectRef ref = (TargetObjectRef) val; + TargetObject obj = GadpValueUtils.getTargetObjectNonLink(path, ref); + if (obj != null) { + return CompletableFuture.completedFuture(val); + } + } + } + return valueRequests.get(path, p -> doRequestValue(p, refresh)) + .exceptionally(GadpClient::nullForNotExist); + } + + @Override + public CompletableFuture fetchModelValue(List path) { + return fetchModelValue(path, false); + } + + protected CompletableFuture doRequestValue(List path, boolean refresh) { + return doRequestValueWithObjectInfo(path, refresh, false, false, false, false); + } + + @Override + public AddressFactory getAddressFactory() { + return factory; + } + + @Override + public TargetObjectRef createRef(List path) { + return getProxyOrStub(path); + } + + @Override + public void invalidateAllLocalCaches() { + List copy; + synchronized (modelProxies) { + copy = List.copyOf(modelProxies.values()); + } + for (GadpClientTargetObject proxy : copy) { + proxy.getDelegate().doClearCaches(); + } + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAccessConditioned.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAccessConditioned.java new file mode 100644 index 0000000000..1f260c3b31 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAccessConditioned.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.gadp.client.annot.GadpAttributeChangeCallback; +import ghidra.dbg.target.TargetAccessConditioned; + +public interface GadpClientTargetAccessConditioned + extends GadpClientTargetObject, TargetAccessConditioned { + + @GadpAttributeChangeCallback(ACCESSIBLE_ATTRIBUTE_NAME) + default void handleAccessibleChanged(Object accessible) { + getDelegate().listeners.fire(TargetAccessibilityListener.class) + .accessibilityChanged(this, fromObj(accessible)); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAggregate.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAggregate.java new file mode 100644 index 0000000000..6ee0f17e9a --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAggregate.java @@ -0,0 +1,22 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetAggregate; + +public interface GadpClientTargetAggregate extends GadpClientTargetObject, TargetAggregate { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAttachable.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAttachable.java new file mode 100644 index 0000000000..3177e477d0 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAttachable.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetAttachable; + +public interface GadpClientTargetAttachable + extends GadpClientTargetObject, TargetAttachable { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAttacher.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAttacher.java new file mode 100644 index 0000000000..d63fc5f012 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetAttacher.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetAttachable; +import ghidra.dbg.target.TargetAttacher; + +public interface GadpClientTargetAttacher + extends GadpClientTargetObject, TargetAttacher { + @Override + default CompletableFuture attach( + TypedTargetObjectRef> ref) { + getDelegate().assertValid(); + getModel().assertMine(TargetObjectRef.class, ref); + return getModel().sendChecked(Gadp.AttachRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setTarget(GadpValueUtils.makePath(ref.getPath())), + Gadp.AttachReply.getDefaultInstance()).thenApply(rep -> null); + } + + @Override + default CompletableFuture attach(long id) { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.AttachRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setPid(id), + Gadp.AttachReply.getDefaultInstance()).thenApply(rep -> null); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointContainer.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointContainer.java new file mode 100644 index 0000000000..7e2e39fdd4 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointContainer.java @@ -0,0 +1,90 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.gadp.client.annot.GadpEventHandler; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.protocol.Gadp.Path; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointAction; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.program.model.address.AddressRange; +import ghidra.util.datastruct.ListenerSet; + +public interface GadpClientTargetBreakpointContainer extends GadpClientTargetObject, + TargetBreakpointContainer { + + @Override + default CompletableFuture placeBreakpoint(AddressRange range, + Set kinds) { + getDelegate().assertValid(); + return getModel() + .sendChecked( + Gadp.BreakCreateRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setAddress(GadpValueUtils.makeRange(range)) + .setKinds(GadpValueUtils.makeBreakKindSet(kinds)), + Gadp.BreakCreateReply.getDefaultInstance()) + .thenApply(rep -> null); + } + + @Override + default CompletableFuture placeBreakpoint(String expression, + Set kinds) { + getDelegate().assertValid(); + return getModel() + .sendChecked( + Gadp.BreakCreateRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setExpression(expression) + .setKinds(GadpValueUtils.makeBreakKindSet(kinds)), + Gadp.BreakCreateReply.getDefaultInstance()) + .thenApply(rep -> null); + } + + @GadpEventHandler(Gadp.EventNotification.EvtCase.BREAK_HIT_EVENT) + default void handleBreakHitEvent(Gadp.EventNotification notification) { + Gadp.BreakHitEvent evt = notification.getBreakHitEvent(); + TargetObjectRef trapped = getModel().getProxyOrStub(evt.getTrapped().getEList()); + Path framePath = evt.getFrame(); + TypedTargetObjectRef> frame = + framePath == null || framePath.getECount() == 0 ? null + : getModel().getProxyOrStub(framePath.getEList()).as(TargetStackFrame.tclass); + Path specPath = evt.getSpec(); + TypedTargetObjectRef> spec = specPath == null ? null + : getModel().getProxyOrStub(specPath.getEList()).as(TargetBreakpointSpec.tclass); + Path bptPath = evt.getEffective(); + TypedTargetObjectRef> breakpoint = bptPath == null + ? null + : getModel().getProxyOrStub(bptPath.getEList()).as(TargetBreakpointLocation.tclass); + getDelegate().listeners.fire(TargetBreakpointListener.class) + .breakpointHit(this, trapped, frame, spec, breakpoint); + if (spec instanceof GadpClientTargetBreakpointSpec) { + // If I don't have a cached proxy, then I don't have any listeners + GadpClientTargetBreakpointSpec specObj = (GadpClientTargetBreakpointSpec) spec; + ListenerSet actions = specObj.getDelegate().getActions(false); + if (actions != null) { + actions.fire.breakpointHit(specObj, trapped, frame, breakpoint); + } + } + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointLocation.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointLocation.java new file mode 100644 index 0000000000..7e32371bed --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointLocation.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetBreakpointLocation; + +public interface GadpClientTargetBreakpointLocation + extends GadpClientTargetObject, + TargetBreakpointLocation { + +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointSpec.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointSpec.java new file mode 100644 index 0000000000..636c7d4b65 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetBreakpointSpec.java @@ -0,0 +1,71 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.gadp.client.annot.GadpAttributeChangeCallback; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetBreakpointSpec; +import ghidra.dbg.util.ValueUtils; +import ghidra.util.datastruct.ListenerSet; + +public interface GadpClientTargetBreakpointSpec + extends GadpClientTargetObject, TargetBreakpointSpec { + + @Override + default CompletableFuture toggle(boolean enabled) { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.BreakToggleRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setEnabled(enabled), + Gadp.BreakToggleReply.getDefaultInstance()).thenApply(rep -> null); + } + + @Override + default CompletableFuture disable() { + return toggle(false); + } + + @Override + default CompletableFuture enable() { + return toggle(true); + } + + @Override + default void addAction(TargetBreakpointAction action) { + getDelegate().getActions(true).add(action); + } + + @Override + default void removeAction(TargetBreakpointAction action) { + ListenerSet actions = getDelegate().getActions(false); + if (actions != null) { + actions.remove(action); + } + } + + default boolean enabledFromObj(Object obj) { + return ValueUtils.expectBoolean(obj, this, ENABLED_ATTRIBUTE_NAME, false); + } + + @GadpAttributeChangeCallback(ENABLED_ATTRIBUTE_NAME) + default void handleEnabledChanged(Object enabled) { + getDelegate().listeners.fire(TargetBreakpointSpecListener.class) + .breakpointToggled(this, enabledFromObj(enabled)); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetConsole.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetConsole.java new file mode 100644 index 0000000000..7d1d9d3f47 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetConsole.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import com.google.protobuf.ByteString; + +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetConsole; + +public interface GadpClientTargetConsole + extends GadpClientTargetObject, TargetConsole { + + @Override + default CompletableFuture write(byte[] data) { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.ConsoleWriteRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setData(ByteString.copyFrom(data)), + Gadp.ConsoleWriteReply.getDefaultInstance()) + .thenApply(rep -> null); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDataTypeMember.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDataTypeMember.java new file mode 100644 index 0000000000..396977929b --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDataTypeMember.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetDataTypeMember; + +public interface GadpClientTargetDataTypeMember + extends GadpClientTargetObject, TargetDataTypeMember { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDataTypeNamespace.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDataTypeNamespace.java new file mode 100644 index 0000000000..1dc83e41e4 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDataTypeNamespace.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetDataTypeNamespace; + +public interface GadpClientTargetDataTypeNamespace + extends GadpClientTargetObject, TargetDataTypeNamespace { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDeletable.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDeletable.java new file mode 100644 index 0000000000..4cb007ee20 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDeletable.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetDeletable; + +public interface GadpClientTargetDeletable + extends GadpClientTargetObject, TargetDeletable { + @Override + default CompletableFuture delete() { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.DeleteRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())), + Gadp.DeleteReply.getDefaultInstance()).thenApply(rep -> null); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDetachable.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDetachable.java new file mode 100644 index 0000000000..251bbd1c51 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetDetachable.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetDetachable; + +public interface GadpClientTargetDetachable + extends GadpClientTargetObject, TargetDetachable { + @Override + default CompletableFuture detach() { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.DetachRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())), + Gadp.DetachReply.getDefaultInstance()).thenApply(rep -> null); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetEnvironment.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetEnvironment.java new file mode 100644 index 0000000000..49f289ae91 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetEnvironment.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetEnvironment; + +public interface GadpClientTargetEnvironment + extends GadpClientTargetObject, TargetEnvironment { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetEventScope.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetEventScope.java new file mode 100644 index 0000000000..e5304c00cd --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetEventScope.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.List; + +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.gadp.client.annot.GadpEventHandler; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetEventScope; +import ghidra.dbg.target.TargetThread; + +public interface GadpClientTargetEventScope + extends GadpClientTargetObject, TargetEventScope { + @GadpEventHandler(Gadp.EventNotification.EvtCase.TARGET_EVENT) + default void handleDebuggerEvent(Gadp.EventNotification notification) { + Gadp.TargetEvent evt = notification.getTargetEvent(); + Gadp.Path threadPath = evt.getEventThread(); + TypedTargetObjectRef> thread = + threadPath == null || threadPath.getECount() == 0 ? null + : getModel().getProxyOrStub(threadPath.getEList()).as(TargetThread.tclass); + TargetEventType type = GadpValueUtils.getTargetEventType(evt.getType()); + String description = evt.getDescription(); + List parameters = + GadpValueUtils.getValues(getModel(), evt.getParametersList()); + getDelegate().listeners.fire(TargetEventScopeListener.class) + .event(this, thread, type, description, parameters); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetExecutionStateful.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetExecutionStateful.java new file mode 100644 index 0000000000..4d394b7b9f --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetExecutionStateful.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.gadp.client.annot.GadpAttributeChangeCallback; +import ghidra.dbg.target.TargetExecutionStateful; +import ghidra.dbg.util.ValueUtils; + +public interface GadpClientTargetExecutionStateful + extends GadpClientTargetObject, TargetExecutionStateful { + + default TargetExecutionState stateFromObj(Object obj) { + return ValueUtils.expectType(obj, TargetExecutionState.class, this, + STATE_ATTRIBUTE_NAME, TargetExecutionState.INACTIVE); + } + + @GadpAttributeChangeCallback(STATE_ATTRIBUTE_NAME) + default void handleStateChanged(Object state) { + getDelegate().listeners.fire(TargetExecutionStateListener.class) + .executionStateChanged(this, stateFromObj(state)); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetFocusScope.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetFocusScope.java new file mode 100644 index 0000000000..e0f64b9a8a --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetFocusScope.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.gadp.client.annot.GadpAttributeChangeCallback; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetFocusScope; +import ghidra.dbg.util.PathUtils; +import ghidra.dbg.util.ValueUtils; + +public interface GadpClientTargetFocusScope + extends GadpClientTargetObject, TargetFocusScope { + + @Override + default CompletableFuture requestFocus(TargetObjectRef obj) { + getDelegate().assertValid(); + getModel().assertMine(TargetObjectRef.class, obj); + // The server should detect this error, but we can detect it here without sending a request + if (!PathUtils.isAncestor(getPath(), obj.getPath())) { + throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope"); + } + return getModel().sendChecked(Gadp.FocusRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setFocus(GadpValueUtils.makePath(obj.getPath())), + Gadp.FocusReply.getDefaultInstance()) + .thenApply(__ -> null); + } + + default TargetObjectRef refFromObj(Object obj) { + return ValueUtils.expectType(obj, TargetObjectRef.class, this, FOCUS_ATTRIBUTE_NAME, this); + } + + @GadpAttributeChangeCallback(FOCUS_ATTRIBUTE_NAME) + default void handleFocusChanged(Object focus) { + getDelegate().listeners.fire(TargetFocusScopeListener.class) + .focusChanged(this, refFromObj(focus)); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetInterpreter.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetInterpreter.java new file mode 100644 index 0000000000..ab21d0801b --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetInterpreter.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.gadp.client.annot.GadpAttributeChangeCallback; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetInterpreter; +import ghidra.dbg.util.ValueUtils; + +public interface GadpClientTargetInterpreter + extends GadpClientTargetObject, TargetInterpreter { + + @Override + default CompletableFuture execute(String cmd) { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.ExecuteRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setCommand(cmd), + Gadp.ExecuteReply.getDefaultInstance()) + .thenApply(rep -> null); + } + + @Override + default CompletableFuture executeCapture(String cmd) { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.ExecuteRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setCommand(cmd) + .setCapture(true), + Gadp.ExecuteReply.getDefaultInstance()) + .thenApply(rep -> rep.getCaptured()); + } + + default String promptFromObj(Object obj) { + return ValueUtils.expectType(obj, String.class, this, PROMPT_ATTRIBUTE_NAME, ">"); + } + + @GadpAttributeChangeCallback(PROMPT_ATTRIBUTE_NAME) + default void handlePromptChanged(Object prompt) { + getDelegate().listeners.fire(TargetInterpreterListener.class) + .promptChanged(this, promptFromObj(prompt)); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetInterruptible.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetInterruptible.java new file mode 100644 index 0000000000..0c5803cd0d --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetInterruptible.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetInterruptible; + +public interface GadpClientTargetInterruptible + extends GadpClientTargetObject, TargetInterruptible { + @Override + default CompletableFuture interrupt() { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.InterruptRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())), + Gadp.InterruptReply.getDefaultInstance()).thenApply(rep -> null); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetKillable.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetKillable.java new file mode 100644 index 0000000000..876d0f2310 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetKillable.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetKillable; + +public interface GadpClientTargetKillable + extends GadpClientTargetObject, TargetKillable { + @Override + default CompletableFuture kill() { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.KillRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())), + Gadp.KillReply.getDefaultInstance()).thenApply(rep -> null); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetLauncher.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetLauncher.java new file mode 100644 index 0000000000..69d85794b0 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetLauncher.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetLauncher; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; + +public interface GadpClientTargetLauncher + extends GadpClientTargetObject, TargetLauncher { + @Override + default CompletableFuture launch(Map arguments) { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.LaunchRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .addAllArgument(GadpValueUtils.makeArguments(arguments)), + Gadp.LaunchReply.getDefaultInstance()).thenApply(__ -> null); + } + + @Override + default TargetParameterMap getParameters() { + return TargetMethod.getParameters(this); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMemory.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMemory.java new file mode 100644 index 0000000000..d690462b7e --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMemory.java @@ -0,0 +1,106 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import com.google.protobuf.ByteString; + +import ghidra.dbg.error.DebuggerMemoryAccessException; +import ghidra.dbg.gadp.client.annot.GadpEventHandler; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.memory.MemoryReader; +import ghidra.dbg.memory.MemoryWriter; +import ghidra.dbg.target.TargetMemory; +import ghidra.lifecycle.Internal; +import ghidra.program.model.address.*; + +public interface GadpClientTargetMemory + extends GadpClientTargetObject, TargetMemory { + + @Internal + default MemoryReader getRawReader(AddressSpace space) { + return (addr, length) -> rawReadMemory(space, addr, length); + } + + @Internal + default CompletableFuture rawReadMemory(AddressSpace space, long addr, int length) { + getDelegate().assertValid(); + Address min = space.getAddress(addr); + AddressRange range; + try { + range = new AddressRangeImpl(min, length); + } + catch (AddressOverflowException e) { + throw new IllegalArgumentException("addr=" + addr + ",len=" + length); + } + return getModel().sendChecked(Gadp.MemoryReadRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setRange(GadpValueUtils.makeRange(range)), + Gadp.MemoryReadReply.getDefaultInstance()).thenApply(rep -> { + return rep.getContent().toByteArray(); + }); + } + + @Override + default CompletableFuture readMemory(Address address, int length) { + return getDelegate().getMemoryCache(address.getAddressSpace()) + .readMemory(address.getOffset(), length); + } + + @Internal + default MemoryWriter getRawWriter(AddressSpace space) { + return (addr, data) -> rawWriteMemory(space, addr, data); + } + + @Internal + default CompletableFuture rawWriteMemory(AddressSpace space, long addr, byte[] data) { + getDelegate().assertValid(); + Address min = space.getAddress(addr); + return getModel().sendChecked(Gadp.MemoryWriteRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setStart(GadpValueUtils.makeAddress(min)) + .setContent(ByteString.copyFrom(data)), + Gadp.MemoryWriteReply.getDefaultInstance()).thenApply(rep -> null); + } + + @Override + default CompletableFuture writeMemory(Address address, byte[] data) { + return getDelegate().getMemoryCache(address.getAddressSpace()) + .writeMemory(address.getOffset(), data); + } + + @GadpEventHandler(Gadp.EventNotification.EvtCase.MEMORY_UPDATE_EVENT) + default void handleMemoryUpdateEvent(Gadp.EventNotification notification) { + Gadp.MemoryUpdateEvent evt = notification.getMemoryUpdateEvent(); + Address address = GadpValueUtils.getAddress(getModel(), evt.getAddress()); + byte[] data = evt.getContent().toByteArray(); + DelegateGadpClientTargetObject delegate = getDelegate(); + delegate.getMemoryCache(address.getAddressSpace()).updateMemory(address.getOffset(), data); + delegate.listeners.fire(TargetMemoryListener.class).memoryUpdated(this, address, data); + } + + @GadpEventHandler(Gadp.EventNotification.EvtCase.MEMORY_ERROR_EVENT) + default void handleMemoryErrorEvent(Gadp.EventNotification notification) { + Gadp.MemoryErrorEvent evt = notification.getMemoryErrorEvent(); + AddressRange range = GadpValueUtils.getAddressRange(getModel(), evt.getRange()); + String message = evt.getMessage(); + // Errors are not cached, but recorded in trace + getDelegate().listeners.fire(TargetMemoryListener.class) + .memoryReadError(this, range, new DebuggerMemoryAccessException(message)); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMemoryRegion.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMemoryRegion.java new file mode 100644 index 0000000000..8aa3873a03 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMemoryRegion.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetMemoryRegion; + +public interface GadpClientTargetMemoryRegion + extends GadpClientTargetObject, TargetMemoryRegion { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMethod.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMethod.java new file mode 100644 index 0000000000..2b91bffbdc --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetMethod.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetMethod; + +public interface GadpClientTargetMethod + extends GadpClientTargetObject, TargetMethod { + @Override + default CompletableFuture invoke(Map arguments) { + getDelegate().assertValid(); + return getModel().sendChecked( + Gadp.InvokeRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .addAllArgument(GadpValueUtils.makeArguments(arguments)), + Gadp.InvokeReply.getDefaultInstance()) + .thenApply(rep -> GadpValueUtils.getValue(getModel(), null, rep.getResult())); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetModule.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetModule.java new file mode 100644 index 0000000000..2bb6546dad --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetModule.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetModule; + +public interface GadpClientTargetModule + extends GadpClientTargetObject, TargetModule { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetModuleContainer.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetModuleContainer.java new file mode 100644 index 0000000000..7a241ba5c7 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetModuleContainer.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetModuleContainer; + +public interface GadpClientTargetModuleContainer + extends GadpClientTargetObject, TargetModuleContainer { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetNamedDataType.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetNamedDataType.java new file mode 100644 index 0000000000..45fa43b09a --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetNamedDataType.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetNamedDataType; + +public interface GadpClientTargetNamedDataType + extends GadpClientTargetObject, TargetNamedDataType { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetObject.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetObject.java new file mode 100644 index 0000000000..e6be0805d5 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetObject.java @@ -0,0 +1,89 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.Collection; +import java.util.List; + +import ghidra.dbg.gadp.client.annot.GadpAttributeChangeCallback; +import ghidra.dbg.gadp.client.annot.GadpEventHandler; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.target.TargetConsole.Channel; +import ghidra.dbg.target.TargetConsole.TargetConsoleListener; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.ValueUtils; +import ghidra.util.Msg; + +public interface GadpClientTargetObject extends TargetObject { + @Override + GadpClient getModel(); + + @Override + List getProtocolID(); + + @Override + String getTypeHint(); + + @Override + Collection getInterfaceNames(); + + @Override + Collection> getInterfaces(); + + DelegateGadpClientTargetObject getDelegate(); + + @GadpEventHandler(Gadp.EventNotification.EvtCase.MODEL_OBJECT_EVENT) + default void handleModelObjectEvent(Gadp.EventNotification notification) { + Gadp.ModelObjectEvent evt = notification.getModelObjectEvent(); + getDelegate().updateWithDelta(evt.getDelta()); + } + + @GadpEventHandler(Gadp.EventNotification.EvtCase.OBJECT_INVALIDATE_EVENT) + default void handleObjectInvalidateEvent(Gadp.EventNotification notification) { + Gadp.ObjectInvalidateEvent evt = notification.getObjectInvalidateEvent(); + getDelegate().doInvalidateSubtree(evt.getReason()); + } + + @GadpEventHandler(Gadp.EventNotification.EvtCase.CACHE_INVALIDATE_EVENT) + default void handleCacheInvalidateEvent(Gadp.EventNotification notification) { + getDelegate().doClearCaches(); + } + + default String displayFromObj(Object obj) { + return ValueUtils.expectType(obj, String.class, this, DISPLAY_ATTRIBUTE_NAME, getName()); + } + + @GadpAttributeChangeCallback(DISPLAY_ATTRIBUTE_NAME) + default void handleDisplayChanged(Object display) { + getDelegate().listeners.fire.displayChanged(this, displayFromObj(display)); + } + + // TODO: It's odd to put this here.... I think it indicates a problem in the API + @GadpEventHandler(Gadp.EventNotification.EvtCase.CONSOLE_OUTPUT_EVENT) + default void handleConsoleOutputEvent(Gadp.EventNotification notification) { + Gadp.ConsoleOutputEvent evt = notification.getConsoleOutputEvent(); + int channelIndex = evt.getChannel(); + Channel[] allChannels = Channel.values(); + if (0 <= channelIndex && channelIndex < allChannels.length) { + getDelegate().listeners.fire(TargetConsoleListener.class) + .consoleOutput(this, allChannels[channelIndex], evt.getData().toByteArray()); + } + else { + Msg.error(this, "Received output for unknown channel " + channelIndex + ": " + + evt.getData().toString()); + } + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetObjectStub.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetObjectStub.java new file mode 100644 index 0000000000..50805de7d1 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetObjectStub.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.List; + +import ghidra.dbg.attributes.TargetObjectRef; + +public class GadpClientTargetObjectStub implements TargetObjectRef { + protected final GadpClient client; + protected final List path; + protected final int hash; + + public GadpClientTargetObjectStub(GadpClient client, List path) { + this.client = client; + this.path = path; + this.hash = computeHashCode(); + } + + @Override + public boolean equals(Object obj) { + return doEquals(obj); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public GadpClient getModel() { + return client; + } + + @Override + public List getPath() { + return path; + } + + @Override + public String toString() { + return ""; + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetProcess.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetProcess.java new file mode 100644 index 0000000000..5933d04715 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetProcess.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetProcess; + +public interface GadpClientTargetProcess + extends GadpClientTargetObject, TargetProcess { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegister.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegister.java new file mode 100644 index 0000000000..73dc4ab570 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegister.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetRegister; + +public interface GadpClientTargetRegister + extends GadpClientTargetObject, TargetRegister { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegisterBank.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegisterBank.java new file mode 100644 index 0000000000..943aba4233 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegisterBank.java @@ -0,0 +1,89 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.gadp.client.annot.GadpEventHandler; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetRegisterBank; + +public interface GadpClientTargetRegisterBank + extends GadpClientTargetObject, TargetRegisterBank { + + @Override + default CompletableFuture> readRegistersNamed( + Collection names) { + getDelegate().assertValid(); + Map result = new LinkedHashMap<>(); + Map cache = getDelegate().getRegisterCache(); + Set needed = new HashSet<>(); + for (String name : names) { + byte[] value = cache.get(name); + if (value == null) { + needed.add(name); + } + // Allow null to reserve the position + result.put(name, value); + } + if (needed.isEmpty()) { + return CompletableFuture.completedFuture(result); + } + return getModel().sendChecked(Gadp.RegisterReadRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .addAllName(needed), + Gadp.RegisterReadReply.getDefaultInstance()).thenApply(rep -> { + for (Gadp.RegisterValue rv : rep.getValueList()) { + result.put(rv.getName(), rv.getContent().toByteArray()); + } + return result; + }); + } + + @Override + default CompletableFuture writeRegistersNamed(Map values) { + getDelegate().assertValid(); + // User may change values before completion (how rude) + Map copy = Map.copyOf(values); + Map cache = getDelegate().getRegisterCache(); + return getModel().sendChecked(Gadp.RegisterWriteRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .addAllValue(GadpValueUtils.makeRegisterValues(copy)), + Gadp.RegisterWriteReply.getDefaultInstance()).thenAccept(rep -> { + cache.putAll(copy); + }); + } + + @Override + default Map getCachedRegisters() { + getDelegate().assertValid(); + Map cache = getDelegate().getRegisterCache(); + synchronized (cache) { + return Map.copyOf(cache); + } + } + + @GadpEventHandler(Gadp.EventNotification.EvtCase.REGISTER_UPDATE_EVENT) + default void handleRegisterUpdateEvent(Gadp.EventNotification notification) { + Gadp.RegisterUpdateEvent evt = notification.getRegisterUpdateEvent(); + Map updates = GadpValueUtils.getRegisterValueMap(evt.getValueList()); + DelegateGadpClientTargetObject delegate = getDelegate(); + delegate.getRegisterCache().putAll(updates); + delegate.listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, updates); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegisterContainer.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegisterContainer.java new file mode 100644 index 0000000000..c368501295 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetRegisterContainer.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetRegisterContainer; + +public interface GadpClientTargetRegisterContainer + extends GadpClientTargetObject, TargetRegisterContainer { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetResumable.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetResumable.java new file mode 100644 index 0000000000..13deb8aabe --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetResumable.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetResumable; + +public interface GadpClientTargetResumable + extends GadpClientTargetObject, TargetResumable { + @Override + default CompletableFuture resume() { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.ResumeRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())), + Gadp.ResumeReply.getDefaultInstance()).thenApply(rep -> null); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSection.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSection.java new file mode 100644 index 0000000000..003b55da07 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSection.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetSection; + +public interface GadpClientTargetSection + extends GadpClientTargetObject, TargetSection { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetStack.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetStack.java new file mode 100644 index 0000000000..68442b2b8a --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetStack.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetStack; + +public interface GadpClientTargetStack + extends GadpClientTargetObject, TargetStack { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetStackFrame.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetStackFrame.java new file mode 100644 index 0000000000..34680447dc --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetStackFrame.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetStackFrame; + +public interface GadpClientTargetStackFrame + extends GadpClientTargetObject, TargetStackFrame { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSteppable.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSteppable.java new file mode 100644 index 0000000000..0a8825b968 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSteppable.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetSteppable; + +public interface GadpClientTargetSteppable + extends GadpClientTargetObject, TargetSteppable { + @Override + default CompletableFuture step(TargetStepKind kind) { + getDelegate().assertValid(); + return getModel().sendChecked(Gadp.StepRequest.newBuilder() + .setPath(GadpValueUtils.makePath(getPath())) + .setKind(GadpValueUtils.makeStepKind(kind)), + Gadp.StepReply.getDefaultInstance()).thenApply(rep -> null); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSymbol.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSymbol.java new file mode 100644 index 0000000000..6aa3d76a52 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSymbol.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetSymbol; + +public interface GadpClientTargetSymbol + extends GadpClientTargetObject, TargetSymbol { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSymbolNamespace.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSymbolNamespace.java new file mode 100644 index 0000000000..4662c06227 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetSymbolNamespace.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetSymbolNamespace; + +public interface GadpClientTargetSymbolNamespace + extends GadpClientTargetObject, TargetSymbolNamespace { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetThread.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetThread.java new file mode 100644 index 0000000000..d5ff60b1d7 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpClientTargetThread.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import ghidra.dbg.target.TargetThread; + +public interface GadpClientTargetThread + extends GadpClientTargetObject, TargetThread { + // Nothing to add +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpTcpDebuggerModelFactory.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpTcpDebuggerModelFactory.java new file mode 100644 index 0000000000..e0dcd3957d --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpTcpDebuggerModelFactory.java @@ -0,0 +1,80 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; + +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.DebuggerModelFactory; +import ghidra.dbg.util.ConfigurableFactory.FactoryDescription; + +@FactoryDescription( // + brief = "GADP connection over TCP", // + htmlDetails = "Connect to an optionally remote agent via GADP/TCP." // +) +public class GadpTcpDebuggerModelFactory implements DebuggerModelFactory { + + private String host = "localhost"; + @FactoryOption("Agent network address") + public final Property agentAddressOption = + Property.fromAccessors(String.class, this::getAgentAddress, this::setAgentAddress); + + private int port = 15432; + @FactoryOption("Agent TCP port") + public final Property agentPortOption = + Property.fromAccessors(Integer.class, this::getAgentPort, this::setAgentPort); + + @Override + public CompletableFuture build() { + try { + // Note, it looks like the executor knows to shut itself down on GC + AsynchronousChannelGroup group = + AsynchronousChannelGroup.withThreadPool(Executors.newSingleThreadExecutor()); + AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group); + CompletableFuture connect = AsyncUtils.completable(TypeSpec.VOID, + channel::connect, new InetSocketAddress(host, port)); + return connect.thenCompose(__ -> { + GadpClient client = new GadpClient(host + ":" + port, channel); + return client.connect().thenApply(___ -> client); + }); + } + catch (IOException e) { + return CompletableFuture.failedFuture(e); + } + } + + public String getAgentAddress() { + return host; + } + + public void setAgentAddress(String host) { + this.host = host; + } + + public int getAgentPort() { + return port; + } + + public void setAgentPort(int port) { + this.port = port; + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/annot/GadpAttributeChangeCallback.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/annot/GadpAttributeChangeCallback.java new file mode 100644 index 0000000000..02b1eaf302 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/annot/GadpAttributeChangeCallback.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client.annot; + +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface GadpAttributeChangeCallback { + String value(); +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/annot/GadpEventHandler.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/annot/GadpEventHandler.java new file mode 100644 index 0000000000..78e52bdd1b --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/annot/GadpEventHandler.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client.annot; + +import java.lang.annotation.*; + +import ghidra.dbg.gadp.protocol.Gadp; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface GadpEventHandler { + Gadp.EventNotification.EvtCase value(); +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpErrorException.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpErrorException.java new file mode 100644 index 0000000000..674b7bcfac --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpErrorException.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.error; + +import ghidra.dbg.gadp.protocol.Gadp.ErrorCode; + +public class GadpErrorException extends GadpRuntimeException { + private ErrorCode code; + + public GadpErrorException(ErrorCode code, String message) { + super(code + ": " + message); + this.code = code; + } + + public ErrorCode getCode() { + return code; + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpException.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpException.java new file mode 100644 index 0000000000..5a08871c15 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpException.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.error; + +public class GadpException extends Exception { + public GadpException(String message) { + super(message); + } + + public GadpException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpIllegalStateException.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpIllegalStateException.java new file mode 100644 index 0000000000..efeac1c089 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpIllegalStateException.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.error; + +public class GadpIllegalStateException extends GadpRuntimeException { + public GadpIllegalStateException(String message) { + super(message); + } + + public GadpIllegalStateException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpMessageException.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpMessageException.java new file mode 100644 index 0000000000..1f3c3938e2 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpMessageException.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.error; + +public class GadpMessageException extends GadpRuntimeException { + public GadpMessageException(String message) { + super(message); + } + + public GadpMessageException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpRuntimeException.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpRuntimeException.java new file mode 100644 index 0000000000..8daa5b8ce5 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/error/GadpRuntimeException.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.error; + +public class GadpRuntimeException extends RuntimeException { + public GadpRuntimeException(String message) { + super(message); + } + + public GadpRuntimeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpLocalDebuggerModelFactory.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpLocalDebuggerModelFactory.java new file mode 100644 index 0000000000..15c373f836 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpLocalDebuggerModelFactory.java @@ -0,0 +1,133 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.server; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.lang.ProcessBuilder.Redirect; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.LocalDebuggerModelFactory; +import ghidra.dbg.gadp.client.GadpClient; +import ghidra.dbg.gadp.client.GadpTcpDebuggerModelFactory; +import ghidra.util.Msg; + +public abstract class AbstractGadpLocalDebuggerModelFactory implements LocalDebuggerModelFactory { + public static final boolean LOG_AGENT_STDOUT = true; + + protected String host = "localhost"; + @FactoryOption("Agent interface address") + public final Property agentAddressOption = + Property.fromAccessors(String.class, this::getAgentAddress, this::setAgentAddress); + + protected int port = 0; // Automatic, ephemeral + @FactoryOption("Agent TCP port") + public final Property agentPortOption = + Property.fromAccessors(int.class, this::getAgentPort, this::setAgentPort); + + protected int jdwpPort = -1; + @FactoryOption("Open agent's JDWP port (-1 to disable, 0 for ephemeral)") + public final Property jdwpPortOption = + Property.fromAccessors(int.class, this::getJdwpPort, this::setJdwpPort); + + /** + * Get the name of the thread which processes the agent's stdout + */ + protected abstract String getThreadName(); + + /** + * Get the command line to launch the agent + * + * Note, the given list already contains the java invocation and incorporates the -debugAgent- + * option. The implementor must incorporate all other options, including -host- and -port-. + * + * @param cmd the command line + */ + protected abstract void completeCommandLine(List cmd); + + public String getAgentAddress() { + return host; + } + + public void setAgentAddress(String host) { + this.host = host; + } + + public int getAgentPort() { + return port; + } + + public void setAgentPort(int port) { + this.port = port; + } + + public int getJdwpPort() { + return jdwpPort; + } + + public void setJdwpPort(int jdwpPort) { + this.jdwpPort = jdwpPort; + } + + @Override + public CompletableFuture build() { + CompletableFuture findPort = new CompletableFuture<>(); + new Thread(() -> { + try { + ProcessBuilder builder = new ProcessBuilder(); + List cmd = new ArrayList<>(); + cmd.add("java"); + cmd.addAll(List.of("-cp", System.getProperty("java.class.path"))); + if (jdwpPort >= 0) { + cmd.add("-agentlib:jdwp=server=y,transport=dt_socket,address=" + jdwpPort + + ",suspend=n"); + } + completeCommandLine(cmd); + builder.command(cmd); + builder.redirectError(Redirect.INHERIT); + + Process agent = builder.start(); + BufferedReader reader = + new BufferedReader(new InputStreamReader(agent.getInputStream())); + String line; + while (null != (line = reader.readLine())) { + if (LOG_AGENT_STDOUT) { + Msg.info(this, "AGENT: " + line); + } + if (line.startsWith(AbstractGadpServer.LISTENING_ON)) { + String[] parts = line.split(":"); // Separates address from port + findPort.complete(Integer.parseInt(parts[parts.length - 1])); + } + } + if (!findPort.isDone()) { + findPort.completeExceptionally( + new RuntimeException("Agent terminated unexpectedly")); + } + } + catch (Throwable e) { + findPort.completeExceptionally(e); + } + }, getThreadName()).start(); + return findPort.thenCompose(selectedPort -> { + GadpTcpDebuggerModelFactory factory = new GadpTcpDebuggerModelFactory(); + // selectedPort may differ from port option, particularly if port == 0 + factory.setAgentPort(selectedPort); + return factory.build(); + }); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpServer.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpServer.java new file mode 100644 index 0000000000..726258428b --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpServer.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.server; + +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousSocketChannel; + +import ghidra.comm.service.AbstractAsyncServer; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.gadp.error.GadpErrorException; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.protocol.Gadp.ErrorCode; +import ghidra.program.model.address.*; + +public abstract class AbstractGadpServer + extends AbstractAsyncServer { + public static final String LISTENING_ON = "GADP Server listening on "; + + protected final DebuggerObjectModel model; + + public AbstractGadpServer(DebuggerObjectModel model, SocketAddress addr) throws IOException { + super(addr); + this.model = model; + System.out.println(LISTENING_ON + getLocalAddress()); + } + + public DebuggerObjectModel getModel() { + return model; + } + + @Override + protected boolean checkAcceptable(AsynchronousSocketChannel sock) { + return true; + } + + @Override + protected GadpClientHandler newHandler(AsynchronousSocketChannel sock) { + return new GadpClientHandler(this, sock); + } + + protected AddressRange getAddressRange(Gadp.AddressRange range) { + AddressSpace space = model.getAddressSpace(range.getSpace()); + if (space == null) { + throw new GadpErrorException(ErrorCode.BAD_ADDRESS, + "Unrecognized address space: " + range); + } + Address min = space.getAddress(range.getOffset()); + // TODO: Should extend be a long? + // Note, +1 accounted for in how Ghidra AddressRanges work (inclusive of end) + return new AddressRangeImpl(min, min.add(Integer.toUnsignedLong(range.getExtend()))); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/GadpClientHandler.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/GadpClientHandler.java new file mode 100644 index 0000000000..5782090ad6 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/GadpClientHandler.java @@ -0,0 +1,760 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.server; + +import java.nio.channels.AsynchronousSocketChannel; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.google.protobuf.ByteString; +import com.google.protobuf.ProtocolStringList; + +import ghidra.async.*; +import ghidra.comm.service.AbstractAsyncClientHandler; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.error.*; +import ghidra.dbg.gadp.GadpVersion; +import ghidra.dbg.gadp.client.GadpClientTargetAttachable; +import ghidra.dbg.gadp.error.GadpErrorException; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.protocol.Gadp.ErrorCode; +import ghidra.dbg.gadp.protocol.Gadp.ObjectInvalidateEvent; +import ghidra.dbg.gadp.util.AsyncProtobufMessageChannel; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibilityListener; +import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointListener; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.dbg.target.TargetConsole.Channel; +import ghidra.dbg.target.TargetEventScope.TargetEventScopeListener; +import ghidra.dbg.target.TargetEventScope.TargetEventType; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionStateListener; +import ghidra.dbg.target.TargetFocusScope.TargetFocusScopeListener; +import ghidra.dbg.target.TargetInterpreter.TargetInterpreterListener; +import ghidra.dbg.target.TargetMemory.TargetMemoryListener; +import ghidra.dbg.target.TargetObject.TargetObjectListener; +import ghidra.dbg.target.TargetRegisterBank.TargetRegisterBankListener; +import ghidra.dbg.util.CollectionUtils.Delta; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressRange; +import ghidra.util.Msg; + +public class GadpClientHandler + extends AbstractAsyncClientHandler { + protected static final boolean LOG_ERROR_REPLY_STACKS = false; + + protected static class UpdateSuppression { + int attributesCount = 0; + int elementsCount = 0; + + boolean isEmpty() { + return attributesCount == 0 && elementsCount == 0; + } + + void incrementAttributes() { + attributesCount++; + } + + void decrementAttributes() { + assert attributesCount > 0; + attributesCount--; + } + + void incrementElements() { + elementsCount++; + } + + void decrementElements() { + assert elementsCount > 0; + elementsCount--; + } + } + + protected class ListenerForEvents + implements TargetObjectListener, TargetAccessibilityListener, TargetBreakpointListener, + TargetEventScopeListener, TargetExecutionStateListener, TargetFocusScopeListener, + TargetInterpreterListener, TargetMemoryListener, TargetRegisterBankListener { + + @Override + public void attributesChanged(TargetObject parent, Collection removed, + Map added) { + // TODO: Can elements and attributes be combined into one message? + sendDelta(parent, Delta.empty(), Delta.create(removed, added)) + .exceptionally(GadpClientHandler::errorSendNotify); + } + + @Override + public void elementsChanged(TargetObject parent, Collection removed, + Map added) { + // TODO: Can elements and attributes be combined into one message? + sendDelta(parent, Delta.create(removed, added), Delta.empty()) + .exceptionally(GadpClientHandler::errorSendNotify); + } + + @Override + public void invalidated(TargetObject object, String reason) { + if (!unsubscribeSubtree(object)) { + return; + } + channel.write(Gadp.RootMessage.newBuilder() + .setEventNotification(Gadp.EventNotification.newBuilder() + .setPath(GadpValueUtils.makePath(object.getPath())) + .setObjectInvalidateEvent( + ObjectInvalidateEvent.newBuilder().setReason(reason))) + .build()).exceptionally(GadpClientHandler::errorSendNotify); + } + + @Override + public void consoleOutput(TargetObject console, Channel c, byte[] data) { + if (c == null || console == null) { + Msg.warn(this, "Why is console or channel null in consoleOutput callback?"); + return; + } + channel.write(Gadp.RootMessage.newBuilder() + .setEventNotification(Gadp.EventNotification.newBuilder() + .setPath(GadpValueUtils.makePath(console.getPath())) + .setConsoleOutputEvent(Gadp.ConsoleOutputEvent.newBuilder() + .setChannel(c.ordinal()) + .setData(ByteString.copyFrom(data)))) + .build()).exceptionally(GadpClientHandler::errorSendNotify); + } + + @Override + public void consoleOutput(TargetObject console, Channel c, String out) { + consoleOutput(console, c, out.getBytes(TargetConsole.CHARSET)); + } + + @Override + public void breakpointHit(TargetBreakpointContainer container, TargetObjectRef trapped, + TypedTargetObjectRef> frame, + TypedTargetObjectRef> spec, + TypedTargetObjectRef> breakpoint) { + Gadp.BreakHitEvent.Builder evt = Gadp.BreakHitEvent.newBuilder() + .setTrapped(GadpValueUtils.makePath(trapped.getPath())) + .setSpec(GadpValueUtils.makePath(spec.getPath())) + .setEffective(GadpValueUtils.makePath(breakpoint.getPath())); + if (frame != null) { + evt.setFrame(GadpValueUtils.makePath(frame.getPath())); + } + channel.write(Gadp.RootMessage.newBuilder() + .setEventNotification(Gadp.EventNotification.newBuilder() + .setPath(GadpValueUtils.makePath(container.getPath())) + .setBreakHitEvent(evt)) + .build()).exceptionally(GadpClientHandler::errorSendNotify); + } + + @Override + public void invalidateCacheRequested(TargetObject object) { + channel.write(Gadp.RootMessage.newBuilder() + .setEventNotification(Gadp.EventNotification.newBuilder() + .setPath(GadpValueUtils.makePath(object.getPath())) + .setCacheInvalidateEvent( + Gadp.CacheInvalidateEvent.getDefaultInstance())) + .build()).exceptionally(GadpClientHandler::errorSendNotify); + } + + @Override + public void memoryReadError(TargetMemory memory, AddressRange range, + DebuggerMemoryAccessException e) { + // TODO: Ignore those generated by this client + channel.write(Gadp.RootMessage.newBuilder() + .setEventNotification(Gadp.EventNotification.newBuilder() + .setPath(GadpValueUtils.makePath(memory.getPath())) + .setMemoryErrorEvent(Gadp.MemoryErrorEvent.newBuilder() + .setRange(GadpValueUtils.makeRange(range)) + .setMessage(e.getMessage()))) + .build()).exceptionally(GadpClientHandler::errorSendNotify); + } + + @Override + public void memoryUpdated(TargetMemory memory, Address address, byte[] data) { + // TODO: Ignore those generated by this client + channel.write(Gadp.RootMessage.newBuilder() + .setEventNotification(Gadp.EventNotification.newBuilder() + .setPath(GadpValueUtils.makePath(memory.getPath())) + .setMemoryUpdateEvent(Gadp.MemoryUpdateEvent.newBuilder() + .setAddress(GadpValueUtils.makeAddress(address)) + .setContent(ByteString.copyFrom(data)))) + .build()).exceptionally(GadpClientHandler::errorSendNotify); + } + + @Override + public void registersUpdated(TargetRegisterBank bank, Map updates) { + // TODO: Ignore those generated by this client + channel.write(Gadp.RootMessage.newBuilder() + .setEventNotification(Gadp.EventNotification.newBuilder() + .setPath(GadpValueUtils.makePath(bank.getPath())) + .setRegisterUpdateEvent(Gadp.RegisterUpdateEvent.newBuilder() + .addAllValue(GadpValueUtils.makeRegisterValues(updates)))) + .build()).exceptionally(GadpClientHandler::errorSendNotify); + } + + @Override + public void event(TargetEventScope object, + TypedTargetObjectRef> eventThread, TargetEventType type, + String description, List parameters) { + Gadp.TargetEvent.Builder evt = Gadp.TargetEvent.newBuilder(); + if (eventThread != null) { + evt.setEventThread(GadpValueUtils.makePath(eventThread.getPath())); + } + evt.setType(GadpValueUtils.makeTargetEventType(type)); + evt.setDescription(description); + evt.addAllParameters(GadpValueUtils.makeValues(parameters)); + channel.write(Gadp.RootMessage.newBuilder() + .setEventNotification(Gadp.EventNotification.newBuilder() + .setPath(GadpValueUtils.makePath(object.getPath())) + .setTargetEvent(evt)) + .build()).exceptionally(GadpClientHandler::errorSendNotify); + } + } + + protected static T errorSendNotify(Throwable e) { + Msg.error(GadpClientHandler.class, "Could not send notification: " + e); + return null; + } + + protected final DebuggerObjectModel model; + protected final AsyncProtobufMessageChannel channel; + protected final ListenerForEvents listenerForEvents = new ListenerForEvents(); + // Keeps strong references and tells level of subscription + protected final NavigableSet subscriptions = new TreeSet<>(); + + public GadpClientHandler(AbstractGadpServer server, AsynchronousSocketChannel sock) { + super(server, sock); + model = server.model; + channel = new AsyncProtobufMessageChannel(sock); + } + + @Override + protected CompletableFuture launchAsync() { + return AsyncUtils.loop(TypeSpec.VOID, loop -> { + if (sock.isOpen()) { + channel.read(Gadp.RootMessage::parseFrom).handle(loop::consume); + } + else { + loop.exit(); + } + }, TypeSpec.cls(Gadp.RootMessage.class), (msg, loop) -> { + loop.repeat(); + try { + processMessage(msg).exceptionally(e -> { + replyError(msg, e).exceptionally(ee -> { + Msg.error(this, "Could not send error reply: " + ee); + return null; + }); + return null; + }); // Do not handle with loop. Loop is already repeating + } + catch (Throwable e) { + replyError(msg, e).exceptionally(ee -> { + Msg.error(this, "Could not send error reply: " + ee); + return null; + }); + } + }); + } + + protected Gadp.RootMessage buildError(Gadp.RootMessage req, ErrorCode code, String message) { + return Gadp.RootMessage.newBuilder() + .setSequence(req.getSequence()) + .setErrorReply(Gadp.ErrorReply.newBuilder().setCode(code).setMessage(message)) + .build(); + } + + protected CompletableFuture replyError(Gadp.RootMessage req, Throwable e) { + Throwable t = AsyncUtils.unwrapThrowable(e); + if (LOG_ERROR_REPLY_STACKS) { + Msg.debug(this, "Error caused by request " + req, e); + } + else { + Msg.debug(this, "Error caused by request " + req + ": " + e); + } + if (t instanceof GadpErrorException) { + GadpErrorException error = (GadpErrorException) t; + return channel.write(buildError(req, error.getCode(), error.getMessage())); + } + if (t instanceof UnsupportedOperationException) { + return channel.write(buildError(req, ErrorCode.NOT_SUPPORTED, t.getMessage())); + } + if (t instanceof DebuggerModelNoSuchPathException) { + return channel.write(buildError(req, ErrorCode.NO_OBJECT, t.getMessage())); + } + if (t instanceof DebuggerModelTypeException) { + return channel.write(buildError(req, ErrorCode.NO_INTERFACE, t.getMessage())); + } + if (t instanceof DebuggerIllegalArgumentException) { + return channel.write(buildError(req, ErrorCode.BAD_ARGUMENT, t.getMessage())); + } + if (t instanceof DebuggerMemoryAccessException) { + return channel.write(buildError(req, ErrorCode.MEMORY_ACCESS, t.getMessage())); + } + if (t instanceof DebuggerRegisterAccessException) { + return channel.write(buildError(req, ErrorCode.REGISTER_ACCESS, t.getMessage())); + } + if (t instanceof DebuggerUserException) { + return channel.write(buildError(req, ErrorCode.USER_ERROR, t.getMessage())); + } + if (t instanceof DebuggerModelAccessException) { + return channel.write(buildError(req, ErrorCode.MODEL_ACCESS, t.getMessage())); + } + return channel.write(buildError(req, ErrorCode.UNKNOWN, "Unknown server-side error")); + } + + protected GadpVersion getVersion() { + return GadpVersion.VER1; + } + + protected CompletableFuture processMessage(Gadp.RootMessage msg) { + switch (msg.getMsgCase()) { + case CONNECT_REQUEST: + return processConnect(msg.getSequence(), msg.getConnectRequest()); + case PING_REQUEST: + return processPing(msg.getSequence(), msg.getPingRequest()); + case ATTACH_REQUEST: + return processAttach(msg.getSequence(), msg.getAttachRequest()); + case BREAK_CREATE_REQUEST: + return processBreakCreate(msg.getSequence(), msg.getBreakCreateRequest()); + case BREAK_TOGGLE_REQUEST: + return processBreakToggle(msg.getSequence(), msg.getBreakToggleRequest()); + case CACHE_INVALIDATE_REQUEST: + return processCacheInvalidate(msg.getSequence(), msg.getCacheInvalidateRequest()); + case DELETE_REQUEST: + return processDelete(msg.getSequence(), msg.getDeleteRequest()); + case DETACH_REQUEST: + return processDetach(msg.getSequence(), msg.getDetachRequest()); + case EXECUTE_REQUEST: + return processExecute(msg.getSequence(), msg.getExecuteRequest()); + case FOCUS_REQUEST: + return processFocus(msg.getSequence(), msg.getFocusRequest()); + case INTERRUPT_REQUEST: + return processInterrupt(msg.getSequence(), msg.getInterruptRequest()); + case KILL_REQUEST: + return processKill(msg.getSequence(), msg.getKillRequest()); + case LAUNCH_REQUEST: + return processLaunch(msg.getSequence(), msg.getLaunchRequest()); + case MEMORY_READ_REQUEST: + return processMemoryRead(msg.getSequence(), msg.getMemoryReadRequest()); + case MEMORY_WRITE_REQUEST: + return processMemoryWrite(msg.getSequence(), msg.getMemoryWriteRequest()); + case REGISTER_READ_REQUEST: + return processRegisterRead(msg.getSequence(), msg.getRegisterReadRequest()); + case REGISTER_WRITE_REQUEST: + return processRegisterWrite(msg.getSequence(), msg.getRegisterWriteRequest()); + case RESUME_REQUEST: + return processResume(msg.getSequence(), msg.getResumeRequest()); + case STEP_REQUEST: + return processStep(msg.getSequence(), msg.getStepRequest()); + case SUBSCRIBE_REQUEST: + return processSubscribe(msg.getSequence(), msg.getSubscribeRequest()); + case INVOKE_REQUEST: + return processInvoke(msg.getSequence(), msg.getInvokeRequest()); + default: + throw new GadpErrorException(ErrorCode.BAD_REQUEST, + "Unrecognized request: " + msg.getMsgCase()); + } + } + + protected CompletableFuture processConnect(int seqno, Gadp.ConnectRequest req) { + String ver = getVersion().getName(); + if (!req.getVersionList().contains(ver)) { + throw new GadpErrorException(ErrorCode.NO_VERSION, "No listed version is supported"); + } + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setConnectReply(Gadp.ConnectReply.newBuilder().setVersion(ver)) + .build()); + } + + protected CompletableFuture processPing(int seqno, Gadp.PingRequest req) { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setPingReply(Gadp.PingReply.newBuilder().setContent(req.getContent())) + .build()); + } + + protected T isObjectValuedAttribute(TargetObject parent, + Map.Entry ent, Class cls) { + Object val = ent.getValue(); + if (!cls.isAssignableFrom(val.getClass())) { + return null; + } + T ref = cls.cast(val); + if (!ref.getPath().equals(PathUtils.extend(parent.getPath(), ent.getKey()))) { + return null; + } + return ref; + } + + protected void changeSubscription(TargetObject obj, boolean subscribed) { + synchronized (subscriptions) { + if (subscribed) { + if (subscriptions.add(obj)) { + obj.addListener(listenerForEvents); + } + } + else { + subscriptions.remove(obj); + obj.removeListener(listenerForEvents); + } + } + } + + protected boolean isSubscribed(TargetObject obj) { + synchronized (subscriptions) { + return subscriptions.contains(obj); + } + } + + protected boolean unsubscribeSubtree(TargetObject seed) { + synchronized (subscriptions) { + if (!subscriptions.remove(seed)) { + return false; + } + Set tail = subscriptions.tailSet(seed); + for (Iterator it = tail.iterator(); it.hasNext();) { + TargetObject o = it.next(); + if (!PathUtils.isAncestor(seed.getPath(), o.getPath())) { + break; + } + o.removeListener(listenerForEvents); + it.remove(); + } + return true; + } + } + + protected CompletableFuture sendDelta(TargetObject parent, + Delta deltaE, Delta deltaA) { + assert isSubscribed(parent); // If listening, we should be subscribed + return channel.write(Gadp.RootMessage.newBuilder() + .setEventNotification(Gadp.EventNotification.newBuilder() + .setPath(GadpValueUtils.makePath(parent.getPath())) + .setModelObjectEvent(Gadp.ModelObjectEvent.newBuilder() + .setDelta(GadpValueUtils.makeDelta(parent, deltaE, deltaA)))) + .build()); + } + + /** + * @implNote To avoid duplicates and spurious messages, we adopt the following strategy: 1) Get + * any elements and/or attributes for the object as requested. 2) Send the response + * and install the listener on the object. 3) Check if the object's elements and/or + * attributes have changed and send a delta immediately. + */ + protected CompletableFuture processSubscribe(int seqno, Gadp.SubscribeRequest req) { + List path = req.getPath().getEList(); + return model.fetchModelValue(path).thenCompose(val -> { + DebuggerObjectModel.requireNonNull(val, path); + TargetObject obj = GadpValueUtils.getTargetObjectNonLink(path, val); + if (obj == null) { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setSubscribeReply(Gadp.SubscribeReply.newBuilder() + .setValue(GadpValueUtils.makeValue(path, val))) + .build()); + } + + changeSubscription(obj, req.getSubscribe()); + AsyncFence fence = new AsyncFence(); + if (req.getFetchElements()) { + fence.include(obj.fetchElements(req.getRefreshElements())); + } + if (req.getFetchAttributes()) { + fence.include(obj.fetchAttributes(req.getRefreshAttributes())); + } + return fence.ready().thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setSubscribeReply(Gadp.SubscribeReply.newBuilder() + .setValue(Gadp.Value.newBuilder() + .setObjectInfo(GadpValueUtils.makeInfo(obj)))) + .build()); + }); + }); + } + + protected CompletableFuture processInvoke(int seqno, Gadp.InvokeRequest req) { + List path = req.getPath().getEList(); + return model.fetchModelObject(path).thenCompose(obj -> { + TargetMethod method = + DebuggerObjectModel.requireIface(TargetMethod.class, obj, path); + return method.invoke(GadpValueUtils.getArguments(model, req.getArgumentList())); + }).thenCompose(result -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setInvokeReply(Gadp.InvokeReply.newBuilder() + .setResult(GadpValueUtils.makeValue(null, result))) + .build()); + }); + } + + protected CompletableFuture processAttach(int seqno, Gadp.AttachRequest req) { + ProtocolStringList path = req.getPath().getEList(); + return model.fetchModelObject(path).thenCompose(obj -> { + TargetAttacher attacher = + DebuggerObjectModel.requireIface(TargetAttacher.class, obj, path); + switch (req.getSpecCase()) { + case TARGET: + TypedTargetObjectRef attachable = + model.createRef(req.getTarget().getEList()) + .as(GadpClientTargetAttachable.class); + return attacher.attach(attachable); + case PID: + return attacher.attach(req.getPid()); + default: + throw new GadpErrorException(ErrorCode.BAD_REQUEST, + "Unrecognized attach specification:" + req); + } + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setAttachReply(Gadp.AttachReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processBreakCreate(int seqno, Gadp.BreakCreateRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetBreakpointContainer breaks = DebuggerObjectModel + .requireIface(TargetBreakpointContainer.class, obj, req.getPath().getEList()); + Set kinds = GadpValueUtils.getBreakKindSet(req.getKinds()); + switch (req.getSpecCase()) { + case EXPRESSION: + return breaks.placeBreakpoint(req.getExpression(), kinds); + case ADDRESS: + AddressRange range = server.getAddressRange(req.getAddress()); + return breaks.placeBreakpoint(range, kinds); + default: + throw new GadpErrorException(ErrorCode.BAD_REQUEST, + "Unrecognized breakpoint specification: " + req); + } + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setBreakCreateReply(Gadp.BreakCreateReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processBreakToggle(int seqno, Gadp.BreakToggleRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetBreakpointSpec spec = DebuggerObjectModel + .requireIface(TargetBreakpointSpec.tclass, obj, req.getPath().getEList()); + return spec.toggle(req.getEnabled()); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setBreakToggleReply(Gadp.BreakToggleReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processDelete(int seqno, Gadp.DeleteRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetDeletable del = DebuggerObjectModel.requireIface(TargetDeletable.tclass, obj, + req.getPath().getEList()); + return del.delete(); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setDeleteReply(Gadp.DeleteReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processDetach(int seqno, Gadp.DetachRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetDetachable det = DebuggerObjectModel.requireIface(TargetDetachable.tclass, obj, + req.getPath().getEList()); + return det.detach(); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setDetachReply(Gadp.DetachReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processExecute(int seqno, Gadp.ExecuteRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetInterpreter interpreter = DebuggerObjectModel + .requireIface(TargetInterpreter.tclass, obj, req.getPath().getEList()); + if (req.getCapture()) { + return interpreter.executeCapture(req.getCommand()); + } + return interpreter.execute(req.getCommand()).thenApply(__ -> ""); + }).thenCompose(out -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setExecuteReply(Gadp.ExecuteReply.newBuilder().setCaptured(out)) + .build()); + }); + } + + protected CompletableFuture processFocus(int seqno, Gadp.FocusRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetFocusScope scope = DebuggerObjectModel.requireIface(TargetFocusScope.tclass, + obj, req.getPath().getEList()); + return scope.requestFocus(model.createRef(req.getFocus().getEList())); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setFocusReply(Gadp.FocusReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processInterrupt(int seqno, Gadp.InterruptRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetInterruptible interruptible = DebuggerObjectModel + .requireIface(TargetInterruptible.tclass, obj, req.getPath().getEList()); + return interruptible.interrupt(); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setInterruptReply(Gadp.InterruptReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processCacheInvalidate(int seqno, + Gadp.CacheInvalidateRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + return DebuggerObjectModel.requireNonNull(obj, req.getPath().getEList()) + .invalidateCaches(); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setCacheInvalidateReply(Gadp.CacheInvalidateReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processKill(int seqno, Gadp.KillRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetKillable killable = DebuggerObjectModel.requireIface(TargetKillable.class, obj, + req.getPath().getEList()); + return killable.kill(); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setKillReply(Gadp.KillReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processLaunch(int seqno, Gadp.LaunchRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetLauncher launcher = DebuggerObjectModel.requireIface(TargetLauncher.class, obj, + req.getPath().getEList()); + return launcher.launch(GadpValueUtils.getArguments(model, req.getArgumentList())); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setLaunchReply(Gadp.LaunchReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processMemoryRead(int seqno, Gadp.MemoryReadRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetMemory memory = + DebuggerObjectModel.requireIface(TargetMemory.class, obj, req.getPath().getEList()); + AddressRange range = GadpValueUtils.getAddressRange(memory.getModel(), req.getRange()); + return memory.readMemory(range.getMinAddress(), (int) range.getLength()); + }).thenCompose(data -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setMemoryReadReply( + Gadp.MemoryReadReply.newBuilder().setContent(ByteString.copyFrom(data))) + .build()); + }); + } + + protected CompletableFuture processMemoryWrite(int seqno, Gadp.MemoryWriteRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetMemory memory = + DebuggerObjectModel.requireIface(TargetMemory.class, obj, req.getPath().getEList()); + Address start = GadpValueUtils.getAddress(memory.getModel(), req.getStart()); + // TODO: Spare a copy by specifying a ByteBuffer variant of writeMemory? + return memory.writeMemory(start, req.getContent().toByteArray()); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setMemoryWriteReply(Gadp.MemoryWriteReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processRegisterRead(int seqno, Gadp.RegisterReadRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetRegisterBank bank = DebuggerObjectModel.requireIface(TargetRegisterBank.class, + obj, req.getPath().getEList()); + return bank.readRegistersNamed(req.getNameList()); + }).thenCompose(data -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setRegisterReadReply(Gadp.RegisterReadReply.newBuilder() + .addAllValue(GadpValueUtils.makeRegisterValues(data))) + .build()); + }); + } + + protected CompletableFuture processRegisterWrite(int seqno, Gadp.RegisterWriteRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetRegisterBank bank = DebuggerObjectModel.requireIface(TargetRegisterBank.class, + obj, req.getPath().getEList()); + Map values = new LinkedHashMap<>(); + for (Gadp.RegisterValue rv : req.getValueList()) { + values.put(rv.getName(), rv.getContent().toByteArray()); + } + return bank.writeRegistersNamed(values); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setRegisterWriteReply(Gadp.RegisterWriteReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processResume(int seqno, Gadp.ResumeRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetResumable resumable = DebuggerObjectModel.requireIface(TargetResumable.class, + obj, req.getPath().getEList()); + return resumable.resume(); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setResumeReply(Gadp.ResumeReply.getDefaultInstance()) + .build()); + }); + } + + protected CompletableFuture processStep(int seqno, Gadp.StepRequest req) { + return model.fetchModelObject(req.getPath().getEList()).thenCompose(obj -> { + TargetSteppable steppable = DebuggerObjectModel.requireIface(TargetSteppable.class, + obj, req.getPath().getEList()); + return steppable.step(GadpValueUtils.getStepKind(req.getKind())); + }).thenCompose(__ -> { + return channel.write(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setStepReply(Gadp.StepReply.getDefaultInstance()) + .build()); + }); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannel.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannel.java new file mode 100644 index 0000000000..2f9bd3e797 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannel.java @@ -0,0 +1,178 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.util; + +import java.io.EOFException; +import java.io.IOException; +import java.nio.*; +import java.nio.channels.AsynchronousByteChannel; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; + +import com.google.protobuf.*; +import com.google.protobuf.CodedOutputStream.OutOfSpaceException; + +import ghidra.async.*; +import ghidra.comm.util.ByteBufferUtils; +import ghidra.util.Msg; + +public class AsyncProtobufMessageChannel { + public static final boolean LOG_READ = false; + public static final boolean LOG_WRITE = false; + + public interface IOFunction { + R read(CodedInputStream stream) throws IOException; + } + + public static final int DEFAULT_BUFFER_SIZE = 4096; + + public static void marshall(Message msg, ByteBuffer wBuf) throws IOException { + int savedPosition = wBuf.position(); + try { + wBuf.putInt(0); // Placeholder for len + CodedOutputStream out = CodedOutputStream.newInstance(wBuf); + msg.writeTo(out); + out.flush(); + wBuf.putInt(savedPosition, out.getTotalBytesWritten()); + savedPosition = wBuf.position(); + } + finally { + wBuf.position(savedPosition); + } + } + + public static R unmarshall(IOFunction receiver, ByteBuffer rBuf) + throws IOException { + rBuf.flip(); + int savedPosition = rBuf.position(); + int savedLimit = rBuf.limit(); + try { + int len = rBuf.getInt(); + if (rBuf.remaining() < len) { + throw new BufferUnderflowException(); + } + rBuf.limit(rBuf.position() + len); + CodedInputStream in = CodedInputStream.newInstance(rBuf); + R res = receiver.read(in); + savedPosition = rBuf.position() + in.getTotalBytesRead(); + return res; + } + finally { + rBuf.position(savedPosition); + rBuf.limit(savedLimit); + rBuf.compact(); + } + } + + private final AsynchronousByteChannel channel; + + /** A lock for sharing the underlying channel's read operation */ + protected final AsyncLock rLock = new AsyncLock(); + /** A lock for sharing the underlying channel's write operation */ + protected final AsyncLock wLock = new AsyncLock(); + /** A buffer of received data */ + protected ByteBuffer rBuf; + /** A buffer of data to send */ + protected ByteBuffer wBuf; + + public AsyncProtobufMessageChannel(AsynchronousByteChannel channel) { + this(channel, DEFAULT_BUFFER_SIZE); + } + + public AsyncProtobufMessageChannel(AsynchronousByteChannel channel, int initialBufferSize) { + this.rBuf = ByteBuffer.allocate(initialBufferSize); + this.wBuf = ByteBuffer.allocate(initialBufferSize); + this.channel = channel; + } + + /** + * Write a message to the channel + * + * @param msg the message to write + * @return the length of the encoded message as a future + */ + public CompletableFuture write(S msg) { + if (LOG_WRITE) { + Msg.debug(this, "Writing: " + msg); + } + AtomicInteger len = new AtomicInteger(); + return wLock.with(TypeSpec.INT, null).then((own, seq) -> { + while (true) { + try { + wBuf.clear(); + marshall(msg, wBuf); + break; + } + catch (BufferOverflowException | OutOfSpaceException e) { + wBuf.clear(); + wBuf = ByteBufferUtils.upsize(wBuf); + } + catch (IOException e) { + seq.exit(e); + return; + } + } + wBuf.flip(); + len.set(wBuf.remaining()); + AsyncUtils.loop(TypeSpec.VOID, loop -> { + AsyncUtils.completable(TypeSpec.INT, channel::write, wBuf).handle(loop::consume); + }, TypeSpec.INT, (l, loop) -> { + loop.repeatWhile(wBuf.hasRemaining()); + }).handle(seq::next); + }).then(seq -> { + seq.exit(len.get()); + }).finish(); + } + + /** + * Read and decode a packet from the channel + * + * @param pktType the type of packet expected + * @return the read packet as a future + */ + public CompletableFuture read(IOFunction receiver) { + return rLock.with(TypeSpec.obj((R2) null), null).then((own, seq) -> { + AsyncUtils.loop(TypeSpec.obj((R2) null), (loop) -> { + try { + R2 msg = unmarshall(receiver, rBuf); + if (LOG_READ) { + Msg.debug(this, "Read: " + msg); + } + loop.exit(msg); + } + catch (BufferUnderflowException | ArrayIndexOutOfBoundsException e) { + AsyncUtils.completable(TypeSpec.INT, channel::read, rBuf).handle(loop::consume); + } + catch (Exception e) { + loop.exit(e); + } + }, TypeSpec.INT, (lenread, loop) -> { + if (lenread == -1) { + loop.exit(new EOFException("Channel is closed")); + return; + } + else if (lenread == 0) { + rBuf = ByteBufferUtils.upsize(rBuf); + } + loop.repeat(); + }).handle(seq::exit); + }).finish(); + } + + public void close() throws IOException { + channel.close(); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/GadpValueUtils.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/GadpValueUtils.java new file mode 100644 index 0000000000..87cda2fc05 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/GadpValueUtils.java @@ -0,0 +1,738 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.util; + +import static ghidra.lifecycle.Unfinished.TODO; + +import java.util.*; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import com.google.protobuf.ByteString; + +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.attributes.*; +import ghidra.dbg.attributes.TargetObjectRefList.DefaultTargetObjectRefList; +import ghidra.dbg.gadp.GadpRegistry; +import ghidra.dbg.gadp.client.GadpClient; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.protocol.Gadp.ModelObjectDelta; +import ghidra.dbg.gadp.protocol.Gadp.ModelObjectInfo; +import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.dbg.target.TargetEventScope.TargetEventType; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetObject.TargetUpdateMode; +import ghidra.dbg.target.TargetSteppable.TargetStepKind; +import ghidra.dbg.target.TargetSteppable.TargetStepKindSet; +import ghidra.dbg.util.CollectionUtils.Delta; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.*; +import ghidra.util.Msg; + +public enum GadpValueUtils { + ; + + public static TargetObjectRefList getRefList(DebuggerObjectModel model, Gadp.PathList list) { + TargetObjectRefList result = new DefaultTargetObjectRefList<>(); + for (Gadp.Path path : list.getPathList()) { + result.add(model.createRef(path.getEList())); + } + return result; + } + + public static TargetBreakpointKindSet getBreakKindSet(Gadp.BreakKindsSet set) { + return TargetBreakpointKindSet.copyOf( + set.getKList().stream().map(k -> getBreakKind(k)).collect(Collectors.toSet())); + } + + public static TargetBreakpointKind getBreakKind(Gadp.BreakKind kind) { + switch (kind) { + case READ: + return TargetBreakpointKind.READ; + case WRITE: + return TargetBreakpointKind.WRITE; + case EXECUTE: + return TargetBreakpointKind.EXECUTE; + case SOFTWARE: + return TargetBreakpointKind.SOFTWARE; + default: + throw new IllegalArgumentException(); + } + } + + public static Gadp.BreakKind makeBreakKind(TargetBreakpointKind kind) { + switch (kind) { + case READ: + return Gadp.BreakKind.READ; + case WRITE: + return Gadp.BreakKind.WRITE; + case EXECUTE: + return Gadp.BreakKind.EXECUTE; + case SOFTWARE: + return Gadp.BreakKind.SOFTWARE; + default: + throw new IllegalArgumentException(); + } + } + + public static TargetStepKindSet getStepKindSet(Gadp.StepKindsSet set) { + return TargetStepKindSet.copyOf( + set.getKList().stream().map(k -> getStepKind(k)).collect(Collectors.toSet())); + } + + public static TargetStepKind getStepKind(Gadp.StepKind kind) { + switch (kind) { + case ADVANCE: + return TargetStepKind.ADVANCE; + case FINISH: + return TargetStepKind.FINISH; + case INTO: + return TargetStepKind.INTO; + case LINE: + return TargetStepKind.LINE; + case OVER: + return TargetStepKind.OVER; + case OVER_LINE: + return TargetStepKind.OVER_LINE; + case RETURN: + return TargetStepKind.RETURN; + case SKIP: + return TargetStepKind.SKIP; + case UNTIL: + return TargetStepKind.UNTIL; + default: + throw new IllegalArgumentException(); + } + } + + public static Gadp.StepKind makeStepKind(TargetStepKind kind) { + switch (kind) { + case ADVANCE: + return Gadp.StepKind.ADVANCE; + case FINISH: + return Gadp.StepKind.FINISH; + case INTO: + return Gadp.StepKind.INTO; + case LINE: + return Gadp.StepKind.LINE; + case OVER: + return Gadp.StepKind.OVER; + case OVER_LINE: + return Gadp.StepKind.OVER_LINE; + case RETURN: + return Gadp.StepKind.RETURN; + case SKIP: + return Gadp.StepKind.SKIP; + case UNTIL: + return Gadp.StepKind.UNTIL; + default: + throw new IllegalArgumentException(); + } + } + + public static TargetExecutionState getExecutionState(Gadp.ExecutionState state) { + switch (state) { + case INACTIVE: + return TargetExecutionState.INACTIVE; + case ALIVE: + return TargetExecutionState.ALIVE; + case STOPPED: + return TargetExecutionState.STOPPED; + case RUNNING: + return TargetExecutionState.RUNNING; + case TERMINATED: + return TargetExecutionState.TERMINATED; + default: + throw new IllegalArgumentException(); + } + } + + public static Gadp.ExecutionState makeExecutionState(TargetExecutionState state) { + switch (state) { + case INACTIVE: + return Gadp.ExecutionState.INACTIVE; + case ALIVE: + return Gadp.ExecutionState.ALIVE; + case STOPPED: + return Gadp.ExecutionState.STOPPED; + case RUNNING: + return Gadp.ExecutionState.RUNNING; + case TERMINATED: + return Gadp.ExecutionState.TERMINATED; + default: + throw new IllegalArgumentException(); + } + } + + public static TargetEventType getTargetEventType(Gadp.TargetEventType type) { + switch (type) { + default: + case EV_STOPPED: + return TargetEventType.STOPPED; + case EV_RUNNING: + return TargetEventType.RUNNING; + case PROCESS_CREATED: + return TargetEventType.PROCESS_CREATED; + case PROCESS_EXITED: + return TargetEventType.PROCESS_EXITED; + case THREAD_CREATED: + return TargetEventType.THREAD_CREATED; + case THREAD_EXITED: + return TargetEventType.THREAD_EXITED; + case MODULE_LOADED: + return TargetEventType.MODULE_LOADED; + case MODULE_UNLOADED: + return TargetEventType.MODULE_UNLOADED; + case BREAKPOINT_HIT: + return TargetEventType.BREAKPOINT_HIT; + case STEP_COMPLETED: + return TargetEventType.STEP_COMPLETED; + case EXCEPTION: + return TargetEventType.EXCEPTION; + case SIGNAL: + return TargetEventType.SIGNAL; + } + } + + public static Gadp.TargetEventType makeTargetEventType(TargetEventType type) { + switch (type) { + default: + case STOPPED: + return Gadp.TargetEventType.EV_STOPPED; + case RUNNING: + return Gadp.TargetEventType.EV_RUNNING; + case PROCESS_CREATED: + return Gadp.TargetEventType.PROCESS_CREATED; + case PROCESS_EXITED: + return Gadp.TargetEventType.PROCESS_EXITED; + case THREAD_CREATED: + return Gadp.TargetEventType.THREAD_CREATED; + case THREAD_EXITED: + return Gadp.TargetEventType.THREAD_EXITED; + case MODULE_LOADED: + return Gadp.TargetEventType.MODULE_LOADED; + case MODULE_UNLOADED: + return Gadp.TargetEventType.MODULE_UNLOADED; + case BREAKPOINT_HIT: + return Gadp.TargetEventType.BREAKPOINT_HIT; + case STEP_COMPLETED: + return Gadp.TargetEventType.STEP_COMPLETED; + case EXCEPTION: + return Gadp.TargetEventType.EXCEPTION; + case SIGNAL: + return Gadp.TargetEventType.SIGNAL; + } + } + + /** + * TODO: Document me + * + * @see GadpClient#getAddress(ghidra.dbg.gadp.protocol.Gadp.Address) + * @param address + * @return + */ + public static Gadp.Address makeAddress(Address address) { + return Gadp.Address.newBuilder() + .setSpace(address.getAddressSpace().getName()) + .setOffset(address.getOffset()) + .build(); + } + + public static Gadp.AddressRange makeRange(AddressRange range) { + return Gadp.AddressRange.newBuilder() + .setSpace(range.getAddressSpace().getName()) + .setOffset(range.getMinAddress().getOffset()) + .setExtend((int) (range.getLength() - 1)) + .build(); + } + + public static Gadp.ModelObjectInfo makeInfo(TargetObject obj) { + ModelObjectInfo.Builder builder = Gadp.ModelObjectInfo.newBuilder() + .setPath(GadpValueUtils.makePath(obj.getPath())) + .setTypeHint(obj.getTypeHint()) + .addAllInterface(GadpRegistry.getInterfaceNames(obj)); + + builder.addAllElementIndex(obj.getCachedElements().keySet()); + for (Entry ent : obj.getCachedAttributes().entrySet()) { + builder.addAttribute(makeAttribute(obj, ent)); + } + + return builder.build(); + } + + public static Gadp.ModelObjectDelta makeDelta(TargetObject parent, + Delta deltaE, Delta deltaA) { + ModelObjectDelta.Builder builder = Gadp.ModelObjectDelta.newBuilder() + .addAllIndexRemoved(deltaE.getKeysRemoved()) + .addAllIndexAdded(deltaE.added.keySet()) + .addAllAttributeRemoved(deltaA.getKeysRemoved()); + for (Entry ent : deltaA.added.entrySet()) { + builder.addAttributeAdded(makeAttribute(parent, ent)); + } + return builder.build(); + } + + public static Gadp.Path makePath(List path) { + return Gadp.Path.newBuilder().addAllE(path).build(); + } + + public static Gadp.Path makePath(TargetObjectRef ref) { + return makePath(ref.getPath()); + } + + public static Gadp.PathList makePathList(TargetObjectRefList list) { + return Gadp.PathList.newBuilder() + .addAllPath(list.stream().map(p -> makePath(p)).collect(Collectors.toList())) + .build(); + } + + public static Gadp.BreakKindsSet makeBreakKindSet(Set set) { + return Gadp.BreakKindsSet.newBuilder() + .addAllK(set.stream().map(k -> makeBreakKind(k)).collect(Collectors.toList())) + .build(); + } + + public static Gadp.RegisterValue makeRegisterValue(Map.Entry e) { + return Gadp.RegisterValue.newBuilder() + .setName(e.getKey()) + .setContent(ByteString.copyFrom(e.getValue())) + .build(); + } + + public static Collection makeRegisterValues(Map map) { + return map.entrySet() + .stream() + .map(GadpValueUtils::makeRegisterValue) + .collect(Collectors.toList()); + } + + public static Map getRegisterValueMap(Collection values) { + return values.stream() + .collect(Collectors.toMap(v -> v.getName(), v -> v.getContent().toByteArray())); + } + + public static Gadp.StepKindsSet makeStepKindSet(Set set) { + return Gadp.StepKindsSet.newBuilder() + .addAllK(set.stream().map(k -> makeStepKind(k)).collect(Collectors.toList())) + .build(); + } + + // TODO: Remove type-specific collections, and just use collections of values (objects) + // NOTE: Map should probably be strictly strings for keys. + public static Gadp.StringList makeStringList(Collection col) { + return Gadp.StringList.newBuilder().addAllS(col).build(); + } + + public static TargetStringList getStringList(Gadp.StringList list) { + return TargetStringList.copyOf(list.getSList()); + } + + public static Gadp.UpdateMode makeUpdateMode(TargetUpdateMode mode) { + switch (mode) { + case SOLICITED: + return Gadp.UpdateMode.SOLICITED; + case FIXED: + return Gadp.UpdateMode.FIXED; + case UNSOLICITED: + default: + return Gadp.UpdateMode.UNSOLICITED; + } + } + + public static TargetUpdateMode getUpdateMode(Gadp.UpdateMode mode) { + switch (mode) { + case FIXED: + return TargetUpdateMode.FIXED; + case SOLICITED: + return TargetUpdateMode.SOLICITED; + case UNSOLICITED: + default: + return TargetUpdateMode.UNSOLICITED; + } + } + + public static Class getValueType(Gadp.ValueType type) { + switch (type) { + case VT_VOID: + return Void.class; + case VT_BOOL: + return Boolean.class; + case VT_INT: + return Integer.class; + case VT_LONG: + return Long.class; + case VT_FLOAT: + return Float.class; + case VT_DOUBLE: + return Double.class; + case VT_BYTES: + return byte[].class; + case VT_STRING: + return String.class; + case VT_STRING_LIST: + return TargetStringList.class; + case VT_ADDRESS: + return Address.class; + case VT_RANGE: + return AddressRange.class; + case VT_BREAK_KIND_SET: + return TargetBreakpointKindSet.class; + case VT_EXECUTION_STATE: + return TargetExecutionState.class; + case VT_STEP_KIND_SET: + return TargetStepKindSet.class; + case VT_PRIMITIVE_KIND: + return TargetPrimitiveDataType.class; + case VT_DATA_TYPE: + return TargetDataType.class; + case VT_UPDATE_MODE: + return TargetUpdateMode.class; + case VT_PATH: + return TargetObjectRef.class; + case VT_PATH_LIST: + return TargetObjectRefList.class; + case VT_TYPE: + return Class.class; + case UNRECOGNIZED: + default: + throw new AssertionError("Unrecgonized type: " + type); + } + } + + private static ParameterDescription getParameterDescription(Class valueType, + DebuggerObjectModel model, Gadp.Parameter param) { + if (param.getChoicesCount() != 0) { + return ParameterDescription.choices(valueType, param.getName(), + getValues(model, param.getChoicesList()).stream() + .map(valueType::cast) + .collect(Collectors.toList()), + param.getDisplay(), param.getDescription()); + } + return ParameterDescription.create(valueType, param.getName(), param.getRequired(), + valueType.cast(getValue(model, null, param.getDefaultValue())), param.getDisplay(), + param.getDescription()); + } + + public static ParameterDescription getParameterDescription(DebuggerObjectModel model, + Gadp.Parameter param) { + return getParameterDescription(getValueType(param.getType()), model, param); + + } + + public static Map> getParameters(DebuggerObjectModel model, + Gadp.ParameterList params) { + return TargetMethod.makeParameters( + params.getParameterList().stream().map(p -> getParameterDescription(model, p))); + } + + public static Gadp.ValueType makeValueType(Class type) { + if (type == Void.class) { + return Gadp.ValueType.VT_VOID; + } + if (type == Boolean.class) { + return Gadp.ValueType.VT_BOOL; + } + if (type == Integer.class) { + return Gadp.ValueType.VT_INT; + } + if (type == Long.class) { + return Gadp.ValueType.VT_LONG; + } + if (type == Float.class) { + return Gadp.ValueType.VT_FLOAT; + } + if (type == Double.class) { + return Gadp.ValueType.VT_DOUBLE; + } + if (type == byte[].class) { + return Gadp.ValueType.VT_BYTES; + } + if (type == String.class) { + return Gadp.ValueType.VT_STRING; + } + if (type == TargetStringList.class) { + return Gadp.ValueType.VT_STRING_LIST; + } + if (type == Address.class) { + return Gadp.ValueType.VT_ADDRESS; + } + if (type == AddressRange.class) { + return Gadp.ValueType.VT_RANGE; + } + if (type == TargetBreakpointKindSet.class) { + return Gadp.ValueType.VT_BREAK_KIND_SET; + } + if (type == TargetExecutionState.class) { + return Gadp.ValueType.VT_EXECUTION_STATE; + } + if (type == TargetStepKindSet.class) { + return Gadp.ValueType.VT_STEP_KIND_SET; + } + if (type == TargetPrimitiveDataType.class) { + return Gadp.ValueType.VT_PRIMITIVE_KIND; + } + if (type == TargetDataType.class) { + return Gadp.ValueType.VT_DATA_TYPE; + } + if (type == TargetUpdateMode.class) { + return Gadp.ValueType.VT_UPDATE_MODE; + } + if (type == TargetObjectRef.class) { + return Gadp.ValueType.VT_PATH; + } + if (type == TargetObjectRefList.class) { + return Gadp.ValueType.VT_PATH_LIST; + } + if (type == Class.class) { + return Gadp.ValueType.VT_TYPE; + } + throw new IllegalArgumentException("Cannot encode type: " + type); + } + + public static Gadp.Parameter makeParameter(ParameterDescription desc) { + return Gadp.Parameter.newBuilder() + .setType(makeValueType(desc.type)) + .setName(desc.name) + .setDefaultValue(makeValue(null, desc.defaultValue)) + .setRequired(desc.required) + .setDisplay(desc.display) + .setDescription(desc.description) + .addAllChoices(makeValues(desc.choices)) + .build(); + } + + public static Gadp.ParameterList makeParameterList(TargetParameterMap map) { + return Gadp.ParameterList.newBuilder() + .addAllParameter( + map.values().stream().map(d -> makeParameter(d)).collect(Collectors.toList())) + .build(); + } + + public static TargetObject getTargetObjectNonLink(List path, Object value) { + if (!(value instanceof TargetObject)) { + return null; + } + TargetObject obj = (TargetObject) value; + if (!Objects.equals(obj.getPath(), path)) { + return null; + } + return obj; + } + + public static Gadp.Value makeValue(List path, Object value) { + Gadp.Value.Builder b = Gadp.Value.newBuilder(); + if (value instanceof Boolean) { + b.setBoolValue((Boolean) value); + } + else if (value instanceof Integer) { + b.setIntValue((Integer) value); + } + else if (value instanceof Long) { + b.setLongValue((Long) value); + } + else if (value instanceof Float) { + b.setFloatValue((Float) value); + } + else if (value instanceof Double) { + b.setDoubleValue((Double) value); + } + else if (value instanceof byte[]) { + b.setBytesValue(ByteString.copyFrom((byte[]) value)); + } + else if (value instanceof String) { + b.setStringValue((String) value); + } + else if (value instanceof TargetStringList) { + b.setStringListValue(makeStringList((TargetStringList) value)); + } + else if (value instanceof Address) { + b.setAddressValue(makeAddress((Address) value)); + } + else if (value instanceof AddressRange) { + b.setRangeValue(makeRange((AddressRange) value)); + } + else if (value instanceof TargetBreakpointKindSet) { + b.setBreakKindsValue(makeBreakKindSet((TargetBreakpointKindSet) value)); + } + else if (value instanceof TargetExecutionState) { + b.setExecStateValue(makeExecutionState((TargetExecutionState) value)); + } + else if (value instanceof TargetStepKindSet) { + b.setStepKindsValue(makeStepKindSet((TargetStepKindSet) value)); + } + // TODO: TargetPrimitiveDataType? + // TODO: TargetDataType? + else if (value instanceof TargetUpdateMode) { + b.setUpdateModeValue(makeUpdateMode((TargetUpdateMode) value)); + } + else if (value instanceof TargetParameterMap) { + b.setParametersValue(makeParameterList((TargetParameterMap) value)); + } + else if (path != null && getTargetObjectNonLink(path, value) != null) { + // This case MUST precede TargetObjectRef + b.setObjectStub(Gadp.ModelObjectStub.getDefaultInstance()); + // NOTE: Never produce info. That is a special case for object retrieval. + } + else if (value instanceof TargetObjectRef) { + b.setPathValue(makePath((TargetObjectRef) value)); + } + else if (value instanceof TargetObjectRefList) { + b.setPathListValue(makePathList((TargetObjectRefList) value)); + } + else if (value instanceof Class) { + b.setTypeValue(makeValueType((Class) value)); + } + else { + throw new IllegalArgumentException( + "Cannot encode value: " + value + " (of type " + value.getClass() + ")"); + } + return b.build(); + } + + public static Gadp.Argument makeArgument(Map.Entry argument) { + return Gadp.Argument.newBuilder() + .setName(argument.getKey()) + .setValue(makeValue(null, argument.getValue())) + .build(); + } + + public static Gadp.Attribute makeAttribute(TargetObject parent, Map.Entry ent) { + return Gadp.Attribute.newBuilder() + .setName(ent.getKey()) + .setValue( + makeValue(PathUtils.extend(parent.getPath(), ent.getKey()), ent.getValue())) + .build(); + } + + public static Address getAddress(DebuggerObjectModel model, Gadp.Address addr) { + return model.getAddress(addr.getSpace(), addr.getOffset()); + } + + public static AddressRange getAddressRange(DebuggerObjectModel model, Gadp.AddressRange range) { + Address min = model.getAddress(range.getSpace(), range.getOffset()); + return new AddressRangeImpl(min, min.add(Integer.toUnsignedLong(range.getExtend()))); + } + + public static Object getValue(DebuggerObjectModel model, List path, Gadp.Value value) { + switch (value.getSpecCase()) { + case BOOL_VALUE: + return value.getBoolValue(); + case INT_VALUE: + return value.getIntValue(); + case LONG_VALUE: + return value.getLongValue(); + case FLOAT_VALUE: + return value.getFloatValue(); + case DOUBLE_VALUE: + return value.getDoubleValue(); + case BYTES_VALUE: + return value.getBytesValue().toByteArray(); + case STRING_VALUE: + return value.getStringValue(); + case STRING_LIST_VALUE: + return getStringList(value.getStringListValue()); + case ADDRESS_VALUE: + return getAddress(model, value.getAddressValue()); + case RANGE_VALUE: + return getAddressRange(model, value.getRangeValue()); + case BREAK_KINDS_VALUE: + return getBreakKindSet(value.getBreakKindsValue()); + case EXEC_STATE_VALUE: + return getExecutionState(value.getExecStateValue()); + case STEP_KINDS_VALUE: + return getStepKindSet(value.getStepKindsValue()); + case PRIMITIVE_KIND_VALUE: + return TODO("Marhsalling types over GADP", value.getPrimitiveKindValue()); + case DATA_TYPE_VALUE: + return TODO("Marshalling types over GADP", value.getDataTypeValue()); + case UPDATE_MODE_VALUE: + return getUpdateMode(value.getUpdateModeValue()); + case PARAMETERS_VALUE: + return getParameters(model, value.getParametersValue()); + case PATH_VALUE: + return model.createRef(value.getPathValue().getEList()); + case PATH_LIST_VALUE: + return getRefList(model, value.getPathListValue()); + case OBJECT_INFO: + Msg.error(GadpValueUtils.class, "ObjectInfo requires special treatment:" + value); + return model.createRef(path); + case OBJECT_STUB: + return model.createRef(path); + case TYPE_VALUE: + return getValueType(value.getTypeValue()); + case SPEC_NOT_SET: + return null; + default: + Msg.error(GadpValueUtils.class, "Got unknown value type: " + value); + return null; + } + } + + public static Object getAttributeValue(TargetObject object, Gadp.Attribute attr) { + return getValue(object.getModel(), PathUtils.extend(object.getPath(), attr.getName()), + attr.getValue()); + } + + public static Map getAttributeMap(TargetObject object, + List list) { + Map result = new LinkedHashMap<>(); + for (Gadp.Attribute attr : list) { + if (result.put(attr.getName(), + GadpValueUtils.getAttributeValue(object, attr)) != null) { + Msg.warn(GadpValueUtils.class, "Received duplicate attribute: " + attr); + } + } + return result; + } + + public static List getValues(DebuggerObjectModel model, List values) { + // Use null for path, since the list cannot refer to itself as an object + return values.stream().map(v -> getValue(model, null, v)).collect(Collectors.toList()); + } + + public static List makeValues(Collection values) { + return values.stream().map(v -> makeValue(null, v)).collect(Collectors.toList()); + } + + public static List makeArguments(Map arguments) { + return arguments.entrySet() + .stream() + .map(ent -> makeArgument(ent)) + .collect(Collectors.toList()); + } + + public static Map getElementMap(TargetObject parent, + List indices) { + Map result = new LinkedHashMap<>(); + for (String index : indices) { + result.put(index, + parent.getModel().createRef(PathUtils.index(parent.getPath(), index))); + } + return result; + } + + public static Map getArguments(DebuggerObjectModel model, + List arguments) { + return arguments.stream() + .collect( + Collectors.toMap(a -> a.getName(), a -> getValue(model, null, a.getValue()))); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/ProtobufOneofByTypeHelper.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/ProtobufOneofByTypeHelper.java new file mode 100644 index 0000000000..bb7457608a --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/util/ProtobufOneofByTypeHelper.java @@ -0,0 +1,89 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.util; + +import java.util.*; + +import com.google.protobuf.*; +import com.google.protobuf.Descriptors.*; + +public class ProtobufOneofByTypeHelper { + public static OneofDescriptor findOneofByName(Descriptor descriptor, String name) { + for (OneofDescriptor desc : descriptor.getOneofs()) { + if (name.equals(desc.getName())) { + return desc; + } + } + return null; + } + + public static ProtobufOneofByTypeHelper create( + M exampleMessage, B exampleBuilder, String oneofName) { + Descriptor typeDesc = exampleMessage.getDescriptorForType(); + if (exampleBuilder.getDescriptorForType() != typeDesc) { + throw new IllegalArgumentException( + "Example message and builder must have the same message type"); + } + OneofDescriptor oneofDesc = findOneofByName(typeDesc, oneofName); + if (oneofDesc == null) { + throw new NoSuchElementException( + "oneof " + oneofName + " in " + typeDesc); + } + return new ProtobufOneofByTypeHelper<>(oneofDesc); + } + + private final OneofDescriptor descriptor; + private final Map fieldsByType; + + private ProtobufOneofByTypeHelper(OneofDescriptor descriptor) { + this.descriptor = descriptor; + Map descsByType = new HashMap<>(); + for (FieldDescriptor desc : descriptor.getFields()) { + Descriptor type = desc.getMessageType(); + if (type == null || descsByType.put(type, desc) != null) { + throw new IllegalArgumentException(this.getClass() + + " requires each fields in " + descriptor + " to be a unique message type"); + } + } + this.fieldsByType = Map.copyOf(descsByType); + } + + public FieldDescriptor getFieldForTypeOf(MessageOrBuilder value) { + FieldDescriptor field = fieldsByType.get(value.getDescriptorForType()); + if (field == null) { + throw new IllegalArgumentException( + "No field in " + descriptor + " has type of " + value); + } + return field; + } + + public void set(B builder, Message value) { + builder.setField(getFieldForTypeOf(value), value); + } + + public void set(B builder, Message.Builder value) { + builder.setField(getFieldForTypeOf(value), value.build()); + } + + @SuppressWarnings("unchecked") + public MI expect(M outer, MI exampleInner) { + FieldDescriptor field = outer.getOneofFieldDescriptor(descriptor); + if (field == null || field.getMessageType() != exampleInner.getDescriptorForType()) { + return null; + } + return (MI) outer.getField(field); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/main/proto/gadp.proto b/Ghidra/Debug/Debugger-gadp/src/main/proto/gadp.proto new file mode 100644 index 0000000000..5ace2d879b --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/main/proto/gadp.proto @@ -0,0 +1,595 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; +package ghidra.dbg.gadp.protocol; + +enum ErrorCode { + UNKNOWN = 0; + BAD_REQUEST = 1; + NO_VERSION = 2; + NO_OBJECT = 3; + NO_INTERFACE = 4; + BAD_ARGUMENT = 5; + BAD_ADDRESS = 6; + NOT_SUPPORTED = 7; + MEMORY_ACCESS = 8; + REGISTER_ACCESS = 9; + USER_ERROR = 10; + MODEL_ACCESS = 11; +} + +// For testing: cause the server to report an unrecognized request error +message ErrorRequest { +} + +message ErrorReply { + ErrorCode code = 1; + string message = 2; +} + +message ConnectRequest { + repeated string version = 1; +} + +message ConnectReply { + string version = 1; +} + +message PingRequest { + string content = 1; +} + +message PingReply { + string content = 1; +} + +message Address { + string space = 1; + uint64 offset = 2; +} + +message AddressRange { + string space = 1; + uint64 offset = 2; + uint32 extend = 3; +} + +message Path { + repeated string e = 1; +} + +message PathList { + repeated Path path = 1; +} + +enum BreakKind { + READ = 0; + WRITE = 1; + EXECUTE = 2; + SOFTWARE = 3; +} + +message BreakKindsSet { + repeated BreakKind k = 1; +} + +enum StepKind { + INTO = 0; + ADVANCE = 1; + FINISH = 2; + LINE = 3; + OVER = 4; + OVER_LINE = 5; + SKIP = 6; + RETURN = 7; + UNTIL = 8; +} + +message StepKindsSet { + repeated StepKind k = 1; +} + +message StringList { + repeated string s = 1; +} + +enum ExecutionState { + INACTIVE = 0; + ALIVE = 1; + STOPPED = 2; + RUNNING = 3; + TERMINATED = 4; +} + +enum PrimitiveKind { + UNDEFINED = 0; + VOID = 1; + UINT = 2; + SINT = 3; + FLOAT = 4; + COMPLEX = 5; +} + +message DataType { + message Array { + DataType element_type = 1; + uint32 element_count = 2; + } + + message Bitfield { + DataType field_type = 1; + uint32 start_bit = 2; + uint32 length = 3; + } + + message Pointer { + DataType referent_type = 1; + } + + message Primitive { + PrimitiveKind kind = 1; + uint32 length = 2; + } + + oneof dt { + Array array_type = 1; + Bitfield bitfield_type = 2; + Path named_type = 3; + Pointer pointer_type = 4; + Primitive primitive_type = 5; + } + bool const = 6; +} + +enum UpdateMode { + UNSOLICITED = 0; + SOLICITED = 1; + FIXED = 2; +} + +enum ValueType { + VT_VOID = 0; + VT_BOOL = 1; + VT_INT = 2; + VT_LONG = 3; + VT_FLOAT = 4; + VT_DOUBLE = 5; + VT_BYTES = 6; + VT_STRING = 7; + VT_STRING_LIST = 8; + VT_ADDRESS = 9; + VT_RANGE = 10; + VT_BREAK_KIND_SET = 11; + VT_EXECUTION_STATE = 12; + VT_STEP_KIND_SET = 13; + VT_PRIMITIVE_KIND = 14; + VT_DATA_TYPE = 15; + VT_UPDATE_MODE = 16; + VT_PATH = 17; + VT_PATH_LIST = 18; + VT_TYPE = 19; +} + +message Parameter { + ValueType type = 1; + string name = 2; + Value defaultValue = 3; + bool required = 4; + string display = 5; + string description = 6; + repeated Value choices = 7; +} + +message ParameterList { + repeated Parameter parameter = 1; +} + +message Argument { + string name = 1; + Value value = 2; +} + +message Value { + oneof spec { + bool bool_value = 1; + int32 int_value = 2; + int64 long_value = 3; + float float_value = 4; + double double_value = 5; + bytes bytes_value = 6; + string string_value = 7; + StringList string_list_value = 8; + Address address_value = 9; + AddressRange range_value = 10; + BreakKindsSet break_kinds_value = 11; + ExecutionState exec_state_value = 12; + StepKindsSet step_kinds_value = 13; + PrimitiveKind primitive_kind_value = 14; + DataType data_type_value = 15; + UpdateMode update_mode_value = 16; + Path path_value = 17; + PathList path_list_value = 18; + ModelObjectInfo object_info = 19; + ModelObjectStub object_stub = 20; + ParameterList parameters_value = 21; + ValueType type_value = 22; + } +} + +message Attribute { + string name = 1; + Value value = 2; +} + +message ModelObjectStub { +} + +message ModelObjectInfo { + Path path = 1; + string type_hint = 2; + repeated string interface = 3; + repeated string element_index = 4; + repeated Attribute attribute = 5; +} + +message ModelObjectDelta { + repeated string index_removed = 2; + repeated string index_added = 3; + // TODO: indices_moved? + repeated string attribute_removed = 4; + repeated Attribute attribute_added = 5; +} + +message ModelObjectEvent { + ModelObjectDelta delta = 1; +} + +/** + * This message both retrieves the requested object(s) and (un)subscribes to further changes + * + * If subscribe is true, then future changes to element indices and attribute names are sent to the + * client. If fetch is true, then the objects current indices and names are included in the + * response. If refresh is true, then the server-side model is asked to refresh the object's + * indices and names while servicing the request. + */ +message SubscribeRequest { + Path path = 1; + bool subscribe = 2; + bool refresh = 3; + bool fetchElements = 4; + bool fetchAttributes = 5; + bool refreshElements = 6; + bool refreshAttributes = 7; +} + +message SubscribeReply { + Value value = 1; +} + +message ObjectInvalidateEvent { + string reason = 1; +} + +message LaunchRequest { + Path path = 1; + repeated Argument argument = 2; +} + +message LaunchReply { +} + +message AttachRequest { + Path path = 1; + oneof spec { + Path target = 2; + sint64 pid = 3; + } +} + +message AttachReply { +} + +message KillRequest { + Path path = 1; +} + +message KillReply { +} + +message DetachRequest { + Path path = 1; +} + +message DetachReply { +} + +message ResumeRequest { + Path path = 1; +} + +message ResumeReply { +} + +message BreakCreateRequest { + Path path = 1; + oneof spec { + string expression = 2; + AddressRange address = 3; + } + BreakKindsSet kinds = 4; +} + +message BreakCreateReply { +} + +message BreakToggleRequest { + Path path = 1; + bool enabled = 2; +} + +message BreakToggleReply { +} + +message BreakHitEvent { + Path trapped = 1; + Path frame = 2; + Path spec = 3; + Path effective = 4; +} + +message DeleteRequest { + Path path = 1; +} + +message DeleteReply { +} + +message ExecuteRequest { + Path path = 1; + string command = 2; + bool capture = 3; +} + +message ConsoleWriteRequest { + Path path = 1; + bytes data = 2; +} + +message ConsoleOutputEvent { + int32 channel = 1; + bytes data = 2; +} + +message ExecuteReply { + string captured = 1; +} + +message ConsoleWriteReply { +} + +message InterruptRequest { + Path path = 1; +} + +message InterruptReply { +} + +message MemoryReadRequest { + Path path = 1; + AddressRange range = 2; +} + +message MemoryReadReply { + bytes content = 1; +} + +message MemoryWriteRequest { + Path path = 1; + Address start = 2; + bytes content = 3; +} + +message MemoryWriteReply { + // TODO: Should this include amount written? +} + +// Sent one something/someone else reads/writes memory +message MemoryUpdateEvent { + Address address = 1; + bytes content = 2; +} + +// Sent one something/someone else causes a read error +message MemoryErrorEvent { + AddressRange range = 1; + string message = 2; +} + +message RegisterValue { + string name = 1; + bytes content = 2; +} + +message RegisterReadRequest { + Path path = 1; + repeated string name = 2; +} + +message RegisterReadReply { + repeated RegisterValue value = 1; +} + +message RegisterWriteRequest { + Path path = 1; + repeated RegisterValue value = 2; +} + +message RegisterWriteReply { +} + +message RegisterUpdateEvent { + repeated RegisterValue value = 1; +} + +message StepRequest { + Path path = 1; + StepKind kind = 2; +} + +message StepReply { +} + +message CacheInvalidateRequest { + Path path = 1; +} + +message CacheInvalidateEvent { +} + +message CacheInvalidateReply { +} + +message FocusRequest { + Path path = 1; + Path focus = 2; +} + +message FocusReply { +} + +message InvokeRequest { + Path path = 1; + repeated Argument argument = 2; +} + +message InvokeReply { + Value result = 1; +} + +enum TargetEventType { + EV_STOPPED = 0; // Stupid name conflict + EV_RUNNING = 1; + PROCESS_CREATED = 2; + PROCESS_EXITED = 3; + THREAD_CREATED = 4; + THREAD_EXITED = 5; + MODULE_LOADED = 6; + MODULE_UNLOADED = 7; + BREAKPOINT_HIT = 8; + STEP_COMPLETED = 9; + EXCEPTION = 10; + SIGNAL = 11; +} + +message TargetEvent { + Path eventThread = 1; + TargetEventType type = 2; + string description = 3; + repeated Value parameters = 4; +} + +message EventNotification { + Path path = 1; + oneof evt { + ModelObjectEvent model_object_event = 304; + + BreakHitEvent break_hit_event = 314; + CacheInvalidateEvent cache_invalidate_event = 320; + ConsoleOutputEvent console_output_event = 312; + MemoryUpdateEvent memory_update_event = 317; + MemoryErrorEvent memory_error_event = 417; + ObjectInvalidateEvent object_invalidate_event = 323; + RegisterUpdateEvent register_update_event = 322; + TargetEvent target_event = 330; + } +} + +message RootMessage { + int32 sequence = 1; + + oneof msg { + EventNotification event_notification = 2; + + ErrorRequest error_request = 100; + ErrorReply error_reply = 200; + + ConnectRequest connect_request = 101; + ConnectReply connect_reply = 201; + + PingRequest ping_request = 102; + PingReply ping_reply = 202; + + AttachRequest attach_request = 108; + AttachReply attach_reply = 208; + + BreakCreateRequest break_create_request = 114; + BreakCreateReply break_create_reply = 214; + + BreakToggleRequest break_toggle_request = 115; + BreakToggleReply break_toggle_reply = 215; + + CacheInvalidateRequest cache_invalidate_request = 120; + CacheInvalidateReply cache_invalidate_reply = 220; + + DeleteRequest delete_request = 116; + DeleteReply delete_reply = 216; + + DetachRequest detach_request = 110; + DetachReply detach_reply = 210; + + ExecuteRequest execute_request = 112; + ExecuteReply execute_reply = 212; + + ConsoleWriteRequest console_write_request = 113; + ConsoleWriteReply console_write_reply = 213; + + FocusRequest focus_request = 123; + FocusReply focus_reply = 223; + + InterruptRequest interrupt_request = 124; + InterruptReply interrupt_reply = 224; + + KillRequest kill_request = 109; + KillReply kill_reply = 209; + + LaunchRequest launch_request = 107; + LaunchReply launch_reply = 207; + + MemoryReadRequest memory_read_request = 117; + MemoryReadReply memory_read_reply = 217; + + MemoryWriteRequest memory_write_request = 118; + MemoryWriteReply memory_write_reply = 218; + + RegisterReadRequest register_read_request = 121; + RegisterReadReply register_read_reply = 221; + + RegisterWriteRequest register_write_request = 122; + RegisterWriteReply register_write_reply = 222; + + ResumeRequest resume_request = 111; + ResumeReply resume_reply = 211; + + StepRequest step_request = 119; + StepReply step_reply = 219; + + SubscribeRequest subscribe_request = 104; + SubscribeReply subscribe_reply = 204; + + InvokeRequest invoke_request = 105; + InvokeReply invoke_reply = 205; + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/GadpClientServerTest.java b/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/GadpClientServerTest.java new file mode 100644 index 0000000000..90ea59340f --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/GadpClientServerTest.java @@ -0,0 +1,1115 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp; + +import static ghidra.lifecycle.Unfinished.TODO; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.junit.Ignore; +import org.junit.Test; + +import com.google.protobuf.GeneratedMessageV3; + +import generic.ID; +import generic.Unique; +import ghidra.async.*; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.agent.*; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.attributes.TargetStringList; +import ghidra.dbg.gadp.client.GadpClient; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.server.AbstractGadpServer; +import ghidra.dbg.gadp.util.AsyncProtobufMessageChannel; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetFocusScope.TargetFocusScopeListener; +import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; +import ghidra.dbg.target.TargetObject.TargetObjectFetchingListener; +import ghidra.dbg.target.TargetObject.TargetObjectListener; +import ghidra.dbg.util.*; +import ghidra.dbg.util.AttributesChangedListener.AttributesChangedInvocation; +import ghidra.dbg.util.ElementsChangedListener.ElementsChangedInvocation; +import ghidra.program.model.address.*; +import ghidra.util.Msg; +import ghidra.util.SystemUtilities; + +public class GadpClientServerTest { + public static final long TIMEOUT_MILLISECONDS = + SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE; + + protected interface DecoratedAsyncByteChannel extends AsynchronousByteChannel { + AsynchronousByteChannel getDelegate(); + + @Override + default void close() throws IOException { + getDelegate().close(); + } + + @Override + default boolean isOpen() { + return getDelegate().isOpen(); + } + + @Override + default void read(ByteBuffer dst, A attachment, + CompletionHandler handler) { + getDelegate().read(dst, attachment, handler); + } + + @Override + default Future read(ByteBuffer dst) { + return getDelegate().read(dst); + } + + @Override + default void write(ByteBuffer src, A attachment, + CompletionHandler handler) { + getDelegate().write(src, attachment, handler); + } + + @Override + default Future write(ByteBuffer src) { + return getDelegate().write(src); + } + } + + protected static class MonitoredAsyncByteChannel implements DecoratedAsyncByteChannel { + private AsynchronousByteChannel delegate; + + private int writeCount = 0; + + public MonitoredAsyncByteChannel(AsynchronousByteChannel delegate) { + this.delegate = delegate; + } + + @Override + public AsynchronousByteChannel getDelegate() { + return delegate; + } + + @Override + public void write(ByteBuffer src, A attachment, + CompletionHandler handler) { + writeCount++; + DecoratedAsyncByteChannel.super.write(src, attachment, handler); + } + + @Override + public Future write(ByteBuffer src) { + writeCount++; + return DecoratedAsyncByteChannel.super.write(src); + } + + private void reset() { + writeCount = 0; + } + } + + public static class MonitoredAsyncProtobufMessageChannel + extends AsyncProtobufMessageChannel { + interface MessageRecord { + S assertSent(); + + R assertReceived(); + } + + public static class MessageSent implements MessageRecord { + public final S message; + + public MessageSent(S message) { + this.message = message; + } + + @Override + public S assertSent() { + return message; + } + + @Override + public R assertReceived() { + fail(); + return null; + } + } + + public static class MessageReceived implements MessageRecord { + public final R message; + + public MessageReceived(R message) { + this.message = message; + } + + @Override + public S assertSent() { + fail(); + return null; + } + + @Override + public R assertReceived() { + return message; + } + } + + List> record = new ArrayList<>(); + AsyncReference count = new AsyncReference<>(); + + public MonitoredAsyncProtobufMessageChannel(AsynchronousByteChannel channel) { + super(channel); + } + + @Override + public CompletableFuture read(IOFunction receiver) { + return super.read(receiver).thenApply(msg -> { + synchronized (this) { + record.add(new MessageReceived<>(msg)); + count.set(record.size(), null); + } + return msg; + }); + } + + @Override + public CompletableFuture write(S msg) { + return super.write(msg).thenApply(s -> { + synchronized (this) { + record.add(new MessageSent<>(msg)); + count.set(record.size(), null); + } + return s; + }); + } + + public synchronized void clear() { + record.clear(); + count.set(0, null); + } + } + + public static class MonitoredGadpClient extends GadpClient { + public MonitoredGadpClient(String description, AsynchronousByteChannel channel) { + super(description, channel); + } + + @Override + protected AsyncProtobufMessageChannel createMessageChannel( + AsynchronousByteChannel channel) { + return new MonitoredAsyncProtobufMessageChannel<>(channel); + } + + MonitoredAsyncProtobufMessageChannel getMessageChannel() { + return (MonitoredAsyncProtobufMessageChannel) messageChannel; + } + } + + protected static T waitOn(CompletableFuture future) throws Throwable { + try { + return future.get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); + } + catch (Exception e) { + throw AsyncUtils.unwrapThrowable(e); + } + } + + public class TestGadpTargetSession extends DefaultTargetModelRoot + implements TargetFocusScope { + protected final TestGadpTargetAvailableContainer available = + new TestGadpTargetAvailableContainer(this); + protected final TestGadpTargetProcessContainer processes = + new TestGadpTargetProcessContainer(this); + protected final TestGadpTargetAvailableLinkContainer links = + new TestGadpTargetAvailableLinkContainer(this); + + public TestGadpTargetSession(FakeDebuggerObjectModel model) { + super(model, "Session"); + + changeAttributes(List.of(), List.of(available, processes), Map.of(), "Initialized"); + } + + public void addLinks() { + assertNotNull(available.fetchElements().getNow(null)); + links.setElements(List.of(), Map.of( + "1", available.getCachedElements().get("2"), + "2", available.getCachedElements().get("1")), + "Initialized"); + changeAttributes(List.of(), List.of(links), Map.of(), "Adding links"); + } + + public void setFocus(TestGadpTargetProcess process) { + changeAttributes(List.of(), List.of(), Map.of( + FOCUS_ATTRIBUTE_NAME, process), + "New Process"); + listeners.fire(TargetFocusScopeListener.class).focusChanged(this, process); + } + + @Override + public CompletableFuture requestFocus(TargetObjectRef obj) { + throw new UnsupportedOperationException(); + } + } + + public class TestTargetObject + extends DefaultTargetObject { + + public TestTargetObject(DebuggerObjectModel model, P parent, String key, String typeHint) { + super(model, parent, key, typeHint); + } + + @Override + public CompletableFuture fetchAttribute(String name) { + if (!PathUtils.isInvocation(name)) { + return super.fetchAttribute(name); + } + Map.Entry invocation = PathUtils.parseInvocation(name); + TestTargetMethod method = + getTypedAttributeNowByName(invocation.getKey(), TestTargetMethod.class, null); + if (method == null) { + return AsyncUtils.nil(); + } + Object ret = method.testInvoke(invocation.getValue()); + changeAttributes(List.of(), Map.of(name, ret), "Invoked " + name); + return CompletableFuture.completedFuture(ret); + } + + @Override + protected CompletableFuture requestAttributes(boolean refresh) { + if (refresh) { + List toRemove = new ArrayList<>(); + for (String name : attributes.keySet()) { + if (PathUtils.isInvocation(name)) { + toRemove.add(name); + } + } + changeAttributes(toRemove, Map.of(), "Refreshed"); + } + return super.requestAttributes(refresh); + } + } + + private static final TargetParameterMap PARAMS = TargetParameterMap.copyOf(Map.of( + "whom", ParameterDescription.create(String.class, "whom", true, "World", + "Whom to greet", "The person or people to greet"), + "others", ParameterDescription.create(TargetStringList.class, "others", + false, TargetStringList.of(), "Others to greet", + "List of other people to greet individually"))); + + public class TestTargetMethod extends TestTargetObject + implements TargetMethod { + private Function method; + + public TestTargetMethod(TargetObject parent, String key, + Function method) { + super(parent.getModel(), parent, key, "Method"); + this.method = method; + + setAttributes(Map.of( + PARAMETERS_ATTRIBUTE_NAME, PARAMS, + RETURN_TYPE_ATTRIBUTE_NAME, Integer.class), + "Initialized"); + } + + public Object testInvoke(String paramsExpr) { + return method.apply(paramsExpr); + } + + @Override + public CompletableFuture invoke(Map arguments) { + TestMethodInvocation invocation = new TestMethodInvocation(arguments); + invocations.offer(invocation); + invocationCount.set(invocations.size(), null); + return invocation; + } + } + + public class TestGadpTargetProcessContainer + extends TestTargetObject + implements TargetLauncher { + public TestGadpTargetProcessContainer(TestGadpTargetSession session) { + super(session.getModel(), session, "Processes", "ProcessContainer"); + } + + @Override + public CompletableFuture launch(Map args) { + launches.offer(args); + TestGadpTargetProcess process = new TestGadpTargetProcess(this, 0); + changeElements(List.of(), List.of(process), Map.of(), "Launched"); + parent.setFocus(process); + return AsyncUtils.NIL; + } + } + + public class TestGadpTargetProcess + extends TestTargetObject { + public TestGadpTargetProcess(TestGadpTargetProcessContainer processes, int index) { + super(processes.getModel(), processes, + PathUtils.makeKey(PathUtils.makeIndex(index)), + "Process"); + } + } + + public class TestGadpTargetAvailableContainer + extends TestTargetObject { + public char punct = '!'; + + public TestGadpTargetAvailableContainer(TestGadpTargetSession session) { + super(session.getModel(), session, "Available", "AvailableContainer"); + + setAttributes(List.of( + new TestTargetMethod(this, "greet", p -> "Hello, " + p + punct)), + Map.of(), "Initialized"); + } + + @Override + public CompletableFuture requestElements( + boolean refresh) { + setElements(List.of( + new TestGadpTargetAvailable(this, 1, "echo"), + new TestGadpTargetAvailable(this, 2, "dd")), + Map.of(), "Refreshed"); + return super.requestElements(refresh); + } + } + + public class TestGadpTargetAvailable + extends TestTargetObject + implements TargetAttachable { + + public TestGadpTargetAvailable(TestGadpTargetAvailableContainer available, int pid, + String cmd) { + super(available.getModel(), available, PathUtils.makeKey(PathUtils.makeIndex(pid)), + "Available"); + setAttributes(List.of(), Map.of( + "pid", pid, + "cmd", cmd // + ), "Initialized"); + } + } + + /** + * This is a totally contrived concept, probably not found in any actual debugger. I just need + * something to have element links for testing. + */ + public class TestGadpTargetAvailableLinkContainer + extends TestTargetObject { + public TestGadpTargetAvailableLinkContainer(TestGadpTargetSession session) { + super(session.getModel(), session, "Links", "AvailableLinkContainer"); + } + } + + // TODO: Refactor with other Fake and Test. Probably put in Framework-Debugging.... + public class FakeDebuggerObjectModel extends AbstractDebuggerObjectModel { + private final TestGadpTargetSession session = new TestGadpTargetSession(this); + + private final AddressSpace ram = + new GenericAddressSpace("RAM", 64, AddressSpace.TYPE_RAM, 0); + private final AddressFactory factory = + new DefaultAddressFactory(new AddressSpace[] { ram }); + + @Override + public CompletableFuture fetchModelRoot() { + return CompletableFuture.completedFuture(session); + } + + @Override + public AddressFactory getAddressFactory() { + return factory; + } + + @Override + public CompletableFuture close() { + return AsyncUtils.NIL; + } + } + + public class TestGadpServer extends AbstractGadpServer { + @SuppressWarnings("hiding") + final FakeDebuggerObjectModel model; + + public TestGadpServer(SocketAddress addr) throws IOException { + super(new FakeDebuggerObjectModel(), addr); + this.model = (FakeDebuggerObjectModel) getModel(); + } + } + + public class ServerRunner implements AutoCloseable { + TestGadpServer server; + + public ServerRunner() throws IOException { + server = new TestGadpServer(new InetSocketAddress("localhost", 0)); + server.launchAsyncService(); + } + + @Override + public void close() throws Exception { + server.terminate(); + } + } + + class TestMethodInvocation extends CompletableFuture { + final Map args; + + public TestMethodInvocation(Map args) { + this.args = args; + } + } + + protected Deque> launches = new LinkedList<>(); + protected AsyncReference invocationCount = new AsyncReference<>(); + protected Deque invocations = new LinkedList<>(); + + @Test + public void testConnectDisconnect() throws Throwable { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + waitOn(client.close()); + } + finally { + socket.close(); + } + } + + @Test + public void testFetchModelValue() throws Throwable { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + Object rootVal = waitOn(client.fetchModelValue(List.of())); + TargetObject root = (TargetObject) rootVal; + assertEquals(List.of(), root.getPath()); + Object cmd = waitOn(client.fetchModelValue(PathUtils.parse("Available[1].cmd"))); + assertEquals("echo", cmd); + waitOn(client.close()); + } + finally { + socket.close(); + } + } + + @Test + public void testFetchModelValueCached() throws Throwable { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + MonitoredAsyncByteChannel monitored = new MonitoredAsyncByteChannel(socket); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", monitored); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + Object rootVal = waitOn(client.fetchModelValue(List.of())); + TargetObject root = (TargetObject) rootVal; + assertEquals(List.of(), root.getPath()); + // Do fetchAll to create objects and populate their caches + Map available = + waitOn(client.fetchObjectElements(PathUtils.parse("Available")) + .thenCompose(DebugModelConventions::fetchAll)); + assertEquals(2, available.size()); + monitored.reset(); + Object cmd = waitOn(client.fetchModelValue(PathUtils.parse("Available[1].cmd"))); + assertEquals("echo", cmd); + assertEquals(0, monitored.writeCount); + waitOn(client.close()); + } + finally { + socket.close(); + } + } + + @Test + public void testInvoke() throws Throwable { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + TargetObject procCont = + waitOn(client.fetchModelObject(PathUtils.parse("Available.greet"))); + assertTrue(procCont.getInterfaceNames().contains("Method")); + TargetMethod method = procCont.as(TargetMethod.tclass); + + assertEquals(PARAMS, method.getParameters()); + assertEquals(Integer.class, method.getReturnType()); + + CompletableFuture future = method.invoke(Map.of( + "whom", "GADP", + "others", TargetStringList.of("Alice", "Bob"))); + waitOn(invocationCount.waitValue(1)); + TestMethodInvocation invocation = invocations.poll(); + assertEquals(Map.of( + "whom", "GADP", + "others", TargetStringList.of("Alice", "Bob")), + invocation.args); + invocation.complete(42); + int result = (Integer) waitOn(future); + assertEquals(42, result); + waitOn(client.close()); + } + finally { + socket.close(); + } + } + + @Test + public void testInvokeMethodCached() throws Throwable { + // TODO: Disambiguate / deconflict these two "method" cases + try (AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + ServerRunner runner = new ServerRunner()) { + MonitoredAsyncByteChannel monitored = new MonitoredAsyncByteChannel(socket); + GadpClient client = new GadpClient("Test", monitored); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + + // Ensure the proxy's cache is in play + TargetObject avail = waitOn(client.fetchModelObject(PathUtils.parse("Available"))); + /** + * TODO: Need to think about what should happen if invocation is generated before + * attributes are fetched the first time. + */ + Map attrs = waitOn(avail.fetchAttributes()); + TargetObjectRef methodRef = (TargetObjectRef) attrs.get("greet"); + TargetMethod method = waitOn(methodRef.as(TargetMethod.tclass).fetch()); + assertNotNull(method); + assertEquals("Hello, World!", waitOn(avail.fetchAttribute("greet(World)"))); + runner.server.model.session.available.punct = '?'; + assertEquals("Hello, World!", waitOn(avail.fetchAttribute("greet(World)"))); + + // Flush the cache + waitOn(avail.fetchAttributes(true)); + assertEquals("Hello, World?", waitOn(avail.fetchAttribute("greet(World)"))); + + waitOn(client.close()); + } + } + + @Test + public void testListRoot() throws Throwable { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + Map elements = + waitOn(client.fetchObjectElements(List.of())); + assertEquals(0, elements.size()); + Map attributes = waitOn(client.fetchObjectAttributes(List.of())); + assertEquals(Set.of( + "Processes", "Available", + TargetObject.DISPLAY_ATTRIBUTE_NAME, + TargetObject.UPDATE_MODE_ATTRIBUTE_NAME), + attributes.keySet()); + Object procContAttr = attributes.get("Processes"); + TargetObjectRef procContRef = (TargetObjectRef) procContAttr; + assertEquals(List.of("Processes"), procContRef.getPath()); + waitOn(client.close()); + } + finally { + socket.close(); + } + } + + @Test + public void testLaunch() throws Throwable { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + TargetObject procCont = waitOn(client.fetchModelObject(List.of("Processes"))); + assertTrue(procCont.getInterfaceNames().contains("Launcher")); + TargetLauncher launcher = procCont.as(TargetLauncher.tclass); + waitOn(launcher.launch( + Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "/bin/echo Hello, World!"))); + waitOn(client.close()); + } + finally { + socket.close(); + } + + assertEquals(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "/bin/echo Hello, World!"), + launches.poll()); + assertNull(launches.poll()); + } + + @Test + public void testGetAvailableWithObjectGettingListener() throws Throwable { + List invocations = new ArrayList<>(); + // Any listener which calls .get on a child ref would do.... + // This object-getting listener is the pattern that revealed this problem, though. + TargetObjectListener listener = new TargetObjectFetchingListener() { + @Override + public void elementsChangedObjects(TargetObject parent, Collection removed, + Map added) { + invocations.add(new ElementsChangedInvocation(parent, removed, added)); + } + }; + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + TargetObject avail = waitOn(client.fetchModelObject(List.of("Available"))); + avail.addListener(listener); + Map elements = waitOn(avail.fetchElements()); + Msg.debug(this, "Elements: " + elements); + waitOn(client.close()); + } + finally { + socket.close(); + } + } + + @Test + public void testFocus() throws Throwable { + // Interesting because it involves a non-object-valued attribute (link) + // Need to check callback as well as getAttributes + + CompletableFuture> focusPath = new CompletableFuture<>(); + AtomicBoolean failed = new AtomicBoolean(); + TargetFocusScopeListener focusListener = new TargetFocusScopeListener() { + @Override + public void focusChanged(TargetFocusScope object, TargetObjectRef focused) { + Msg.info(this, "Focus changed to " + focused); + if (!focusPath.complete(focused.getPath())) { + failed.set(true); + } + } + }; + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + TargetObject session = waitOn(client.fetchModelObject(List.of())); + session.addListener(focusListener); + TargetObject procCont = waitOn(client.fetchModelObject(List.of("Processes"))); + assertTrue(procCont.getInterfaceNames().contains("Launcher")); + TargetLauncher launcher = procCont.as(TargetLauncher.tclass); + waitOn(launcher.launch( + Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "/bin/echo Hello, World!"))); + launches.clear(); // Don't care. Just free it up. + Map attrs = waitOn(client.fetchObjectAttributes(List.of())); + assertTrue(attrs.containsKey(TargetFocusScope.FOCUS_ATTRIBUTE_NAME)); + TargetObjectRef ref = + (TargetObjectRef) attrs.get(TargetFocusScope.FOCUS_ATTRIBUTE_NAME); + // NOTE: Could be actual object, but need not be + assertEquals(List.of("Processes", "[0]"), ref.getPath()); + waitOn(client.close()); + + assertFalse(failed.get()); + } + finally { + socket.close(); + } + assertEquals(List.of("Processes", "[0]"), focusPath.getNow(null)); + } + + @Test + public void testSubscribeNoSuchPath() throws Throwable { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + assertNull(waitOn(client.fetchModelObject(List.of("NotHere")))); + } + finally { + socket.close(); + } + } + + @Test + public void testSubscribeLaunchForChildrenChanged() throws Throwable { + ElementsChangedListener elemL = new ElementsChangedListener(); + + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + TargetObject procContainer = waitOn(client.fetchModelObject(List.of("Processes"))); + assertTrue(procContainer.getInterfaceNames().contains("Launcher")); + procContainer.addListener(elemL); + TargetLauncher launcher = procContainer.as(TargetLauncher.tclass); + waitOn(launcher.launch( + Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "/bin/echo Hello, World!"))); + waitOn(elemL.count.waitValue(1)); + + waitOn(client.close()); + + assertEquals(1, elemL.invocations.size()); + ElementsChangedInvocation eci = elemL.invocations.get(0); + assertEquals(procContainer, eci.parent); + assertEquals(List.of(), List.copyOf(eci.removed)); + assertEquals(1, eci.added.size()); + Entry ent = + eci.added.entrySet().iterator().next(); + assertEquals("0", ent.getKey()); + assertEquals(List.of("Processes", "[0]"), ent.getValue().getPath()); + } + finally { + socket.close(); + } + } + + @Test + public void testReplaceElement() throws Throwable { + ElementsChangedListener elemL = new ElementsChangedListener(); + InvalidatedListener invL = new InvalidatedListener(); + + try (AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + + TargetObject availCont = + waitOn(client.fetchModelObject(PathUtils.parse("Available"))); + availCont.addListener(elemL); + Map avail1 = waitOn(availCont.fetchElements() + .thenCompose(DebugModelConventions::fetchAll)); + assertEquals(2, avail1.size()); + for (TargetObject a : avail1.values()) { + assertTrue(a.isValid()); + a.addListener(invL); + } + + elemL.clear(); + TestGadpTargetAvailableContainer ssAvail = runner.server.model.session.available; + ssAvail.setElements(List.of( + new TestGadpTargetAvailable(ssAvail, 1, "cat") // + ), "Changed"); + + waitOn(invL.count.waitValue(2)); + waitOn(elemL.count.waitValue(1)); + + for (TargetObject a : avail1.values()) { + assertFalse(a.isValid()); + } + + assertEquals(1, availCont.getCachedElements().size()); + Map avail2 = waitOn(availCont.fetchElements() + .thenCompose(DebugModelConventions::fetchAll)); + assertEquals(1, avail2.size()); + assertEquals("cat", avail2.get("1").getCachedAttribute("cmd")); + + ElementsChangedInvocation changed = Unique.assertOne(elemL.invocations); + assertSame(availCont, changed.parent); + // Use equals here, since the listener only gets the ref + assertEquals(avail2.get("1"), Unique.assertOne(changed.added.values())); + + Map, String> actualInv = invL.invocations.stream() + .collect(Collectors.toMap(ii -> ID.of(ii.object), ii -> ii.reason)); + Map, String> expectedInv = + avail1.values() + .stream() + .collect(Collectors.toMap(o -> ID.of(o), o -> "Changed")); + assertEquals(expectedInv, actualInv); + } + } + + @Test + public void testReplaceAttribute() throws Throwable { + AttributesChangedListener attrL = new AttributesChangedListener(); + + try (AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + + TargetObject echoAvail = + waitOn(client.fetchModelObject(PathUtils.parse("Available[1]"))); + echoAvail.addListener(attrL); + assertEquals(Map.of( + "pid", 1, + "cmd", "echo" // + ), waitOn(echoAvail.fetchAttributes())); + + TestGadpTargetAvailable ssEchoAvail = + runner.server.model.session.available.getCachedElements().get("1"); + + ssEchoAvail.setAttributes(Map.of( + "cmd", "echo", + "args", "Hello, World!" // + ), "Changed"); + + waitOn(attrL.count.waitValue(1)); + + assertEquals(Map.of( + "cmd", "echo", + "args", "Hello, World!" // + ), echoAvail.getCachedAttributes()); + + AttributesChangedInvocation changed = Unique.assertOne(attrL.invocations); + assertSame(echoAvail, changed.parent); + assertEquals(Set.of("pid"), Set.copyOf(changed.removed)); + assertEquals(Map.of( + "args", "Hello, World!" // + ), changed.added); + } + } + + @Test + public void testSubtreeInvalidationDeduped() throws Throwable { + InvalidatedListener invL = new InvalidatedListener(); + + try (AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + ServerRunner runner = new ServerRunner()) { + MonitoredGadpClient client = new MonitoredGadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + + TargetObject availCont = + waitOn(client.fetchModelObject(PathUtils.parse("Available"))); + availCont.addListener(invL); + for (TargetObject avail : waitOn(availCont.fetchElements() + .thenCompose(DebugModelConventions::fetchAll)).values()) { + avail.addListener(invL); + } + + client.getMessageChannel().clear(); + runner.server.model.session.changeAttributes(List.of("Available"), Map.of(), "Clear"); + + /** + * Yes, should see all invalidations for the user side, but only one message should be + * sent, for the root of the invalidated sub tree + */ + waitOn(invL.count.waitValue(3)); + // NB. will not see attribute change, since not subscribed to root + waitOn(client.getMessageChannel().count.waitUntil(v -> v >= 1)); + + assertEquals(Gadp.RootMessage.newBuilder() + .setEventNotification(Gadp.EventNotification.newBuilder() + .setPath(Gadp.Path.newBuilder() + .addAllE(List.of("Available"))) + .setObjectInvalidateEvent(Gadp.ObjectInvalidateEvent.newBuilder() + .setReason("Clear"))) + .build(), + client.getMessageChannel().record.get(0).assertReceived()); + } + } + + @Test + public void testNoEventsAfterInvalidated() throws Throwable { + AttributesChangedListener attrL = new AttributesChangedListener(); + + try (AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + + TargetObject echoAvail = + waitOn(client.fetchModelObject(PathUtils.parse("Available[1]"))); + echoAvail.addListener(attrL); + assertEquals(Map.of( + "pid", 1, + "cmd", "echo" // + ), waitOn(echoAvail.fetchAttributes())); + + TargetObject ddAvail = waitOn(client.fetchModelObject(PathUtils.parse("Available[2]"))); + ddAvail.addListener(attrL); + assertEquals(Map.of( + "pid", 2, + "cmd", "dd" // + ), waitOn(ddAvail.fetchAttributes())); + + // NB: copy + Map ssAvail = + runner.server.model.session.available.getCachedElements(); + + runner.server.model.session.available.changeElements(List.of("1"), List.of(), Map.of(), + "1 is Gone"); + // Should produce nothing + (ssAvail.get("1")).setAttributes(List.of(), Map.of( + "cmd", "echo", + "args", "Hello, World!"), + "Changed"); + // Produce something, so we know we didn't get the other thing + (ssAvail.get("2")).changeAttributes(List.of(), List.of(), Map.of( + "args", "if=/dev/null"), + "Observe"); + + waitOn(attrL.count.waitValue(1)); + + AttributesChangedInvocation changed = Unique.assertOne(attrL.invocations); + assertSame(ddAvail, changed.parent); + assertEquals(Set.of(), Set.copyOf(changed.removed)); + assertEquals(Map.of( + "args", "if=/dev/null" // + ), changed.added); + } + } + + @Test + public void testProxyWithLinkedElementsCanonicalFirst() throws Throwable { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + runner.server.model.session.addLinks(); + TargetObject canonical = + waitOn(client.fetchModelObject(PathUtils.parse("Available[2]"))); + TargetObject link = + waitOn(client.fetchModelObject(PathUtils.parse("Links[1]"))); + assertSame(canonical, link); + assertEquals(PathUtils.parse("Available[2]"), link.getPath()); + waitOn(client.close()); + } + finally { + socket.close(); + } + } + + @Test + public void testProxyWithLinkedElementsLinkFirst() throws Throwable { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + runner.server.model.session.addLinks(); + TargetObjectRef linkRef = + (TargetObjectRef) waitOn(client.fetchModelValue(PathUtils.parse("Links[1]"))); + assertFalse(linkRef instanceof TargetObject); + TargetObject link = + waitOn(client.fetchModelObject(PathUtils.parse("Links[1]"))); + TargetObject canonical = + waitOn(client.fetchModelObject(PathUtils.parse("Available[2]"))); + assertSame(canonical, link); + assertEquals(PathUtils.parse("Available[2]"), link.getPath()); + waitOn(client.close()); + } + finally { + socket.close(); + } + } + + @Test + public void testFetchModelValueFollowsLink() throws Throwable { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + try (ServerRunner runner = new ServerRunner()) { + GadpClient client = new GadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + runner.server.model.session.addLinks(); + assertEquals("echo", waitOn(client.fetchModelValue(PathUtils.parse("Links[2].cmd")))); + waitOn(client.close()); + } + finally { + socket.close(); + } + } + + @Test + @Ignore("TODO") + public void testCachingWithLinks() throws Throwable { + + try (AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + ServerRunner runner = new ServerRunner()) { + MonitoredGadpClient client = new MonitoredGadpClient("Test", socket); + waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect, + runner.server.getLocalAddress())); + waitOn(client.connect()); + runner.server.model.session.addLinks(); + + client.getMessageChannel().clear(); + assertEquals("echo", waitOn(client.fetchModelValue(PathUtils.parse("Links[2].cmd")))); + assertEquals(2, client.getMessageChannel().record.size()); + assertEquals(Gadp.RootMessage.MsgCase.SUBSCRIBE_REQUEST, + client.getMessageChannel().record.get(0).assertSent().getMsgCase()); + assertEquals(Gadp.RootMessage.MsgCase.SUBSCRIBE_REPLY, + client.getMessageChannel().record.get(1).assertReceived().getMsgCase()); + + // Since I don't have the parent, as usual, no cache + client.getMessageChannel().clear(); + assertEquals("echo", waitOn(client.fetchModelValue(PathUtils.parse("Links[2].cmd")))); + assertEquals(2, client.getMessageChannel().record.size()); + + /** + * Now, fetch Links[2] first, and repeat the experiment It should still not use the + * cache, because we won't know if Links[2] still points to Available[1]. Technically, + * Available[1] is what will be cached. Index 2 of Links will not be cached until/if + * Links is fetched. + */ + client.getMessageChannel().clear(); + TargetObject avail1 = waitOn(client.fetchModelObject(PathUtils.parse("Links[2]"))); + // Odd, but I believe it's correct since the first reply comes back with a ref + assertEquals(4, client.getMessageChannel().record.size()); + assertNotNull(avail1); + client.getMessageChannel().clear(); + assertEquals("echo", waitOn(client.fetchModelValue(PathUtils.parse("Links[2].cmd")))); + assertEquals(2, client.getMessageChannel().record.size()); + client.getMessageChannel().clear(); + assertEquals("echo", waitOn(client.fetchModelValue(PathUtils.parse("Links[2].cmd")))); + assertEquals(2, client.getMessageChannel().record.size()); + + // Now, fetch Links, and its elements to ensure it is cached + TargetObject links = waitOn(client.fetchModelObject(PathUtils.parse("Links"))); + assertSame(avail1, waitOn(links.fetchElement("2"))); + + client.getMessageChannel().clear(); + assertSame(avail1, waitOn(links.fetchElement("2"))); + assertEquals(2, client.getMessageChannel().record.size()); + assertEquals("Links[2]", PathUtils.toString(client.getMessageChannel().record.get(0) + .assertSent() + .getSubscribeRequest() + .getPath() + .getEList())); + + TODO(); + waitOn(client.close()); + } + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/client/GadpClientTest.java b/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/client/GadpClientTest.java new file mode 100644 index 0000000000..b8db71a685 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/client/GadpClientTest.java @@ -0,0 +1,372 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import generic.Unique; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.comm.packet.err.PacketDecodeException; +import ghidra.comm.packet.err.PacketEncodeException; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.gadp.GadpVersion; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.util.AsyncProtobufMessageChannel; +import ghidra.dbg.gadp.util.GadpValueUtils; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.ElementsChangedListener; +import ghidra.dbg.util.ElementsChangedListener.ElementsChangedInvocation; +import ghidra.dbg.util.PathUtils; +import ghidra.util.Msg; +import ghidra.util.SystemUtilities; + +public class GadpClientTest { + private static final long TIMEOUT_MILLISECONDS = + SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE; + private static final String HELLO_WORLD = "Hello, World!"; + private static final String TEST_HOST = "localhost"; + + protected static T waitOn(CompletableFuture future) throws Throwable { + try { + return future.get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); + } + catch (Exception e) { + throw AsyncUtils.unwrapThrowable(e); + } + } + + interface TestClientHandler { + void handle(SocketChannel cli, GadpVersion version) throws Exception; + } + + class TestServer implements AutoCloseable { + private final ServerSocketChannel srv; + private final SocketAddress addr; + private SocketChannel cli; + private int seqno = 0; + + private final ByteBuffer inbuf = ByteBuffer.allocate(1024); + private final ByteBuffer outbuf = ByteBuffer.allocate(1024); + + public TestServer() throws IOException { + this.srv = ServerSocketChannel.open(); + this.srv.bind(new InetSocketAddress(TEST_HOST, 0), 1); + this.addr = this.srv.getLocalAddress(); + } + + @Override + public void close() throws Exception { + cli.configureBlocking(false); // Cannot wait forever + assertEquals(-1, cli.read(inbuf)); + cli.close(); + srv.close(); + } + + public void accept() throws IOException { + this.cli = srv.accept(); + } + + public void expect(Gadp.RootMessage msg) + throws IOException, PacketDecodeException, PacketEncodeException { + Msg.debug(this, "Expecting: " + msg); + + while (true) { + try { + //dumpBuffer(inbuf); + Gadp.RootMessage recv = + AsyncProtobufMessageChannel.unmarshall(Gadp.RootMessage::parseFrom, inbuf); + + Msg.trace(this, "Server Received: " + recv); + assertEquals(msg, recv); + return; + } + catch (BufferUnderflowException e) { + Msg.trace(this, "Waiting for data. Currently have " + inbuf.position()); + Thread.yield(); + if (Thread.interrupted()) { + return; + } + int len = cli.read(inbuf); + Msg.trace(this, "Read " + len + " bytes"); + if (len < 0) { + throw new AssertionError("Socket is closed"); + } + } + } + } + + public void send(Gadp.RootMessage msg) + throws PacketEncodeException, IOException { + Msg.debug(this, "Server Sending: " + msg); + outbuf.clear(); + AsyncProtobufMessageChannel.marshall(msg, outbuf); + outbuf.flip(); + while (outbuf.hasRemaining()) { + if (Thread.interrupted()) { + return; + } + cli.write(outbuf); + } + } + + public void handleConnect(GadpVersion version) + throws IOException, PacketDecodeException, PacketEncodeException { + expect(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setConnectRequest(GadpVersion.makeRequest()) + .build()); + send(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setConnectReply(Gadp.ConnectReply.newBuilder() + .setVersion(version.getName())) + .build()); + seqno++; + } + + public void handlePing() + throws IOException, PacketDecodeException, PacketEncodeException { + expect(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setPingRequest(Gadp.PingRequest.newBuilder() + .setContent(HELLO_WORLD)) + .build()); + send(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setPingReply(Gadp.PingReply.newBuilder() + .setContent(HELLO_WORLD)) + .build()); + seqno++; + } + + public void handleSubscribeValue(List path, Object value) + throws Exception { + expect(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setSubscribeRequest(Gadp.SubscribeRequest.newBuilder() + .setPath(GadpValueUtils.makePath(path)) + .setSubscribe(true)) + .build()); + send(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setSubscribeReply(Gadp.SubscribeReply.newBuilder() + .setValue(GadpValueUtils.makeValue(path, value))) + .build()); + seqno++; + } + + public void handleFetchObject(List path) throws Exception { + expect(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setSubscribeRequest(Gadp.SubscribeRequest.newBuilder() + .setPath(GadpValueUtils.makePath(path)) + .setSubscribe(true)) + .build()); + Gadp.SubscribeReply.Builder reply = Gadp.SubscribeReply.newBuilder() + .setValue(Gadp.Value.newBuilder() + .setObjectInfo(Gadp.ModelObjectInfo.newBuilder() + .setPath(GadpValueUtils.makePath(path)))); + send(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setSubscribeReply(reply) + .build()); + seqno++; + } + + public void handleFetchElements(List path, Collection indices) + throws Exception { + expect(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setSubscribeRequest(Gadp.SubscribeRequest.newBuilder() + .setPath(GadpValueUtils.makePath(path)) + .setSubscribe(true) + .setFetchElements(true)) + .build()); + Gadp.SubscribeReply.Builder reply = Gadp.SubscribeReply.newBuilder() + .setValue(Gadp.Value.newBuilder() + .setObjectInfo(Gadp.ModelObjectInfo.newBuilder() + .setPath(GadpValueUtils.makePath(path)) + .addAllElementIndex(indices))); + send(Gadp.RootMessage.newBuilder() + .setSequence(seqno) + .setSubscribeReply(reply) + .build()); + seqno++; + } + + public void sendModelEvent(List path, Collection indicesAdded) + throws Exception { + Gadp.ModelObjectEvent.Builder evt = Gadp.ModelObjectEvent.newBuilder(); + evt.setDelta(Gadp.ModelObjectDelta.newBuilder() + .addAllIndexAdded(indicesAdded)); + send(Gadp.RootMessage.newBuilder() + .setEventNotification(Gadp.EventNotification.newBuilder() + .setPath(GadpValueUtils.makePath(path)) + .setModelObjectEvent(evt)) + .build()); + } + } + + protected void dumpBuffer(ByteBuffer buf) { + for (byte b : buf.array()) { + System.err.print(String.format("%02x", b)); + } + System.err.println(); + for (int i = 0; i < buf.position(); i++) { + System.err.print(" "); + } + System.err.print("^ "); + if (!buf.hasRemaining()) { + System.err.println("(empty)"); + } + else { + for (int i = buf.position() + 2; i < buf.limit(); i++) { + System.err.print(" "); + } + System.err.println(" |"); + } + } + + protected void seqConnectDisconnect(SocketChannel cli, GadpVersion version) { + // Nothing to add + } + + @Test + public void testConnectDisconnect() throws Throwable { + try (TestServer srv = new TestServer(); + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open()) { + GadpClient client = new GadpClient("Test", socket); + + CompletableFuture sockConnect = + AsyncUtils.completable(TypeSpec.VOID, socket::connect, srv.addr); + srv.accept(); + waitOn(sockConnect); + + CompletableFuture gadpConnect = client.connect(); + srv.handleConnect(GadpVersion.VER1); + waitOn(gadpConnect); + + waitOn(client.close()); + } + } + + @Test + public void testPing() throws Throwable { + try (TestServer srv = new TestServer(); + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open()) { + GadpClient client = new GadpClient("Test", socket); + + CompletableFuture sockConnect = + AsyncUtils.completable(TypeSpec.VOID, socket::connect, srv.addr); + srv.accept(); + waitOn(sockConnect); + + CompletableFuture gadpConnect = client.connect(); + srv.handleConnect(GadpVersion.VER1); + waitOn(gadpConnect); + + CompletableFuture ping = client.ping(HELLO_WORLD); + srv.handlePing(); + waitOn(ping); + + waitOn(client.close()); + } + } + + @Test + public void testGetObjectRefInListener() throws Throwable { + ElementsChangedListener elemL = new ElementsChangedListener(); + + try (TestServer srv = new TestServer(); + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open()) { + GadpClient client = new GadpClient("Test", socket); + + CompletableFuture sockConnect = + AsyncUtils.completable(TypeSpec.VOID, socket::connect, srv.addr); + srv.accept(); + waitOn(sockConnect); + + CompletableFuture gadpConnect = client.connect(); + srv.handleConnect(GadpVersion.VER1); + waitOn(gadpConnect); + + CompletableFuture fetchParent = + client.fetchModelObject(PathUtils.parse("Parent")); + srv.handleFetchObject(PathUtils.parse("Parent")); + TargetObject parent = waitOn(fetchParent); + parent.addListener(elemL); + + CompletableFuture> fetchElements = + parent.fetchElements(); + srv.handleFetchElements(PathUtils.parse("Parent"), List.of()); + assertEquals(Map.of(), waitOn(fetchElements)); + + srv.sendModelEvent(PathUtils.parse("Parent"), List.of("0")); + waitOn(elemL.count.waitValue(1)); + ElementsChangedInvocation changed = Unique.assertOne(elemL.invocations); + assertEquals(parent, changed.parent); + TargetObjectRef childRef = Unique.assertOne(changed.added.values()); + assertEquals(PathUtils.parse("Parent[0]"), childRef.getPath()); + + // Not cached, since fetchElements just lists refs + CompletableFuture fetchChild = childRef.fetch(); + srv.handleFetchObject(PathUtils.parse("Parent[0]")); + TargetObject child = waitOn(fetchChild); + assertEquals(Map.of("0", child), parent.getCachedElements()); + } + assertEquals(1, elemL.count.get().intValue()); // After connection is closed + } + + @Test + public void testGetModelValueDeduped() throws Throwable { + try (TestServer srv = new TestServer(); + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open()) { + GadpClient client = new GadpClient("Test", socket); + + CompletableFuture sockConnect = + AsyncUtils.completable(TypeSpec.VOID, socket::connect, srv.addr); + srv.accept(); + waitOn(sockConnect); + + CompletableFuture gadpConnect = client.connect(); + srv.handleConnect(GadpVersion.VER1); + waitOn(gadpConnect); + + CompletableFuture fetchVal1 = client.fetchModelValue(PathUtils.parse("value")); + CompletableFuture fetchVal2 = client.fetchModelValue(PathUtils.parse("value")); + srv.handleSubscribeValue(PathUtils.parse("value"), HELLO_WORLD); + assertEquals(HELLO_WORLD, waitOn(fetchVal1)); + assertEquals(HELLO_WORLD, waitOn(fetchVal2)); + + // Because parent not cached, it should send another request + CompletableFuture fetchVal3 = client.fetchModelValue(PathUtils.parse("value")); + srv.handleSubscribeValue(PathUtils.parse("value"), "Hi"); + assertEquals("Hi", waitOn(fetchVal3)); + } + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/client/GadpClientTestHelper.java b/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/client/GadpClientTestHelper.java new file mode 100644 index 0000000000..14335c1ea5 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/client/GadpClientTestHelper.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.client; + +import java.util.concurrent.CompletableFuture; + +import com.google.protobuf.Message; + +/** + * A means for tests to access package members of {@link GadpClient} + */ +public enum GadpClientTestHelper { + ; + public static CompletableFuture sendChecked(GadpClient client, + Message.Builder req, MI exampleRep) { + return client.sendChecked(req, exampleRep); + } +} diff --git a/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannelTest.java b/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannelTest.java new file mode 100644 index 0000000000..9b9e3eb987 --- /dev/null +++ b/Ghidra/Debug/Debugger-gadp/src/test/java/ghidra/dbg/gadp/util/AsyncProtobufMessageChannelTest.java @@ -0,0 +1,188 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.gadp.util; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousByteChannel; +import java.nio.channels.CompletionHandler; +import java.util.Arrays; +import java.util.concurrent.*; + +import org.junit.Test; + +import ghidra.async.*; +import ghidra.dbg.gadp.protocol.Gadp; +import ghidra.dbg.gadp.protocol.Gadp.RootMessage; +import ghidra.util.NumericUtilities; +import ghidra.util.SystemUtilities; + +public class AsyncProtobufMessageChannelTest { + public final static long TIMEOUT_MILLIS = + SystemUtilities.isInTestingBatchMode() ? 1000 : Long.MAX_VALUE; + + public static T get(CompletableFuture future) throws Exception { + return future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } + + public static class AsyncFakeByteChannel implements AsynchronousByteChannel { + AsyncPairingQueue> readReqs = new AsyncPairingQueue<>(); + AsyncPairingQueue> writeReqs = new AsyncPairingQueue<>(); + + @Override + public void close() throws IOException { + } + + @Override + public boolean isOpen() { + return true; + } + + @Override + public void read(ByteBuffer dst, A attachment, + CompletionHandler handler) { + readReqs.give( + CompletableFuture.completedFuture(new ReadReq<>(dst, attachment, handler))); + } + + @Override + public Future read(ByteBuffer dst) { + return AsyncUtils.completable(TypeSpec.INT, this::read, dst); + } + + @Override + public void write(ByteBuffer src, A attachment, + CompletionHandler handler) { + writeReqs.give( + CompletableFuture.completedFuture(new WriteReq<>(src, attachment, handler))); + } + + @Override + public Future write(ByteBuffer src) { + return AsyncUtils.completable(TypeSpec.INT, this::write, src); + } + } + + public static class ReadReq { + private final ByteBuffer dst; + private final A attachment; + private final CompletionHandler handler; + + ReadReq(ByteBuffer dst, A attachment, CompletionHandler handler) { + this.dst = dst; + this.attachment = attachment; + this.handler = handler; + } + + void complete(byte[] src) { + dst.put(src); + handler.completed(src.length, attachment); + } + + void error(Throwable exc) { + handler.failed(exc, attachment); + } + } + + public static class WriteReq { + private final ByteBuffer src; + private final A attachment; + private final CompletionHandler handler; + + WriteReq(ByteBuffer src, A attachment, CompletionHandler handler) { + this.src = src; + this.attachment = attachment; + this.handler = handler; + } + + void complete(byte[] dst) { + src.get(dst); + handler.completed(dst.length, attachment); + } + + void error(Throwable exc) { + handler.failed(exc, attachment); + } + } + + AsyncFakeByteChannel byteChannel = new AsyncFakeByteChannel(); + + AsyncProtobufMessageChannel channel = + new AsyncProtobufMessageChannel<>(byteChannel, 1); + + @Test + public void testSendBufferGrows() throws Exception { + channel.write(Gadp.RootMessage.newBuilder().setSequence(0x12345678).build()); + + WriteReq wr = get(byteChannel.writeReqs.take()); + byte[] data = new byte[wr.src.remaining()]; + wr.complete(data); + assertEquals("0000000608f8acd19101", NumericUtilities.convertBytesToString(data)); + } + + @Test + public void testRecvBufferGrows() throws Exception { + byte[] data = + NumericUtilities.convertStringToBytes("0000000608f8acd191010000000608f8acd19101"); + CompletableFuture msg = channel.read(Gadp.RootMessage::parseFrom); + + ReadReq rd = get(byteChannel.readReqs.take()); + assertEquals(1, rd.dst.remaining()); + rd.complete(Arrays.copyOfRange(data, 0, 1)); + + rd = get(byteChannel.readReqs.take()); + assertEquals(0, rd.dst.remaining()); + rd.complete(Arrays.copyOfRange(data, 1, 1)); + + rd = get(byteChannel.readReqs.take()); + assertEquals(1, rd.dst.remaining()); + rd.complete(Arrays.copyOfRange(data, 1, 2)); + + rd = get(byteChannel.readReqs.take()); + assertEquals(0, rd.dst.remaining()); + rd.complete(Arrays.copyOfRange(data, 2, 2)); + + rd = get(byteChannel.readReqs.take()); + assertEquals(2, rd.dst.remaining()); + rd.complete(Arrays.copyOfRange(data, 2, 4)); + + rd = get(byteChannel.readReqs.take()); + assertEquals(0, rd.dst.remaining()); + rd.complete(Arrays.copyOfRange(data, 4, 4)); + + rd = get(byteChannel.readReqs.take()); + assertEquals(4, rd.dst.remaining()); + rd.complete(Arrays.copyOfRange(data, 4, 8)); + + rd = get(byteChannel.readReqs.take()); + assertEquals(0, rd.dst.remaining()); + rd.complete(Arrays.copyOfRange(data, 8, 8)); + + rd = get(byteChannel.readReqs.take()); + assertEquals(8, rd.dst.remaining()); + rd.complete(Arrays.copyOfRange(data, 8, 16)); + + assertEquals(Gadp.RootMessage.newBuilder().setSequence(0x12345678).build(), get(msg)); + + msg = channel.read(Gadp.RootMessage::parseFrom); + rd = get(byteChannel.readReqs.take()); + assertEquals(10, rd.dst.remaining()); + rd.complete(Arrays.copyOfRange(data, 6, 10)); + assertEquals(Gadp.RootMessage.newBuilder().setSequence(0x12345678).build(), get(msg)); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/Module.manifest b/Ghidra/Debug/Debugger-jpda/Module.manifest new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Ghidra/Debug/Debugger-jpda/build.gradle b/Ghidra/Debug/Debugger-jpda/build.gradle new file mode 100644 index 0000000000..e26c81cf4d --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/build.gradle @@ -0,0 +1,15 @@ +apply from: "${rootProject.projectDir}/gradle/javaProject.gradle" +apply from: "${rootProject.projectDir}/gradle/jacocoProject.gradle" +apply from: "${rootProject.projectDir}/gradle/javaTestProject.gradle" +apply from: "${rootProject.projectDir}/gradle/distributableGhidraModule.gradle" + +apply plugin: 'eclipse' +eclipse.project.name = 'Debug Debugger-jpda' + +dependencies { + compile project(':Framework-AsyncComm') + compile project(':Framework-Debugging') + compile project(':ProposedUtils') + + testCompile project(path: ':Framework-Debugging', configuration: 'testArtifacts') +} diff --git a/Ghidra/Debug/Debugger-jpda/certification.manifest b/Ghidra/Debug/Debugger-jpda/certification.manifest new file mode 100644 index 0000000000..f97a3aacc3 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/certification.manifest @@ -0,0 +1,5 @@ +##VERSION: 2.0 +.classpath||NONE||reviewed||END| +.project||NONE||reviewed||END| +Module.manifest||GHIDRA||||END| +build.gradle||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/JdiDebuggerModelFactory.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/JdiDebuggerModelFactory.java new file mode 100644 index 0000000000..fe13d67a0c --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/JdiDebuggerModelFactory.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.LocalDebuggerModelFactory; +import ghidra.dbg.jdi.model.JdiModelImpl; +import ghidra.dbg.util.ConfigurableFactory.FactoryDescription; +import ghidra.util.classfinder.ExtensionPointProperties; + +@FactoryDescription( // + brief = "JDI debugger", // + htmlDetails = "Debug a Java or Dalvik VM (supports JDWP)" // +) +@ExtensionPointProperties(priority = 50) +public class JdiDebuggerModelFactory implements LocalDebuggerModelFactory { + + @Override + public CompletableFuture build() { + return CompletableFuture.completedFuture(new JdiModelImpl()); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiCause.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiCause.java new file mode 100644 index 0000000000..bbe3eac3cb --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiCause.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +/** + * Identifies the cause of an event emitted by JDI + * + * This is not a concept native to JDI. Rather, it is a means to distinguish events that result from + * commands issued by the {@link JdiManager} from those issued by the user or some other means. For + * example, a call to {@link JdiManager#addInferior()} will emit a + * {@link JdiEventsListener#inferiorAdded(JdiVM, JdiCause)} event, identifying the + * {@link JdiPendingCommand} as the cause. However, a call to {@link JdiManager#console(String)} + * issuing an "add-inferior" command will emit the same event, but the cause will be + * {@link JdiCause.Causes#UNCLAIMED}. + */ +public interface JdiCause { + public enum Causes implements JdiCause { + UNCLAIMED; + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiConsoleOutputListener.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiConsoleOutputListener.java new file mode 100644 index 0000000000..14ac16a6ac --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiConsoleOutputListener.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +import ghidra.dbg.target.TargetConsole.Channel; + +public interface JdiConsoleOutputListener { + /** + * JDI outputted some text + * + *

+ * TODO: Do not depend on TargetObject API at the manager level. Make two callbacks, or define + * our own Channel enum. + * + * @param channel indicates stderr or stdout + * @param out the output + */ + void output(Channel channel, String out); +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventHandler.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventHandler.java new file mode 100644 index 0000000000..ac4eab5494 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventHandler.java @@ -0,0 +1,563 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +import java.util.concurrent.*; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import com.sun.jdi.*; +import com.sun.jdi.event.*; +import com.sun.jdi.request.EventRequest; + +import ghidra.async.AsyncReference; +import ghidra.dbg.jdi.manager.impl.DebugStatus; +import ghidra.util.Msg; +import ghidra.util.datastruct.ListenerSet; + +public class JdiEventHandler implements Runnable { + + volatile boolean connected = true; + boolean completed = false; + String shutdownMessageKey; + + private VirtualMachine vm; + private Thread thread; + private JdiEventHandler global; + + protected final AsyncReference state = + new AsyncReference<>(ThreadReference.THREAD_STATUS_NOT_STARTED); + public final ListenerSet listenersEvent = + new ListenerSet<>(JdiEventsListener.class); + protected final ExecutorService eventThread = Executors.newSingleThreadExecutor(); + + public JdiEventHandler() { + } + + public JdiEventHandler(VirtualMachine vm, JdiEventHandler global) { + this.vm = vm; + this.global = global; + state.filter(this::stateFilter); + } + + public void start() { + this.thread = new Thread(this, "event-handler"); + thread.start(); + } + + synchronized void shutdown() { + connected = false; // force run() loop termination + thread.interrupt(); + while (!completed) { + try { + wait(); + } + catch (InterruptedException exc) { + } + } + } + + public CompletableFuture event(Runnable r, String text) { + //Msg.debug(this, "Queueing event: " + text); + return CompletableFuture.runAsync(r, eventThread).exceptionally(ex -> { + Msg.error(this, "Error in event callback:", ex); + return ExceptionUtils.rethrow(ex); + }); + } + + public void addStateListener(JdiStateListener listener) { + state.addChangeListener(listener); + } + + public void removeStateListener(JdiStateListener listener) { + state.removeChangeListener(listener); + } + + public void addEventsListener(JdiEventsListener listener) { + listenersEvent.add(listener); + } + + public void removeEventsListener(JdiEventsListener listener) { + listenersEvent.remove(listener); + } + + @Override + public void run() { + EventQueue queue = vm.eventQueue(); + while (connected) { + try { + EventSet eventSet = queue.remove(); + DebugStatus status = DebugStatus.BREAK; + state.set(ThreadReference.THREAD_STATUS_WAIT, JdiCause.Causes.UNCLAIMED); + EventIterator it = eventSet.eventIterator(); + while (it.hasNext()) { + Event nextEvent = it.nextEvent(); + global.processEvent(nextEvent); + status = DebugStatus.update(processEvent(nextEvent)); + } + + if (status.equals(DebugStatus.GO)) { + state.set(ThreadReference.THREAD_STATUS_RUNNING, JdiCause.Causes.UNCLAIMED); + eventSet.resume(); + } + else if (eventSet.suspendPolicy() == EventRequest.SUSPEND_ALL) { + setCurrentThread(eventSet); + event( + () -> listenersEvent.fire.processStop(eventSet, JdiCause.Causes.UNCLAIMED), + "processStopped"); + } + } + catch (InterruptedException exc) { + // Do nothing. Any changes will be seen at top of loop. + } + catch (VMDisconnectedException discExc) { + handleDisconnectedException(); + break; + } + } + synchronized (this) { + completed = true; + notifyAll(); + } + } + + private DebugStatus processEvent(Event event) { + System.err.println(event + ":" + vm); + if (event instanceof ExceptionEvent) { + return processException((ExceptionEvent) event); + } + else if (event instanceof BreakpointEvent) { + return processBreakpoint((BreakpointEvent) event); + } + else if (event instanceof WatchpointEvent) { + return processWatchpoint((WatchpointEvent) event); + } + else if (event instanceof AccessWatchpointEvent) { + return processAccessWatchpoint((AccessWatchpointEvent) event); + } + else if (event instanceof ModificationWatchpointEvent) { + return processWatchpointModification((ModificationWatchpointEvent) event); + } + else if (event instanceof StepEvent) { + return processStep((StepEvent) event); + } + else if (event instanceof MethodEntryEvent) { + return processMethodEntry((MethodEntryEvent) event); + } + else if (event instanceof MethodExitEvent) { + return processMethodExit((MethodExitEvent) event); + } + else if (event instanceof MonitorContendedEnteredEvent) { + return processMCEntered((MonitorContendedEnteredEvent) event); + } + else if (event instanceof MonitorContendedEnterEvent) { + return processMCEnter((MonitorContendedEnterEvent) event); + } + else if (event instanceof MonitorWaitedEvent) { + return processMonitorWaited((MonitorWaitedEvent) event); + } + else if (event instanceof MonitorWaitEvent) { + return processMonitorWait((MonitorWaitEvent) event); + } + else if (event instanceof ClassPrepareEvent) { + return processClassPrepare((ClassPrepareEvent) event); + } + else if (event instanceof ClassUnloadEvent) { + return processClassUnload((ClassUnloadEvent) event); + } + else if (event instanceof ThreadStartEvent) { + return processThreadStart((ThreadStartEvent) event); + } + else if (event instanceof ThreadDeathEvent) { + return processThreadDeath((ThreadDeathEvent) event); + } + else if (event instanceof VMStartEvent) { + return processVMStart((VMStartEvent) event); + } + else if (event instanceof VMDisconnectEvent) { + return processVMDisconnect((VMDisconnectEvent) event); + } + else if (event instanceof VMDeathEvent) { + return processVMDeath((VMDeathEvent) event); + } + else { + System.err.println("Unknown event: " + event); + return null; + } + } + + private boolean vmDied = false; + + private DebugStatus handleExitEvent(Event event) { + if (event instanceof VMDeathEvent) { + vmDied = true; + return processVMDeath((VMDeathEvent) event); + } + else if (event instanceof VMDisconnectEvent) { + connected = false; + if (!vmDied) { + processVMDisconnect((VMDisconnectEvent) event); + } + /* + * Inform jdb command line processor that jdb is being shutdown. JDK-8154144. + */ + event(() -> listenersEvent.fire.processShutdown(event, JdiCause.Causes.UNCLAIMED), + "processStopped"); + return null; ///false; + } + else { + throw new InternalError(); + } + } + + synchronized void handleDisconnectedException() { + /* + * A VMDisconnectedException has happened while dealing with + * another event. We need to flush the event queue, dealing only + * with exit events (VMDeath, VMDisconnect) so that we terminate + * correctly. + */ + EventQueue queue = vm.eventQueue(); + while (connected) { + try { + EventSet eventSet = queue.remove(); + EventIterator iter = eventSet.eventIterator(); + while (iter.hasNext()) { + handleExitEvent(iter.next()); + } + } + catch (VMDisconnectedException exc) { + // ignore + } + catch (InterruptedException exc) { + // ignore + } + catch (InternalError exc) { + // ignore + } + } + } + + private ThreadReference eventThread(Event event) { + if (event instanceof ClassPrepareEvent) { + return ((ClassPrepareEvent) event).thread(); + } + else if (event instanceof LocatableEvent) { + return ((LocatableEvent) event).thread(); + } + else if (event instanceof ThreadStartEvent) { + return ((ThreadStartEvent) event).thread(); + } + else if (event instanceof ThreadDeathEvent) { + return ((ThreadDeathEvent) event).thread(); + } + else if (event instanceof VMStartEvent) { + return ((VMStartEvent) event).thread(); + } + else { + return null; + } + } + + private void setCurrentThread(EventSet set) { + ThreadReference thread; + if (set.size() > 0) { + /* + * If any event in the set has a thread associated with it, + * they all will, so just grab the first one. + */ + Event event = set.iterator().next(); // Is there a better way? + thread = eventThread(event); + } + else { + thread = null; + } + setCurrentThread(thread); + } + + private void setCurrentThread(ThreadReference thread) { + JdiThreadInfo.invalidateAll(); + JdiThreadInfo.setCurrentThread(thread); + } + + /** + * Handler for breakpoint events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processBreakpoint(BreakpointEvent evt) { + event(() -> listenersEvent.fire.breakpointHit(evt, JdiCause.Causes.UNCLAIMED), + "breakpointHit"); + return DebugStatus.BREAK; + } + + /** + * Handler for exception events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processException(ExceptionEvent evt) { + event(() -> listenersEvent.fire.exceptionHit(evt, JdiCause.Causes.UNCLAIMED), + "exceptionHit"); + return DebugStatus.BREAK; + } + + /** + * Handler for method entry events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processMethodEntry(MethodEntryEvent evt) { + event(() -> listenersEvent.fire.methodEntry(evt, JdiCause.Causes.UNCLAIMED), "methodEntry"); + return DebugStatus.GO; + } + + /** + * Handler for method exit events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processMethodExit(MethodExitEvent evt) { + event(() -> listenersEvent.fire.methodExit(evt, JdiCause.Causes.UNCLAIMED), "methodExit"); + return DebugStatus.GO; + } + + /** + * Handler for class prepared events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processClassPrepare(ClassPrepareEvent evt) { + event(() -> listenersEvent.fire.classPrepare(evt, JdiCause.Causes.UNCLAIMED), + "classPrepare"); + /* + if (!Env.specList.resolve(cle)) { + MessageOutput.lnprint("Stopping due to deferred breakpoint errors."); + return true; + } else { + return false; + } + */ + return DebugStatus.GO; + } + + /** + * Handler for class unload events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processClassUnload(ClassUnloadEvent evt) { + event(() -> listenersEvent.fire.classUnload(evt, JdiCause.Causes.UNCLAIMED), "classUnload"); + return DebugStatus.GO; + } + + /** + * Handler for monitor contended entered events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processMCEntered(MonitorContendedEnteredEvent evt) { + event(() -> listenersEvent.fire.monitorContendedEntered(evt, JdiCause.Causes.UNCLAIMED), + "monitorContendedEntered"); + return DebugStatus.GO; + } + + /** + * Handler for monitor contended enter events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processMCEnter(MonitorContendedEnterEvent evt) { + event(() -> listenersEvent.fire.monitorContendedEnter(evt, JdiCause.Causes.UNCLAIMED), + "monitorContendedEnter"); + return DebugStatus.GO; + } + + /** + * Handler for monitor waited events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processMonitorWaited(MonitorWaitedEvent evt) { + event(() -> listenersEvent.fire.monitorWaited(evt, JdiCause.Causes.UNCLAIMED), + "monitorWaited"); + return DebugStatus.GO; + } + + /** + * Handler for monitor waited events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processMonitorWait(MonitorWaitEvent evt) { + event(() -> listenersEvent.fire.monitorWait(evt, JdiCause.Causes.UNCLAIMED), "monitorWait"); + return DebugStatus.GO; + } + + /** + * Handler for step events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processStep(StepEvent evt) { + evt.request().disable(); + event(() -> listenersEvent.fire.stepComplete(evt, JdiCause.Causes.UNCLAIMED), "step"); + return DebugStatus.STEP_INTO; + } + + /** + * Handler for watchpoint events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processWatchpoint(WatchpointEvent evt) { + event(() -> listenersEvent.fire.watchpointHit(evt, JdiCause.Causes.UNCLAIMED), + "watchpointHit"); + return DebugStatus.BREAK; + } + + /** + * Handler for access watchpoint events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processAccessWatchpoint(AccessWatchpointEvent evt) { + event(() -> listenersEvent.fire.accessWatchpointHit(evt, JdiCause.Causes.UNCLAIMED), + "accessWatchpointHit"); + return DebugStatus.BREAK; + } + + /** + * Handler for watchpoint modified events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processWatchpointModification(ModificationWatchpointEvent evt) { + event(() -> listenersEvent.fire.watchpointModified(evt, JdiCause.Causes.UNCLAIMED), + "watchpointModified"); + return DebugStatus.GO; + } + + /** + * Handler for thread death events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processThreadDeath(ThreadDeathEvent evt) { + event(() -> listenersEvent.fire.threadExited(evt, JdiCause.Causes.UNCLAIMED), + "threadExited"); + JdiThreadInfo.removeThread(evt.thread()); + return DebugStatus.GO; + } + + /** + * Handler for thread start events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processThreadStart(ThreadStartEvent evt) { + JdiThreadInfo.addThread(evt.thread()); + event(() -> listenersEvent.fire.threadStarted(evt, JdiCause.Causes.UNCLAIMED), + "threadStarted"); + return DebugStatus.GO; + } + + /** + * Handler for vm death events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processVMDeath(VMDeathEvent evt) { + shutdownMessageKey = "The application exited"; + event(() -> listenersEvent.fire.vmDied(evt, JdiCause.Causes.UNCLAIMED), "vmDied"); + return DebugStatus.BREAK; + } + + /** + * Handler for vm disconnect events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processVMDisconnect(VMDisconnectEvent evt) { + shutdownMessageKey = "The application has been disconnected"; + event(() -> listenersEvent.fire.vmDisconnected(evt, JdiCause.Causes.UNCLAIMED), + "vmDisconnected"); + return DebugStatus.BREAK; + } + + /** + * Handler for vm start events + * + * @param evt the event + * @param v nothing + * @return + */ + protected DebugStatus processVMStart(VMStartEvent evt) { + event(() -> listenersEvent.fire.vmStarted(evt, JdiCause.Causes.UNCLAIMED), "vmStarted"); + return DebugStatus.BREAK; + } + + public Integer getState() { + return state.get(); + } + + public void setState(Integer val, JdiCause cause) { + state.set(val, cause); + } + + private Integer stateFilter(Integer cur, Integer set, JdiCause cause) { + if (set == null) { + return cur; + } + return set; + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventsListener.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventsListener.java new file mode 100644 index 0000000000..96d5a32665 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventsListener.java @@ -0,0 +1,267 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +import com.sun.jdi.*; +import com.sun.jdi.event.*; + +import ghidra.dbg.jdi.manager.breakpoint.JdiBreakpointInfo; + +/** + * A listener for events related to objects known to the manager + */ +public interface JdiEventsListener { + + /** + * A different vm has been selected (gained focus) + * + * @param vm a handle to the selected vm + * @param cause the cause of this event + */ + void vmSelected(VirtualMachine vm, JdiCause cause); + + /** + * A different thread has been selected (gained focus) + * + * @param thread a handle to the selected thread + * @param frame a handle to the current frame + * @param cause the cause of this event + */ + void threadSelected(ThreadReference thread, StackFrame frame, JdiCause cause); + + /** + * A library has been loaded by an vm + * + * @param vm a handle to the vm which loaded the library + * @param name the name of the library on the target + * @param cause the cause of this event + */ + void libraryLoaded(VirtualMachine vm, String name, JdiCause cause); + + /** + * A library has been unloaded from an vm + * + * @param vm a handle to the vm which unloaded the library + * @param name the name of the library on the target + * @param cause the cause of this event + */ + void libraryUnloaded(VirtualMachine vm, String name, JdiCause cause); + + /** + * A breakpoint has been created in the session + * + * @param info information about the new breakpoint + * @param cause the cause of this event + */ + void breakpointCreated(JdiBreakpointInfo info, JdiCause cause); + + /** + * A breakpoint in the session has been modified + * + * @param newInfo new information about the modified breakpoint + * @param oldInfo old information about the modified breakpoint + * @param cause the cause of this event + */ + void breakpointModified(JdiBreakpointInfo newInfo, JdiBreakpointInfo oldInfo, JdiCause cause); + + /** + * A breakpoint has been deleted from the session + * + * @param info information about the now-deleted breakpoint + * @param cause the cause of this event + */ + void breakpointDeleted(JdiBreakpointInfo info, JdiCause cause); + + /** + * TODO: This is not yet implemented + * + * It is not clear whether JDI detects when a target writes into its own memory, or if this + * event is emitted when JDI changes the target's memory, or both. + * + * @param vm the vm whose memory changed + * @param addr the address of the change + * @param len the length, with the address, bounding the region of change + * @param cause the cause of this event + */ + void memoryChanged(VirtualMachine vm, long addr, int len, JdiCause cause); + + void vmInterrupted(); + + /** + * A breakpoint has been hit + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void breakpointHit(BreakpointEvent evt, JdiCause cause); + + /** + * An exception has been hit + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void exceptionHit(ExceptionEvent evt, JdiCause cause); + + /** + * A method has been invoked + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void methodEntry(MethodEntryEvent evt, JdiCause cause); + + /** + * A method is about to finish + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void methodExit(MethodExitEvent evt, JdiCause cause); + + /** + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void classPrepare(ClassPrepareEvent evt, JdiCause cause); + + /** + * A calls is being unloaded + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void classUnload(ClassUnloadEvent evt, JdiCause cause); + + /** + * A thread has entered a monitor after release from another thread + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void monitorContendedEntered(MonitorContendedEnteredEvent evt, JdiCause cause); + + /** + * A thread is attempting to enter monitor acquired by another thread + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void monitorContendedEnter(MonitorContendedEnterEvent evt, JdiCause cause); + + /** + * A vm has finished waiting on a monitor object + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void monitorWaited(MonitorWaitedEvent evt, JdiCause cause); + + /** + * A vm is about to wait on a monitor object + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void monitorWait(MonitorWaitEvent evt, JdiCause cause); + + /** + * A step has completed + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void stepComplete(StepEvent evt, JdiCause cause); + + /** + * A watchpoint has been hit + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void watchpointHit(WatchpointEvent evt, JdiCause cause); + + /** + * A field has been accessed + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void accessWatchpointHit(AccessWatchpointEvent evt, JdiCause cause); + + /** + * A field has been modified + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void watchpointModified(ModificationWatchpointEvent evt, JdiCause cause); + + /** + * A thread has exited + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void threadExited(ThreadDeathEvent evt, JdiCause cause); + + /** + * A thread has started + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void threadStarted(ThreadStartEvent evt, JdiCause cause); + + /** + * A thread has changed state + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void threadStateChanged(ThreadReference thread, Integer state, JdiCause cause, + JdiReason reason); + + /** + * A vm has exited + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void vmDied(VMDeathEvent evt, JdiCause cause); + + /** + * A vm has been disconnected + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void vmDisconnected(VMDisconnectEvent evt, JdiCause cause); + + /** + * A vm has started + * + * @param evt the triggering event + * @param cause the cause of this event + */ + void vmStarted(VMStartEvent evt, JdiCause cause); + + void processStop(EventSet eventSet, JdiCause cause); + + void processShutdown(Event event, JdiCause cause); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventsListenerAdapter.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventsListenerAdapter.java new file mode 100644 index 0000000000..044adbf6a7 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventsListenerAdapter.java @@ -0,0 +1,157 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +import java.util.Collection; + +import com.sun.jdi.*; +import com.sun.jdi.event.*; + +import ghidra.dbg.jdi.manager.breakpoint.JdiBreakpointInfo; + +/** + * An adapter for {@link JdiEventsListener} + * + * This provides an empty default implementation of each method. + */ +public interface JdiEventsListenerAdapter extends JdiEventsListener { + + @Override + default void vmSelected(VirtualMachine vm, JdiCause cause) { + } + + @Override + default void threadSelected(ThreadReference thread, StackFrame frame, JdiCause cause) { + } + + @Override + default void threadStateChanged(ThreadReference thread, Integer state, JdiCause cause, + JdiReason reason) { + } + + @Override + default void libraryLoaded(VirtualMachine vm, String name, JdiCause cause) { + } + + @Override + default void libraryUnloaded(VirtualMachine vm, String name, JdiCause cause) { + } + + @Override + default void breakpointCreated(JdiBreakpointInfo info, JdiCause cause) { + } + + @Override + default void breakpointModified(JdiBreakpointInfo newInfo, JdiBreakpointInfo oldInfo, + JdiCause cause) { + } + + @Override + default void breakpointDeleted(JdiBreakpointInfo info, JdiCause cause) { + } + + @Override + default void memoryChanged(VirtualMachine vm, long addr, int len, JdiCause cause) { + } + + @Override + default void vmInterrupted() { + } + + @Override + default void breakpointHit(BreakpointEvent evt, JdiCause cause) { + } + + @Override + default void exceptionHit(ExceptionEvent evt, JdiCause cause) { + } + + @Override + default void methodEntry(MethodEntryEvent evt, JdiCause cause) { + } + + @Override + default void methodExit(MethodExitEvent evt, JdiCause cause) { + } + + @Override + default void classPrepare(ClassPrepareEvent evt, JdiCause cause) { + } + + @Override + default void classUnload(ClassUnloadEvent evt, JdiCause cause) { + } + + @Override + default void monitorContendedEntered(MonitorContendedEnteredEvent evt, JdiCause cause) { + } + + @Override + default void monitorContendedEnter(MonitorContendedEnterEvent evt, JdiCause cause) { + } + + @Override + default void monitorWaited(MonitorWaitedEvent evt, JdiCause cause) { + } + + @Override + default void monitorWait(MonitorWaitEvent evt, JdiCause cause) { + } + + @Override + default void stepComplete(StepEvent evt, JdiCause cause) { + } + + @Override + default void watchpointHit(WatchpointEvent evt, JdiCause cause) { + } + + @Override + default void accessWatchpointHit(AccessWatchpointEvent evt, JdiCause cause) { + } + + @Override + default void watchpointModified(ModificationWatchpointEvent evt, JdiCause cause) { + } + + @Override + default void threadExited(ThreadDeathEvent evt, JdiCause cause) { + } + + @Override + default void threadStarted(ThreadStartEvent evt, JdiCause cause) { + } + + @Override + default void vmDied(VMDeathEvent evt, JdiCause cause) { + } + + @Override + default void vmDisconnected(VMDisconnectEvent evt, JdiCause cause) { + } + + @Override + default void vmStarted(VMStartEvent evt, JdiCause cause) { + } + + @Override + default void processStop(EventSet eventSet, JdiCause cause) { + } + + @Override + default void processShutdown(Event event, JdiCause cause) { + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiManager.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiManager.java new file mode 100644 index 0000000000..de0e14ce30 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiManager.java @@ -0,0 +1,219 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.tuple.Pair; + +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.VirtualMachineManager; +import com.sun.jdi.connect.Connector; +import com.sun.jdi.connect.Connector.Argument; + +import ghidra.dbg.jdi.manager.impl.JdiManagerImpl; + +/** + * The controlling side of a JDI session + */ +public interface JdiManager extends AutoCloseable { + + public enum Channel { + STDOUT, STDERR; + } + + /** + * Get a new manager instance, without starting JDI + * + * @return the manager + */ + public static JdiManager newInstance() { + return new JdiManagerImpl(); + } + + /** + * Terminates JDI + */ + @Override + default void close() { + terminate(); + } + + /** + * Terminate JDI + */ + void terminate(); + + /** + * Add a listener for JDI's state + * + * @see #getState() + * @param vm the virtual machine + * @param listener the listener to add + */ + void addStateListener(VirtualMachine vm, JdiStateListener listener); + + /** + * Remove a listener for JDI's state + * + * @see #getState() + * @param vm the virtual machine + * @param listener the listener to remove + */ + void removeStateListener(VirtualMachine vm, JdiStateListener listener); + + /** + * Add a listener for events on inferiors + * + * @param vm the virtual machine + * @param listener the listener to add + */ + void addEventsListener(VirtualMachine vm, JdiEventsListener listener); + + /** + * Remove a listener for events on inferiors + * + * @param vm the virtual machine + * @param listener the listener to remove + */ + void removeEventsListener(VirtualMachine vm, JdiEventsListener listener); + + /** + * Add a listener for target output + * + * @param listener the listener to add + */ + void addTargetOutputListener(JdiTargetOutputListener listener); + + /** + * Remove a listener for target output + * + * @see #addTargetOutputListener(JdiTargetOutputListener) + * @param listener + */ + void removeTargetOutputListener(JdiTargetOutputListener listener); + + /** + * Add a listener for console output + * + * @param listener the listener to add + */ + void addConsoleOutputListener(JdiConsoleOutputListener listener); + + /** + * Remove a listener for console output + * + * @param listener + */ + void removeConsoleOutputListener(JdiConsoleOutputListener listener); + + /** + * Get an inferior by its JDI-assigned ID + * + * JDI numbers virtual machines incrementally. All vms and created and destroyed by the user. + * See {@link #getVM()}. + * + * @param iid the inferior ID + * @return a handle to the inferior, if it exists + */ + VirtualMachine getVM(String id); + + /** + * Get all inferiors known to the manager + * + * This does not ask JDI to list its inferiors. Rather it returns a read-only view of the + * manager's understanding of the current inferiors based on its tracking of JDI events. + * + * @return a map of inferior IDs to corresponding inferior handles + */ + Map getKnownVMs(); + + /** + * Send an interrupt to JDI regardless of other queued commands + * + * This may be useful if the manager's command queue is stalled because an inferior is running. + * + * @throws IOException if an I/O error occurs + * @throws InterruptedException + */ + void sendInterruptNow() throws IOException; + + /** + * Add a virtual machine + * + * @param cx Connector specifying how to access the vm + * @param args start-up parameters + * + * @return a future which completes with the handle to the new vm + */ + CompletableFuture addVM(Connector cx, List args); + + CompletableFuture addVM(Connector cx, Map args); + + /** + * Remove a vm + * + * @param vm the vm to remove + * @return a future which completes then JDI has executed the command + */ + CompletableFuture removeVM(VirtualMachine vm); + + /** + * Execute an arbitrary CLI command, printing output to the CLI console + * + * Note: to ensure a certain thread or inferior has focus for a console command, see + * {@link JdiThread#console(String)} and {@link JdiVM#console(String)}. + * + * @param command the command to execute + * @return a future that completes when JDI has executed the command + */ + CompletableFuture console(String command); + + /** + * Execute an arbitrary CLI command, capturing its console output + * + * The output will not be printed to the CLI console. To ensure a certain thread or inferior has + * focus for a console command, see {@link JdiThread#consoleCapture(String)} and + * {@link JdiVM#consoleCapture(String)}. + * + * @param command the command to execute + * @return a future that completes with the captured output when JDI has executed the command + */ + CompletableFuture consoleCapture(String command); + + /** + * List JDI's virtual machines + * + * @return a future that completes with a map of inferior IDs to inferior handles + */ + CompletableFuture> listVMs(); + + /** + * List the available processes on target + * + * @return a future that completes with a list of PIDs + */ + @Deprecated(forRemoval = true) + CompletableFuture>> listAvailableProcesses(); + + public VirtualMachineManager getVirtualMachineManager(); + + JdiEventHandler getEventHandler(VirtualMachine vm); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiMemoryMapping.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiMemoryMapping.java new file mode 100644 index 0000000000..c3ff95ed3c --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiMemoryMapping.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +public interface JdiMemoryMapping { + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiReason.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiReason.java new file mode 100644 index 0000000000..3ef9e37c72 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiReason.java @@ -0,0 +1,95 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +import com.sun.jdi.ThreadReference; + +/** + * Indicates the reason for a thread's state to change, usually only when + * {@link ThreadReference#STOPPED} + * + * This concept is native to JDI. When a thread stops, JDI may communicate the reason, e.g., a + * breakpoint was hit, or the thread exited. The manager attempts to parse information for the + * reasons it understands and provides it in e.g., a {@link JdiBreakpointHitReason}. If JDI provides + * a reason that is not understood by the manager, then {@link JdiReason.Reasons#UNKNOWN} is given. + * If no reason is provided, then {@link JdiReason.Reasons#NONE} is given. + */ +public interface JdiReason { + /** + * A map of reason strings to reason classes + */ + /* + static final Map> TYPES = + new ImmutableMap.Builder>() + .put("signal-received", JdiSignalReceivedReason::new) + .put("breakpoint-hit", JdiBreakpointHitReason::new) + .put("end-stepping-range", JdiEndSteppingRangeReason::new) + .put("exited", JdiExitedReason::new) + .put("exited-normally", JdiExitNormallyReason::new) + .build(); + */ + + /** + * Reasons other than those given by JDI + */ + enum Reasons implements JdiReason { + /** + * No reason was given + */ + NONE("No reason"), + /** + * Step complete + */ + STEP("Step"), + /** + * Target interrupted + */ + INTERRUPT("Interrupt"), + /** + * Breakpoint hit + */ + BREAKPOINT_HIT("Breakpoint"), + /** + * Watchpoint hit + */ + WATCHPOINT_HIT("Watchpoint"), + /** + * Access watchpoint hit + */ + ACCESS_WATCHPOINT_HIT("Access Watchpoint"), + /** + * Target resumed + */ + RESUMED("Resumed"), + /** + * A reason was given, but the manager does not understand it + */ + UNKNOWN("Unknown"); + + final String desc; + + private Reasons(String desc) { + this.desc = desc; + } + + @Override + public String desc() { + return desc; + } + } + + public String desc(); +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiStateListener.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiStateListener.java new file mode 100644 index 0000000000..d0a6da1862 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiStateListener.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +import ghidra.util.TriConsumer; + +/** + * A listener for changes in JDI's state + */ +public interface JdiStateListener extends TriConsumer { + /** + * The state has changed because of the given cause + * + * @param state the new state + * @param cause the reason for the change + */ + void stateChanged(Integer state, JdiCause cause); + + @Override + default void accept(Integer oldSt, Integer newSt, JdiCause u) { + stateChanged(newSt, u); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiTargetOutputListener.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiTargetOutputListener.java new file mode 100644 index 0000000000..959e437410 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiTargetOutputListener.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +/** + * A listener for target console output + * + * Note the details of this listener are not well established, for lack of examples that use JDI's + * target output record. + */ +public interface JdiTargetOutputListener { + /** + * The target outputted some text + * + * @param out the output + */ + void output(String out); +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiThreadInfo.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiThreadInfo.java new file mode 100644 index 0000000000..0521cefea4 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiThreadInfo.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +import com.sun.jdi.ThreadReference; + +public class JdiThreadInfo { + + public static void addThread(ThreadReference thread) { + // TODO Auto-generated method stub + + } + + public static void removeThread(ThreadReference thread) { + // TODO Auto-generated method stub + + } + + public static void invalidateAll() { + // TODO Auto-generated method stub + + } + + public static void setCurrentThread(ThreadReference thread) { + // TODO Auto-generated method stub + + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiVMThreadGroup.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiVMThreadGroup.java new file mode 100644 index 0000000000..61e596ff69 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiVMThreadGroup.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager; + +public class JdiVMThreadGroup { + private final int iid; + private final String type; + private final Long pid; + private final Long exitCode; + private final String executable; + // TODO: cores + + public JdiVMThreadGroup(int iid, String type, Long pid, Long exitCode, + String executable) { + this.iid = iid; + this.type = type; + this.pid = pid; + this.exitCode = exitCode; + this.executable = executable; + } + + public int getInferiorId() { + return iid; + } + + public String getType() { + return type; + } + + public Long getPid() { + return pid; + } + + public Long getExitCode() { + return exitCode; + } + + public String getExecutable() { + return executable; + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/breakpoint/JdiBreakpointInfo.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/breakpoint/JdiBreakpointInfo.java new file mode 100644 index 0000000000..29786d2734 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/breakpoint/JdiBreakpointInfo.java @@ -0,0 +1,163 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager.breakpoint; + +import java.util.*; + +import com.sun.jdi.*; +import com.sun.jdi.request.*; + +/** + * Information about a JDI breakpoint + * + * The contains the semantic processing for JDI breakpoint information. Mostly, it just stores the + * information, but it also enumerates the locations of a breakpoint and generates the "effective" + * breakpoints. + * + * Note this is not a handle to the breakpoint. Rather, this is the captured information from some + * event or request. If other commands have been executed since this information was gathered, the + * information may be stale. + */ +public class JdiBreakpointInfo { + + private final EventRequest request; + private final JdiBreakpointType type; + + private ObjectReference objectFilter; + private ThreadReference threadFilter; + private ReferenceType classFilter; + private String filterPattern; + private boolean excludePattern; + + public JdiBreakpointInfo(BreakpointRequest request) { + this.request = request; + this.type = JdiBreakpointType.BREAKPOINT; + } + + public JdiBreakpointInfo(AccessWatchpointRequest request) { + this.request = request; + this.type = JdiBreakpointType.ACCESS_WATCHPOINT; + } + + public JdiBreakpointInfo(ModificationWatchpointRequest request) { + this.request = request; + this.type = JdiBreakpointType.MODIFICATION_WATCHPOINT; + } + + @Override + public int hashCode() { + return Objects.hash(request); + } + + @Override + public String toString() { + return request.toString(); + } + + @Override + public boolean equals(Object obj) { + if (!((obj instanceof JdiBreakpointInfo))) { + return false; + } + JdiBreakpointInfo that = (JdiBreakpointInfo) obj; + if (this.request != that.request) { + return false; + } + return true; + } + + /** + * Get the type of breakpoint + * + * @return the type + */ + public JdiBreakpointType getType() { + return type; + } + + /** + * Get the original request + * + * @return the request + */ + public EventRequest getRequest() { + return request; + } + + public ObjectReference getObjectFilter() { + return objectFilter; + } + + public void setObjectFilter(ObjectReference objectFilter) { + this.objectFilter = objectFilter; + } + + public ThreadReference getThreadFilter() { + return threadFilter; + } + + public void setThreadFilter(ThreadReference threadFilter) { + this.threadFilter = threadFilter; + } + + public ReferenceType getClassFilter() { + return classFilter; + } + + public void setClassFilter(ReferenceType classFilter) { + this.classFilter = classFilter; + } + + public String getFilterPattern() { + return filterPattern; + } + + public void setFilterPattern(String filterPattern) { + this.filterPattern = filterPattern; + } + + public boolean isEnabled() { + if (request instanceof BreakpointRequest) { + return ((BreakpointRequest) request).isEnabled(); + } + if (request instanceof WatchpointRequest) { + return ((WatchpointRequest) request).isEnabled(); + } + return false; + } + + public void setEnabled(boolean b) { + if (request instanceof BreakpointRequest) { + BreakpointRequest breakpoint = (BreakpointRequest) request; + if (b) { + breakpoint.enable(); + } + else { + breakpoint.disable(); + } + } + if (request instanceof WatchpointRequest) { + WatchpointRequest watchpoint = (WatchpointRequest) request; + if (b) { + watchpoint.enable(); + } + else { + watchpoint.disable(); + } + } + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/breakpoint/JdiBreakpointType.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/breakpoint/JdiBreakpointType.java new file mode 100644 index 0000000000..a6d6a1fe1c --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/breakpoint/JdiBreakpointType.java @@ -0,0 +1,65 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager.breakpoint; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * The type of JDI breakpoint + */ +public enum JdiBreakpointType { + + BREAKPOINT("breakpoint", false), + ACCESS_WATCHPOINT("access watchpont", true), + MODIFICATION_WATCHPOINT("modification watchpoint", true), + OTHER("", false); + + public static final Map BY_NAME = + List.of(values()).stream().collect(Collectors.toMap(v -> v.getName(), v -> v)); + + /** + * Parse a type from a JDI breakpoint information block + * + * @param string the value of type parsed + * @return the enum constant, or {@link #OTHER} if unrecognized + */ + public static JdiBreakpointType fromStr(String string) { + return BY_NAME.getOrDefault(string, OTHER); + } + + private final String name; + private final boolean isWatchpoint; + + private JdiBreakpointType(String name, boolean isWatchpoint) { + this.name = name; + this.isWatchpoint = isWatchpoint; + } + + @Override + public String toString() { + return name; + } + + public String getName() { + return name; + } + + public boolean isWatchpoint() { + return isWatchpoint; + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/DebugStatus.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/DebugStatus.java new file mode 100644 index 0000000000..5b4a7d805a --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/DebugStatus.java @@ -0,0 +1,65 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager.impl; + +import com.sun.jdi.ThreadReference; + +public enum DebugStatus { + NO_CHANGE(false, null, 13), // + GO(true, ThreadReference.THREAD_STATUS_RUNNING, 10), // + STEP_OVER(true, ThreadReference.THREAD_STATUS_RUNNING, 7), // + STEP_INTO(true, ThreadReference.THREAD_STATUS_RUNNING, 5), // + BREAK(false, ThreadReference.THREAD_STATUS_RUNNING, 0), // + NO_DEBUGGEE(true, null, 1), // shouldWait is true to handle process creation + STEP_BRANCH(true, null, 6), // + IGNORE_EVENT(false, null, 11), // + RESTART_REQUESTED(true, null, 12), // + OUT_OF_SYNC(false, null, 2), // + WAIT_INPUT(false, null, 3), // + TIMEOUT(false, null, 4), // + ; + + public static final long MASK = 0xaf; + public static final long INSIDE_WAIT = 0x100000000L; + public static final long WAIT_TIMEOUT = 0x200000000L; + + DebugStatus(boolean shouldWait, Integer threadState, int precedence) { + this.shouldWait = shouldWait; + this.threadState = threadState; + this.precedence = precedence; + } + + public final boolean shouldWait; + public final Integer threadState; + public final int precedence; // 0 is highest + + public static DebugStatus fromArgument(long argument) { + return values()[(int) (argument & MASK)]; + } + + public static boolean isInsideWait(long argument) { + return (argument & INSIDE_WAIT) != 0; + } + + public static boolean isWaitTimeout(long argument) { + return (argument & WAIT_TIMEOUT) != 0; + } + + public static DebugStatus update(DebugStatus added) { + return added; + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/JdiManagerImpl.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/JdiManagerImpl.java new file mode 100644 index 0000000000..85bdb55bd9 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/JdiManagerImpl.java @@ -0,0 +1,276 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.manager.impl; + +import static ghidra.lifecycle.Unfinished.*; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.*; + +import org.apache.commons.lang3.tuple.Pair; + +import com.sun.jdi.*; +import com.sun.jdi.connect.*; + +import ghidra.dbg.jdi.manager.*; +import ghidra.dbg.jdi.manager.JdiCause.Causes; +import ghidra.util.datastruct.ListenerSet; + +public class JdiManagerImpl implements JdiManager { + + public DebugStatus status; + + private VirtualMachineManager virtualMachineManager; + private final Map vms = new LinkedHashMap<>(); + private VirtualMachine curVM = null; + private final Map unmodifiableVMs = Collections.unmodifiableMap(vms); + + protected final ListenerSet listenersTargetOutput = + new ListenerSet<>(JdiTargetOutputListener.class); + protected final ListenerSet listenersConsoleOutput = + new ListenerSet<>(JdiConsoleOutputListener.class); + protected final ExecutorService eventThread = Executors.newSingleThreadExecutor(); + + protected JdiEventHandler globalEventHandler = new JdiEventHandler(); + protected Map eventHandlers = new HashMap<>(); + protected Map connectors = new HashMap<>(); + + /** + * Instantiate a new manager + */ + public JdiManagerImpl() { + virtualMachineManager = Bootstrap.virtualMachineManager(); + } + + public VirtualMachine connectVM(Connector cx, Map arguments) + throws Exception { + if (cx instanceof LaunchingConnector) { + LaunchingConnector lcx = (LaunchingConnector) cx; + return lcx.launch(arguments); + } + if (cx instanceof AttachingConnector) { + AttachingConnector acx = (AttachingConnector) cx; + return acx.attach(arguments); + } + if (cx instanceof ListeningConnector) { + ListeningConnector lcx = (ListeningConnector) cx; + return lcx.accept(arguments); + } + throw new Exception("Unknown connector type"); + } + + @Override + public void terminate() { + /** + * NB: can use manager.connectedVMs, because technically, other things could be using the + * JDI outside of this manager. + */ + for (VirtualMachine vm : vms.values()) { + // TODO: Force exit those we launched? + try { + vm.dispose(); + } + catch (VMDisconnectedException e) { + // I guess we're good! + } + } + } + + @Override + public void addStateListener(VirtualMachine vm, JdiStateListener listener) { + if (vm != null) { + JdiEventHandler eventHandler = eventHandlers.get(vm); + if (eventHandler != null) { + eventHandler.addStateListener(listener); + } + } + else { + globalEventHandler.addStateListener(listener); + } + } + + @Override + public void removeStateListener(VirtualMachine vm, JdiStateListener listener) { + if (vm != null) { + eventHandlers.get(vm).removeStateListener(listener); + } + else { + globalEventHandler.removeStateListener(listener); + } + } + + @Override + public void addEventsListener(VirtualMachine vm, JdiEventsListener listener) { + if (vm != null) { + eventHandlers.get(vm).addEventsListener(listener); + } + else { + globalEventHandler.addEventsListener(listener); + } + } + + @Override + public void removeEventsListener(VirtualMachine vm, JdiEventsListener listener) { + if (vm != null) { + eventHandlers.get(vm).removeEventsListener(listener); + } + else { + globalEventHandler.removeEventsListener(listener); + } + } + + @Override + public void addTargetOutputListener(JdiTargetOutputListener listener) { + listenersTargetOutput.add(listener); + } + + @Override + public void removeTargetOutputListener(JdiTargetOutputListener listener) { + listenersTargetOutput.remove(listener); + } + + @Override + public void addConsoleOutputListener(JdiConsoleOutputListener listener) { + listenersConsoleOutput.add(listener); + } + + @Override + public void removeConsoleOutputListener(JdiConsoleOutputListener listener) { + listenersConsoleOutput.remove(listener); + } + + @Override + public VirtualMachine getVM(String id) { + return vms.get(id); + } + + @Override + public Map getKnownVMs() { + return unmodifiableVMs; + } + + @Override + public void sendInterruptNow() throws IOException { + for (VirtualMachine vm : vms.values()) { + for (ThreadReference thread : vm.allThreads()) { + thread.interrupt(); + } + } + } + + @Override + public CompletableFuture addVM(Connector cx, List args) { + Map arguments = cx.defaultArguments(); + if (cx instanceof LaunchingConnector) { + if (arguments.containsKey("command")) { + arguments.get("command").setValue(args.get(0)); + } + else { + arguments.get("main").setValue(args.get(0)); + } + } + if (cx instanceof AttachingConnector) { + if (arguments.containsKey("pid")) { + arguments.get("pid").setValue("" + Integer.decode(args.get(0))); + } + else { + if (args.size() == 2) { + arguments.get("hostname").setValue(args.get(0)); + arguments.get("port").setValue(args.get(1)); + } + else { + arguments.get("port").setValue(args.get(0)); + } + } + } + if (cx instanceof ListeningConnector) { + arguments.get("port").setValue("0"); + arguments.get("localAddress").setValue("localhost"); + } + return addVM(cx, arguments); + } + + @Override + public CompletableFuture addVM(Connector cx, + Map args) { + // TODO: Since this is making a blocking-on-the-network call, it should be supplyAsync + try { + curVM = connectVM(cx, args); + JdiEventHandler eventHandler = new JdiEventHandler(curVM, globalEventHandler); + eventHandler.start(); + eventHandler.setState(ThreadReference.THREAD_STATUS_NOT_STARTED, Causes.UNCLAIMED); + eventHandlers.put(curVM, eventHandler); + vms.put(curVM.name(), curVM); + connectors.put(curVM, cx); + } + catch (VMDisconnectedException e) { + System.out.println("Virtual Machine is disconnected."); + return CompletableFuture.failedFuture(e); + } + catch (Exception e) { + return CompletableFuture.failedFuture(e); + } + return CompletableFuture.completedFuture(curVM); + } + + @Override + public CompletableFuture removeVM(VirtualMachine vm) { + if (curVM == vm) { + curVM = null; + } + vms.remove(vm.name()); + connectors.remove(vm); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture console(String command) { + return TODO(); + } + + @Override + public CompletableFuture consoleCapture(String command) { + return TODO(); + } + + @Override + public CompletableFuture> listVMs() { + return CompletableFuture.completedFuture(vms); + } + + @Override + @Deprecated(forRemoval = true) + public CompletableFuture>> listAvailableProcesses() { + List> processes = new ArrayList<>(); + return CompletableFuture.completedFuture(processes); + } + + @Override + public VirtualMachineManager getVirtualMachineManager() { + return virtualMachineManager; + } + + public Connector getConnector(VirtualMachine vm) { + return connectors.get(vm); + } + + @Override + public JdiEventHandler getEventHandler(VirtualMachine vm) { + return eventHandlers.get(vm); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelImpl.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelImpl.java new file mode 100644 index 0000000000..3e858e6dbb --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelImpl.java @@ -0,0 +1,224 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.*; + +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.AbstractDebuggerObjectModel; +import ghidra.dbg.jdi.manager.JdiManager; +import ghidra.dbg.target.TargetObject; +import ghidra.program.model.address.*; + +public class JdiModelImpl extends AbstractDebuggerObjectModel { + + protected JdiModelTargetRoot root; + protected final CompletableFuture completedRoot; + + public static final long BLOCK_SIZE = 0x1000L; + public static final long DEFAULT_SECTION = 0x0000L; + + protected JdiManager jdi; + protected final AddressSpace ram = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 0); + protected Long ramIndex = Long.valueOf(0x1000L); + protected final AddressSpace pool = + new GenericAddressSpace("constantPool", 64, AddressSpace.TYPE_RAM, 0); + protected Long poolIndex = Long.valueOf(0x0L); + protected final AddressFactory addressFactory = + new DefaultAddressFactory(new AddressSpace[] { ram, pool }); + public AddressRangeImpl defaultRange; + + private Map addressRangeByMethod = new HashMap<>(); + private Map methodsByKey = new HashMap<>(); + private Map addressRangeByClass = new HashMap<>(); + + public JdiModelImpl() { + this.jdi = JdiManager.newInstance(); + this.root = new JdiModelTargetRoot(this); + this.completedRoot = CompletableFuture.completedFuture(root); + + Address start = ram.getAddress(0L); + this.defaultRange = new AddressRangeImpl(start, start.add(BLOCK_SIZE)); + } + + @Override + public CompletableFuture fetchModelRoot() { + return completedRoot; + } + + @Override + public AddressSpace getAddressSpace(String name) { + switch (name) { + case "ram": + return ram; + case "constantPool": + return pool; + default: + return null; + } + } + + @Override + public CompletableFuture close() { + jdi.terminate(); + return AsyncUtils.NIL; + } + + public JdiModelTargetRoot getRoot() { + return root; + } + + public JdiManager getManager() { + return jdi; + } + + public static String methodToKey(Method method) { + return method.toString(); + } + + public void registerMethod(Method method) { + if (method == null) { + return; + } + if (!methodsByKey.containsValue(method)) { + methodsByKey.put(methodToKey(method), method); + } + if (!addressRangeByMethod.containsKey(methodToKey(method))) { + ReferenceType declaringType = method.declaringType(); + if (declaringType instanceof ClassType) { + JdiModelTargetVM targetVM = getTargetVM(declaringType); + JdiModelTargetReferenceType classRef = + (JdiModelTargetReferenceType) targetVM.getTargetObject(declaringType); + JdiModelTargetSectionContainer sectionContainer = classRef.sections; + for (Method m : declaringType.methods()) { + if (m.location() != null && targetVM.vm.canGetBytecodes()) { + byte[] bytecodes = m.bytecodes(); + int length = bytecodes.length; + if (length > 0) { + synchronized (ramIndex) { + Address start = ram.getAddress(ramIndex); + AddressRangeImpl range = + new AddressRangeImpl(start, start.add(BLOCK_SIZE - 1)); + String key = methodToKey(m); + if (addressRangeByMethod.containsKey(key)) { + System.err.println("non-null location: " + m.location() + + " with " + length + " bytes"); + //throw new RuntimeException("non-null location: " + + // m.location() + " with " + length + " bytes"); + } + addressRangeByMethod.put(key, range); + if (sectionContainer != null) { + sectionContainer.addSection(m); + } + else { + System.err.println("null sectionContainer"); + } + ramIndex += 0x1000; //bytecodes.length; + } + } + else { + Address start = ram.getAddress(DEFAULT_SECTION); + AddressRangeImpl range = + new AddressRangeImpl(start, start.add(BLOCK_SIZE - 1)); + String key = methodToKey(m); + if (addressRangeByMethod.containsKey(key)) { + throw new RuntimeException("non-null location: " + m.location() + + " with " + length + " bytes"); + } + addressRangeByMethod.put(key, range); + } + } + } + } + } + } + + public void registerConstantPool(JdiModelTargetReferenceType classType, int size) { + if (!addressRangeByClass.containsKey(classType)) { + ReferenceType declaringType = classType.reftype; + if (declaringType instanceof ClassType) { + JdiModelTargetVM targetVM = getTargetVM(declaringType); + if (targetVM.vm.canGetConstantPool()) { + long length = size; + if (length > 0) { + synchronized (poolIndex) { + Address start = pool.getAddress(poolIndex); + length = (length % BLOCK_SIZE + 1) * BLOCK_SIZE; + AddressRangeImpl range = + new AddressRangeImpl(start, start.add(length - 1)); + if (addressRangeByClass.containsKey(classType)) { + System.err.println("non-null location: " + classType + " with " + + length + " bytes"); + } + addressRangeByClass.put(classType, range); + poolIndex += length; //bytecodes.length; + } + } + } + } + } + } + + public JdiModelTargetVM getTargetVM(Mirror mirror) { + return root.vms.getTargetVM(mirror.virtualMachine()); + } + + public AddressRange getAddressRange(Method method) { + if (method == null) { + return defaultRange; + } + AddressRange range = addressRangeByMethod.get(methodToKey(method)); + if (range == null) { + registerMethod(method); + range = addressRangeByMethod.get(methodToKey(method)); + } + return range; + } + + public Method getMethodForAddress(Address address) { + for (String methodName : addressRangeByMethod.keySet()) { + AddressRange range = addressRangeByMethod.get(methodName); + if (range.contains(address)) { + return methodsByKey.get(methodName); + } + } + return null; + } + + public Location getLocation(Address address) { + Method method = getMethodForAddress(address); + long codeIndex = address.subtract(getAddressRange(method).getMinAddress()); + return method.locationOfCodeIndex(codeIndex); + } + + public AddressRange getAddressRange(JdiModelTargetReferenceType classType, int sz) { + AddressRange range = addressRangeByClass.get(classType); + if (range == null) { + registerConstantPool(classType, sz); + range = addressRangeByClass.get(classType); + } + return range; + } + + public AddressFactory getAddressFactory() { + return addressFactory; + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetAttributesContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetAttributesContainer.java new file mode 100644 index 0000000000..ad08c082d1 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetAttributesContainer.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; + +import ghidra.dbg.jdi.manager.JdiEventsListenerAdapter; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +// TODO: Should TargetThreadContainer be a thing? + +public class JdiModelTargetAttributesContainer extends JdiModelTargetObjectImpl + implements JdiEventsListenerAdapter { + + public JdiModelTargetAttributesContainer(JdiModelTargetObject parent, String name) { + super(parent, name); + } + + public void addAttributes(Map attrs) { + setAttributes(List.of(), attrs, "Initialized"); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetBreakpointContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetBreakpointContainer.java new file mode 100644 index 0000000000..fde92b7dc8 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetBreakpointContainer.java @@ -0,0 +1,137 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.Location; + +import ghidra.dbg.jdi.manager.JdiCause; +import ghidra.dbg.jdi.manager.JdiEventsListenerAdapter; +import ghidra.dbg.jdi.manager.breakpoint.JdiBreakpointInfo; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetBreakpointContainer; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.program.model.address.AddressRange; +import ghidra.util.datastruct.WeakValueHashMap; + +public class JdiModelTargetBreakpointContainer extends JdiModelTargetObjectImpl implements + TargetBreakpointContainer, JdiEventsListenerAdapter { + + protected static final TargetBreakpointKindSet SUPPORTED_KINDS = + TargetBreakpointKindSet.of(TargetBreakpointKind.values()); + + protected final Map specsByInfo = + new WeakValueHashMap<>(); + + public JdiModelTargetBreakpointContainer(JdiModelTargetVM vm) { + super(vm, "Breakpoints"); + + impl.getManager().addEventsListener(null, this); + + changeAttributes(List.of(), List.of(), Map.of( // + // TODO: Seems terrible to duplicate this static attribute on each instance + SUPPORTED_BREAK_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS // + ), "Initialized"); + } + + @Override + public void breakpointCreated(JdiBreakpointInfo info, JdiCause cause) { + changeElements(List.of(), List.of(getTargetBreakpointSpec(info)), Map.of(), "Created"); + } + + @Override + public void breakpointModified(JdiBreakpointInfo newInfo, JdiBreakpointInfo oldInfo, + JdiCause cause) { + getTargetBreakpointSpec(oldInfo).updateInfo(oldInfo, newInfo, "Modified"); + } + + @Override + public void breakpointDeleted(JdiBreakpointInfo info, JdiCause cause) { + synchronized (this) { + specsByInfo.remove(info); + } + changeElements(List.of(info.toString()), List.of(), Map.of(), "Deleted"); + } + + @Override + public CompletableFuture placeBreakpoint(AddressRange range, + Set kinds) { + if (kinds.contains(TargetBreakpointKind.SOFTWARE)) { + Location location = impl.getLocation(range.getMinAddress()); + JdiModelTargetLocation targetLocation = + (JdiModelTargetLocation) getTargetObject(location); + if (targetLocation == null) { + targetLocation = new JdiModelTargetLocation(this, location); + } + JdiBreakpointInfo info = targetLocation.addBreakpoint(); + breakpointCreated(info, JdiCause.Causes.UNCLAIMED); + } + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture placeBreakpoint(String expression, + Set kinds) { + JdiModelTargetObject targetObject = getTargetObject(expression); + if (targetObject != null) { + if (kinds.contains(TargetBreakpointKind.SOFTWARE) && + targetObject instanceof JdiModelTargetLocation) { + JdiModelTargetLocation targetLocation = (JdiModelTargetLocation) targetObject; + JdiBreakpointInfo info = targetLocation.addBreakpoint(); + breakpointCreated(info, JdiCause.Causes.UNCLAIMED); + } + if ((kinds.contains(TargetBreakpointKind.READ) || + kinds.contains(TargetBreakpointKind.EXECUTE)) && + targetObject instanceof JdiModelTargetField && targetVM.vm.canWatchFieldAccess()) { + JdiModelTargetField targetField = (JdiModelTargetField) targetObject; + JdiBreakpointInfo info = targetField.addAccessWatchpoint(); + breakpointCreated(info, JdiCause.Causes.UNCLAIMED); + } + if (kinds.contains(TargetBreakpointKind.WRITE) && + targetObject instanceof JdiModelTargetField && + targetVM.vm.canWatchFieldModification()) { + JdiModelTargetField targetField = (JdiModelTargetField) targetObject; + JdiBreakpointInfo info = targetField.addModificationWatchpoint(); + breakpointCreated(info, JdiCause.Causes.UNCLAIMED); + } + } + return CompletableFuture.completedFuture(null); + } + + public synchronized JdiModelTargetBreakpointSpec getTargetBreakpointSpec( + JdiBreakpointInfo info) { + return specsByInfo.computeIfAbsent(info, i -> new JdiModelTargetBreakpointSpec(this, info)); + } + + protected void updateUsingBreakpoints(Map byNumber) { + List specs; + synchronized (this) { + specs = byNumber.values() + .stream() + .map(this::getTargetBreakpointSpec) + .collect(Collectors.toList()); + } + setElements(specs, Map.of(), "Refreshed"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return CompletableFuture.completedFuture(null); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetBreakpointSpec.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetBreakpointSpec.java new file mode 100644 index 0000000000..17343d0d2b --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetBreakpointSpec.java @@ -0,0 +1,144 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.jdi.manager.breakpoint.JdiBreakpointInfo; +import ghidra.dbg.jdi.model.iface1.JdiModelTargetDeletable; +import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet; +import ghidra.dbg.target.TargetBreakpointSpec; +import ghidra.dbg.util.CollectionUtils.Delta; +import ghidra.util.datastruct.ListenerSet; + +public class JdiModelTargetBreakpointSpec extends JdiModelTargetObjectImpl implements // + TargetBreakpointSpec, // + JdiModelTargetDeletable { + + protected JdiBreakpointInfo info; + protected TargetBreakpointKindSet kinds; + + protected final ListenerSet actions = + new ListenerSet<>(TargetBreakpointAction.class) { + // Use strong references on actions + protected Map createMap() { + return Collections.synchronizedMap(new LinkedHashMap<>()); + } + }; + + public JdiModelTargetBreakpointSpec(JdiModelTargetBreakpointContainer breakpoints, + JdiBreakpointInfo info) { + super(breakpoints, info.toString(), info); + } + + @Override + public CompletableFuture delete() { + info.setEnabled(false); + return CompletableFuture.completedFuture(null); + } + + @Override + public boolean isEnabled() { + return info.isEnabled(); + } + + @Override + public String getExpression() { + return ""; + } + + protected TargetBreakpointKindSet computeKinds(JdiBreakpointInfo from) { + switch (from.getType()) { + case BREAKPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.SOFTWARE); + case MODIFICATION_WATCHPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.WRITE); + case ACCESS_WATCHPOINT: + return TargetBreakpointKindSet.of(TargetBreakpointKind.READ, + TargetBreakpointKind.WRITE); + default: + return TargetBreakpointKindSet.of(); + } + } + + @Override + public TargetBreakpointKindSet getKinds() { + return kinds; + } + + @Override + public void addAction(TargetBreakpointAction action) { + actions.add(action); + } + + @Override + public void removeAction(TargetBreakpointAction action) { + actions.remove(action); + } + + protected CompletableFuture getInfo(boolean refresh) { + return CompletableFuture.completedFuture(info); + } + + protected void updateAttributesFromInfo(String reason) { + boolean enabled = info.isEnabled(); + + Delta delta = changeAttributes(List.of(), List.of(), Map.of( // + ENABLED_ATTRIBUTE_NAME, enabled, // + KINDS_ATTRIBUTE_NAME, kinds = computeKinds(info), // + DISPLAY_ATTRIBUTE_NAME, display = getDisplay() // + ), reason); + // TODO: These attribute-specific conveniences should be done by DTO. + if (delta.added.containsKey(ENABLED_ATTRIBUTE_NAME)) { + listeners.fire(TargetBreakpointSpecListener.class).breakpointToggled(this, enabled); + } + if (delta.added.containsKey(DISPLAY_ATTRIBUTE_NAME)) { + listeners.fire.displayChanged(this, display); + } + } + + protected CompletableFuture updateInfo(JdiBreakpointInfo oldInfo, + JdiBreakpointInfo newInfo, String reason) { + this.info = newInfo; + updateAttributesFromInfo(reason); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getInfo(refresh).thenCompose(i -> { + return updateInfo(info, i, "Refreshed"); + }); + } + + @Override + public CompletableFuture disable() { + info.setEnabled(false); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture enable() { + info.setEnabled(true); + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + return info == null ? super.getDisplay() : info.toString(); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetClassContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetClassContainer.java new file mode 100644 index 0000000000..54ae68300e --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetClassContainer.java @@ -0,0 +1,95 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.ReferenceType; + +import ghidra.async.AsyncFence; +import ghidra.async.AsyncUtils; +import ghidra.util.Msg; + +public class JdiModelTargetClassContainer extends JdiModelTargetObjectImpl { + + protected final JdiModelTargetVM vm; + + // TODO: Is it possible to load the same object twice? + private final Map classesByName = new HashMap<>(); + + public JdiModelTargetClassContainer(JdiModelTargetVM vm) { + super(vm, "Classes"); + this.vm = vm; + + requestElements(true); + } + + protected CompletableFuture updateUsingClasses(Map byName) { + List classes; + synchronized (this) { + classes = + byName.values().stream().map(this::getTargetClass).collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetReferenceType c : classes) { + fence.include(c.init()); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), classes, Map.of(), "Refreshed"); + }); + } + + @Override + protected CompletableFuture requestElements(boolean refresh) { + // Ignore 'refresh' because inferior.getKnownModules may exclude executable + return doRefresh(); + } + + protected CompletableFuture doRefresh() { + Map map = new HashMap<>(); + List allClasses = vm.vm.allClasses(); + for (ReferenceType ref : allClasses) { + map.put(ref.name(), ref); + } + getClassesByName().keySet().retainAll(map.keySet()); + return updateUsingClasses(map); + } + + protected synchronized JdiModelTargetReferenceType getTargetClass(ReferenceType reftype) { + return getClassesByName().computeIfAbsent(reftype.name(), + n -> (JdiModelTargetReferenceType) getInstance(reftype)); + } + + public synchronized JdiModelTargetReferenceType getTargetModuleIfPresent(String name) { + return getClassesByName().get(name); + } + + public CompletableFuture refresh() { + if (!isObserved()) { + return AsyncUtils.NIL; + } + return doRefresh().exceptionally(ex -> { + Msg.error(this, "Problem refreshing vm's classes", ex); + return null; + }); + } + + public Map getClassesByName() { + return classesByName; + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConnector.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConnector.java new file mode 100644 index 0000000000..2e0139a729 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConnector.java @@ -0,0 +1,86 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.connect.Connector; +import com.sun.jdi.connect.Connector.Argument; + +import ghidra.dbg.jdi.model.iface1.JdiModelSelectableObject; +import ghidra.dbg.jdi.model.iface1.JdiModelTargetLauncher; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; + +public class JdiModelTargetConnector extends JdiModelTargetObjectImpl + implements JdiModelSelectableObject, + // TODO: Make a JidModelTargetLaunchingConnector and JdiModelTargetAttachingConnector + JdiModelTargetLauncher { + + protected final JdiModelTargetConnectorContainer connectors; + protected final Connector cx; + protected final Map> paramDescs; + + public JdiModelTargetConnector(JdiModelTargetConnectorContainer connectors, Connector cx) { + super(connectors, cx.name(), cx); + this.connectors = connectors; + this.cx = cx; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, cx.name(), // + "Description", cx.description(), // + "Default Arguments", cx.defaultArguments(), // + "Transport", cx.transport(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, paramDescs = computeParameters(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public CompletableFuture init() { + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + return cx == null ? super.getDisplay() : cx.name(); + } + + @Override + public CompletableFuture select() { + connectors.setDefaultConnector(this); + return CompletableFuture.completedFuture(null); + } + + protected Map> computeParameters() { + return JdiModelTargetLauncher.getParameters(cx.defaultArguments()); + } + + @Override + public TargetParameterMap getParameters() { + return TargetMethod.getParameters(this); + } + + @Override + public CompletableFuture launch(Map args) { + Map jdiArgs = + JdiModelTargetLauncher.getArguments(cx.defaultArguments(), paramDescs, args); + return getManager().addVM(cx, jdiArgs).thenApply(__ -> null); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConnectorContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConnectorContainer.java new file mode 100644 index 0000000000..b8f08ec742 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConnectorContainer.java @@ -0,0 +1,106 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.VirtualMachineManager; +import com.sun.jdi.connect.Connector; + +import ghidra.async.AsyncFence; +import ghidra.async.AsyncUtils; +import ghidra.util.Msg; + +public class JdiModelTargetConnectorContainer extends JdiModelTargetObjectImpl { + + protected final JdiModelTargetRoot root; + + // TODO: Is it possible to load the same object twice? + protected final Map connectorsByName = new HashMap<>(); + private JdiModelTargetConnector defaultConnector; + + public JdiModelTargetConnectorContainer(JdiModelTargetRoot root) { + super(root, "Connectors"); + this.root = root; + } + + protected CompletableFuture updateUsingConnectors(Map byName) { + List connectors; + synchronized (this) { + connectors = + byName.values().stream().map(this::getTargetConnector).collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetConnector c : connectors) { + fence.include(c.init()); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), connectors, Map.of(), "Refreshed"); + }); + } + + @Override + protected CompletableFuture requestElements(boolean refresh) { + // Ignore 'refresh' because inferior.getKnownModules may exclude executable + return doRefresh(); + } + + protected CompletableFuture doRefresh() { + Map map = new HashMap<>(); + VirtualMachineManager vmm = impl.getManager().getVirtualMachineManager(); + List allConnectors = vmm.allConnectors(); + for (Connector cx : allConnectors) { + map.put(cx.name(), cx); + } + connectorsByName.keySet().retainAll(map.keySet()); + return updateUsingConnectors(map); + } + + protected synchronized JdiModelTargetConnector getTargetConnector(Connector cx) { + return connectorsByName.computeIfAbsent(cx.name(), + n -> new JdiModelTargetConnector(this, cx)); + } + + public synchronized JdiModelTargetConnector getTargetConnectorIfPresent(String name) { + for (String key : connectorsByName.keySet()) { + if (key.contains(name)) { + return connectorsByName.get(key); + } + } + return null; + } + + public CompletableFuture refresh() { + if (!isObserved()) { + return AsyncUtils.NIL; + } + return doRefresh().exceptionally(ex -> { + Msg.error(this, "Problem refreshing inferior's modules", ex); + return null; + }); + } + + public JdiModelTargetConnector getDefaultConnector() { + return defaultConnector; + } + + public void setDefaultConnector(JdiModelTargetConnector defaultConnector) { + this.defaultConnector = defaultConnector; + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConstantPool.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConstantPool.java new file mode 100644 index 0000000000..12ecb3a601 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetConstantPool.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.target.TargetMemoryRegion; +import ghidra.dbg.target.TargetSection; +import ghidra.program.model.address.AddressRange; + +public class JdiModelTargetConstantPool extends JdiModelTargetObjectImpl implements // + //TargetMemory, + TargetMemoryRegion, TargetSection { + + private AddressRange range; + private byte[] pool; + + public JdiModelTargetConstantPool(JdiModelTargetSectionContainer parent, byte[] pool) { + super(parent, "Constant Pool", pool); + this.pool = pool; + + this.range = impl.getAddressRange(getClassType(), pool.length); + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + MODULE_ATTRIBUTE_NAME, parent.getClassType(), // + READABLE_ATTRIBUTE_NAME, true, // + MEMORY_ATTRIBUTE_NAME, parent, TargetMemoryRegion.RANGE_ATTRIBUTE_NAME, range, // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + this.range = impl.getAddressRange(getClassType(), pool.length); + if (range != null) { + changeAttributes(List.of(), List.of(), Map.of(), "Initialized"); + } + + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture init() { + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + JdiModelTargetReferenceType classType = getClassType(); + return classType.getName() + ": ConstPool" + range; + } + + private JdiModelTargetReferenceType getClassType() { + JdiModelTargetReferenceType classType = + ((JdiModelTargetSectionContainer) parent).getClassType(); + return classType; + } + + @Override + public AddressRange getRange() { + return range; + } + + public byte[] getPool() { + return pool; + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetElementsContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetElementsContainer.java new file mode 100644 index 0000000000..9426c0a008 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetElementsContainer.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; + +import com.sun.jdi.ThreadGroupReference; + +import ghidra.dbg.jdi.manager.JdiEventsListenerAdapter; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; +import ghidra.util.datastruct.WeakValueHashMap; + +// TODO: Should TargetThreadContainer be a thing? +public class JdiModelTargetElementsContainer extends JdiModelTargetObjectImpl + implements JdiEventsListenerAdapter { + + protected static String keyGroup(ThreadGroupReference group) { + return PathUtils.makeKey(group.name()); + } + + protected final Map threadGroupsById = + new WeakValueHashMap<>(); + + public JdiModelTargetElementsContainer(JdiModelTargetObject parent, String name) { + super(parent, name); + } + + public void addElements(List els) { + setElements(els, Map.of(), "Initialized"); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetField.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetField.java new file mode 100644 index 0000000000..6bcf7d5b5d --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetField.java @@ -0,0 +1,121 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.ClassNotLoadedException; +import com.sun.jdi.Field; +import com.sun.jdi.request.*; + +import ghidra.dbg.jdi.manager.breakpoint.JdiBreakpointInfo; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public class JdiModelTargetField extends JdiModelTargetObjectImpl { + + protected final Field field; + + private JdiModelTargetType type; + private JdiModelTargetReferenceType declaringType; + private JdiModelTargetAttributesContainer addedAttributes; + + public JdiModelTargetField(JdiModelTargetObject fields, Field field) { + super(fields, field.toString(), field); + this.field = field; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + TYPE_ATTRIBUTE_NAME, field.typeName(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + private void populateAttributes() { + this.addedAttributes = new JdiModelTargetAttributesContainer(this, "Attributes"); + Map attrs = new HashMap<>(); + attrs.put("isEnumConstant", field.isEnumConstant()); + attrs.put("isFinal", field.isFinal()); + try { + attrs.put("isPackagePrivate", field.isPackagePrivate()); + attrs.put("isPrivate", field.isPrivate()); + attrs.put("isProtected", field.isProtected()); + attrs.put("isPublic", field.isPublic()); + } + catch (Exception e) { + if (e instanceof ClassNotLoadedException) { + attrs.put("status", "Class not loaded"); + } + } + attrs.put("isStatic", field.isStatic()); + attrs.put("isSynthetic", field.isSynthetic()); + attrs.put("isTransient", field.isTransient()); + attrs.put("isVolatile", field.isVolatile()); + attrs.put("modifiers", field.modifiers()); + addedAttributes.addAttributes(attrs); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + this.declaringType = (JdiModelTargetReferenceType) getInstance(field.declaringType()); + + populateAttributes(); + + changeAttributes(List.of(), List.of( // + addedAttributes // + ), Map.of( // + "Declaring Type", declaringType // + ), "Initialized"); + + try { + this.type = (JdiModelTargetType) getInstance(field.type()); + if (type != null) { + changeAttributes(List.of(), List.of(), Map.of( // + "Type", type // + ), "Initialized"); + } + } + catch (ClassNotLoadedException e) { + // Ignore + } + return CompletableFuture.completedFuture(null); + } + + public CompletableFuture init() { + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + return field == null ? super.getDisplay() : field.name(); + } + + public JdiBreakpointInfo addAccessWatchpoint() { + EventRequestManager eventManager = field.virtualMachine().eventRequestManager(); + AccessWatchpointRequest request = eventManager.createAccessWatchpointRequest(field); + request.enable(); + return new JdiBreakpointInfo(request); + } + + public JdiBreakpointInfo addModificationWatchpoint() { + EventRequestManager eventManager = field.virtualMachine().eventRequestManager(); + ModificationWatchpointRequest request = + eventManager.createModificationWatchpointRequest(field); + request.enable(); + return new JdiBreakpointInfo(request); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetFieldContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetFieldContainer.java new file mode 100644 index 0000000000..87146adb8f --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetFieldContainer.java @@ -0,0 +1,75 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.Field; + +import ghidra.async.AsyncFence; + +public class JdiModelTargetFieldContainer extends JdiModelTargetObjectImpl { + // NOTE: -file-list-shared-libraries omits the main module and system-supplied DSO. + + protected final JdiModelTargetReferenceType reftype; + + // TODO: Is it possible to load the same object twice? + protected final Map fieldsByName = new HashMap<>(); + private boolean useAll; + + public JdiModelTargetFieldContainer(JdiModelTargetReferenceType reftype, boolean all) { + super(reftype, all ? "Fields (All)" : "Fields"); + this.reftype = reftype; + this.useAll = all; + } + + protected CompletableFuture updateUsingFields(Map byName) { + List fields; + synchronized (this) { + fields = + byName.values().stream().map(this::getTargetField).collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetField m : fields) { + fence.include(m.init()); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), fields, Map.of(), "Refreshed"); + }); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + Map map = new HashMap<>(); + List fields = useAll ? reftype.reftype.allFields() : reftype.reftype.fields(); + for (Field f : fields) { + map.put(f.name(), f); + } + fieldsByName.keySet().retainAll(map.keySet()); + return updateUsingFields(map); + } + + protected synchronized JdiModelTargetField getTargetField(Field var) { + return fieldsByName.computeIfAbsent(var.name(), + n -> (JdiModelTargetField) getInstance(var)); + } + + public synchronized JdiModelTargetField getTargetFieldIfPresent(String name) { + return fieldsByName.get(name); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocalVariable.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocalVariable.java new file mode 100644 index 0000000000..785b3309fc --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocalVariable.java @@ -0,0 +1,90 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.ClassNotLoadedException; +import com.sun.jdi.LocalVariable; + +public class JdiModelTargetLocalVariable extends JdiModelTargetObjectImpl { + + String IS_ARGUMENT_ATTRIBUTE_NAME = "IsArg"; + String VISIBLE_TYPE_ATTRIBUTE_NAME = "Type"; + + protected final LocalVariable var; + private JdiModelTargetAttributesContainer addedAttributes; + + public JdiModelTargetLocalVariable(JdiModelTargetLocalVariableContainer variables, + LocalVariable var) { + super(variables, var.name(), var); + this.var = var; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, var.name(), // + VISIBLE_TYPE_ATTRIBUTE_NAME, var.typeName(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + + } + + private void populateAttributes() { + this.addedAttributes = new JdiModelTargetAttributesContainer(this, "Attributes"); + Map attrs = new HashMap<>(); + attrs.put("isArgument", var.isArgument()); + addedAttributes.addAttributes(attrs); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + populateAttributes(); + + changeAttributes(List.of(), List.of( // + addedAttributes // + ), Map.of( // + "Signature", var.signature() // + ), "Initialized"); + + try { + JdiModelTargetType type = (JdiModelTargetType) getInstance(var.type()); + changeAttributes(List.of(), List.of(), Map.of( // + "Type", type // + ), "Initialized"); + } + catch (ClassNotLoadedException e) { + // Ignore + } + String genericSignature = var.genericSignature(); + if (genericSignature != null) { + changeAttributes(List.of(), List.of(), Map.of( // + "Generic Signature", genericSignature // + ), "Initialized"); + } + return CompletableFuture.completedFuture(null); + } + + public CompletableFuture init() { + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + return var == null ? super.getDisplay() : var.name(); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocalVariableContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocalVariableContainer.java new file mode 100644 index 0000000000..6932a80b33 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocalVariableContainer.java @@ -0,0 +1,78 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.LocalVariable; + +import ghidra.async.AsyncFence; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public class JdiModelTargetLocalVariableContainer extends JdiModelTargetObjectImpl { + + private List vars; + + // TODO: Is it possible to load the same object twice? + protected final Map variablesByName = new HashMap<>(); + + public JdiModelTargetLocalVariableContainer(JdiModelTargetObject parent, String name, + List vars) { + super(parent, name); + this.vars = vars; + } + + protected CompletableFuture updateUsingVariables(Map byName) { + List locations; + synchronized (this) { + locations = + byName.values().stream().map(this::getTargetVariable).collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetLocalVariable loc : locations) { + fence.include(loc.init()); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), locations, Map.of(), "Refreshed"); + }); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + Map map = new HashMap<>(); + try { + for (LocalVariable var : vars) { + map.put(var.name(), var); + } + variablesByName.keySet().retainAll(map.keySet()); + } + catch (Exception e) { + e.printStackTrace(); + } + return updateUsingVariables(map); + } + + protected synchronized JdiModelTargetLocalVariable getTargetVariable(LocalVariable var) { + return variablesByName.computeIfAbsent(var.name(), + n -> new JdiModelTargetLocalVariable(this, var)); + } + + public synchronized JdiModelTargetLocalVariable getTargetVariableIfPresent(String name) { + return variablesByName.get(name); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocation.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocation.java new file mode 100644 index 0000000000..da40f329d6 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocation.java @@ -0,0 +1,114 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.AbsentInformationException; +import com.sun.jdi.Location; +import com.sun.jdi.request.BreakpointRequest; +import com.sun.jdi.request.EventRequestManager; + +import ghidra.dbg.jdi.manager.breakpoint.JdiBreakpointInfo; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressRange; + +public class JdiModelTargetLocation extends JdiModelTargetObjectImpl { + + public static String getUniqueId(Location obj) { + return obj.toString() + ":" + obj.codeIndex(); + } + + protected final Location location; + private JdiModelTargetReferenceType declaringType; + private Address address; + + public JdiModelTargetLocation(JdiModelTargetObject parent, Location location) { + super(parent, getUniqueId(location), location); + this.location = location; + + impl.registerMethod(location.method()); + + this.address = getAddress(); + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + "Method", location.method().name(), // + "Line", location.lineNumber(), // + "Index", location.codeIndex(), // + "Address", Long.toHexString(address.getOffset()), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + this.declaringType = (JdiModelTargetReferenceType) getInstance(location.declaringType()); + + changeAttributes(List.of(), List.of(), Map.of( // + "Declaring Type", declaringType // + ), "Initialized"); + + try { + String sourceName = location.sourceName(); + String sourcePath = location.sourcePath(); + changeAttributes(List.of(), List.of(), Map.of( // + "Source Name", sourceName, // + "Source Path", sourcePath // + ), "Initialized"); + } + catch (AbsentInformationException e) { + // Ignore + } + return CompletableFuture.completedFuture(null); + } + + public CompletableFuture init() { + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + return location == null ? super.getDisplay() + : location.toString() + " [" + Long.toHexString(address.getOffset()) + "]"; + } + + public Address getAddress() { + if (address != null) { + return address; + } + getInstance(location.method()); + AddressRange addressRange = impl.getAddressRange(location.method()); + if (addressRange == null) { + return impl.getAddressSpace("ram").getAddress(-1L); + } + long codeIndex = location.codeIndex(); + return addressRange.getMinAddress().add(codeIndex < 0 ? 0 : codeIndex); + } + + public JdiBreakpointInfo addBreakpoint() { + EventRequestManager eventManager = location.virtualMachine().eventRequestManager(); + BreakpointRequest request = eventManager.createBreakpointRequest(location); + request.enable(); + return new JdiBreakpointInfo(request); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocationContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocationContainer.java new file mode 100644 index 0000000000..01ad6b5d9b --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetLocationContainer.java @@ -0,0 +1,75 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.Location; + +import ghidra.async.AsyncFence; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public class JdiModelTargetLocationContainer extends JdiModelTargetObjectImpl { + + private List locations; + + // TODO: Is it possible to load the same object twice? + protected final Map locationsByName = new HashMap<>(); + + public JdiModelTargetLocationContainer(JdiModelTargetObject parent, String name, + List locations) { + super(parent, name); + this.locations = locations; + } + + protected CompletableFuture updateUsingLocations(Map byName) { + List locs; + synchronized (this) { + locs = + byName.values().stream().map(this::getTargetLocation).collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetLocation loc : locs) { + fence.include(loc.init()); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), locs, Map.of(), "Refreshed"); + }); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + Map map = new HashMap<>(); + if (locations != null) { + for (Location loc : locations) { + map.put(loc.toString(), loc); + } + } + locationsByName.keySet().retainAll(map.keySet()); + return updateUsingLocations(map); + } + + protected synchronized JdiModelTargetLocation getTargetLocation(Location loc) { + return locationsByName.computeIfAbsent(loc.toString(), + n -> new JdiModelTargetLocation(this, loc)); + } + + public synchronized JdiModelTargetLocation getTargetLocationsIfPresent(String name) { + return locationsByName.get(name); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetMethod.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetMethod.java new file mode 100644 index 0000000000..765f9ae183 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetMethod.java @@ -0,0 +1,167 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.*; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public class JdiModelTargetMethod extends JdiModelTargetObjectImpl { + + protected final Method method; + + private JdiModelTargetLocation location; + private JdiModelTargetAttributesContainer addedAttributes; + private JdiModelTargetTypeContainer argumentTypes; + private JdiModelTargetLocalVariableContainer arguments; + private JdiModelTargetLocationContainer locations; + private JdiModelTargetLocalVariableContainer variables; + private JdiModelTargetType returnType; + + public JdiModelTargetMethod(JdiModelTargetObject parent, Method method) { + super(parent, method.toString(), method); + this.method = method; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + + } + + private void populateAttributes() { + this.addedAttributes = new JdiModelTargetAttributesContainer(this, "Attributes"); + Map attrs = new HashMap<>(); + attrs.put("isAbstract", method.isAbstract()); + attrs.put("isBridge", method.isBridge()); + attrs.put("isStatic", method.isStatic()); + attrs.put("isConstructor", method.isConstructor()); + attrs.put("isDefault", method.isDefault()); + attrs.put("isFinal", method.isFinal()); + attrs.put("isNative", method.isNative()); + attrs.put("isObsolete", method.isObsolete()); + attrs.put("isStatic", method.isStatic()); + attrs.put("isStaticInitializer", method.isStaticInitializer()); + attrs.put("isSynchronized", method.isSynchronized()); + attrs.put("isSynthetic", method.isSynthetic()); + try { + attrs.put("isPackagePrivate", method.isPackagePrivate()); + attrs.put("isPrivate", method.isPrivate()); + attrs.put("isProtected", method.isProtected()); + attrs.put("isPublic", method.isPublic()); + } + catch (Exception e) { + if (e instanceof ClassNotLoadedException) { + attrs.put("status", "Class not loaded"); + } + } + addedAttributes.addAttributes(attrs); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + populateAttributes(); + + changeAttributes(List.of(), List.of( // + addedAttributes // + ), Map.of("Argument Types", method.argumentTypeNames(), // + "Return Type", method.returnTypeName() // + ), "Initialized"); + + this.location = method.location() == null ? null + : new JdiModelTargetLocation(parent, method.location()); + if (location != null) { + changeAttributes(List.of(), List.of( // + location // + ), Map.of(), "Initialized"); + } + try { + this.arguments = + new JdiModelTargetLocalVariableContainer(this, "Arguments", method.arguments()); + if (arguments != null) { + changeAttributes(List.of(), List.of( // + arguments // + ), Map.of(), "Initialized"); + } + } + catch (AbsentInformationException e) { + // Ignore + } + try { + this.argumentTypes = + new JdiModelTargetTypeContainer(this, "Argument Types", method.argumentTypes()); + if (argumentTypes != null) { + changeAttributes(List.of(), List.of( // + argumentTypes // + ), Map.of(), "Initialized"); + } + } + catch (ClassNotLoadedException e) { + // Ignore + } + try { + locations = + new JdiModelTargetLocationContainer(this, "Locations", method.allLineLocations()); + if (locations != null) { + changeAttributes(List.of(), List.of( // + locations // + ), Map.of(), "Initialized"); + } + } + catch (AbsentInformationException e) { + // Ignore + } + try { + returnType = (JdiModelTargetType) getInstance(method.returnType()); + if (returnType != null) { + changeAttributes(List.of(), List.of( // + returnType // + ), Map.of(), "Initialized"); + } + } + catch (ClassNotLoadedException e) { + // Ignore + } + try { + this.variables = + new JdiModelTargetLocalVariableContainer(this, "Variables", method.variables()); + if (variables != null) { + changeAttributes(List.of(), List.of( // + variables // + ), Map.of(), "Initialized"); + } + } + catch (AbsentInformationException e) { + // Ignore + } + + return CompletableFuture.completedFuture(null); + } + + public CompletableFuture init() { + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + return method == null ? super.getDisplay() : method.name(); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetMethodContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetMethodContainer.java new file mode 100644 index 0000000000..8ff177a765 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetMethodContainer.java @@ -0,0 +1,86 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.Method; + +import ghidra.async.AsyncFence; + +public class JdiModelTargetMethodContainer extends JdiModelTargetObjectImpl { + + protected final JdiModelTargetReferenceType reftype; + + private final Map methodsByName = new HashMap<>(); + private boolean useAll; + + public JdiModelTargetMethodContainer(JdiModelTargetReferenceType reftype, boolean all) { + super(reftype, all ? "Methods (All)" : "Methods"); + this.reftype = reftype; + this.useAll = all; + } + + protected CompletableFuture updateUsingMethods(Map byName) { + List methods; + synchronized (this) { + methods = + byName.values().stream().map(this::getTargetMethod).collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetMethod m : methods) { + fence.include(m.init()); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), methods, Map.of(), "Refreshed"); + }); + } + + @Override + protected CompletableFuture requestElements(boolean refresh) { + Map map = new HashMap<>(); + List methods = useAll ? reftype.reftype.allMethods() : reftype.reftype.methods(); + for (Method var : methods) { + map.put(var.name(), var); + } + getMethodsByName().keySet().retainAll(map.keySet()); + return updateUsingMethods(map); + } + + protected synchronized JdiModelTargetMethod getTargetMethod(Method method) { + return getMethodsByName().computeIfAbsent(method.name(), + n -> (JdiModelTargetMethod) getInstance(method)); + } + + public synchronized JdiModelTargetMethod getTargetMethodIfPresent(String name) { + return getMethodsByName().get(name); + } + + @Override + public CompletableFuture init() { + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetMethod method : methodsByName.values()) { + fence.include(method.init()); + } + return fence.ready(); + } + + public Map getMethodsByName() { + return methodsByName; + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetModule.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetModule.java new file mode 100644 index 0000000000..f3fcc5fa19 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetModule.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.ModuleReference; + +public class JdiModelTargetModule extends JdiModelTargetObjectReference { + + public static String getUniqueId(ModuleReference module) { + return module.name() == null ? "#" + module.hashCode() : module.name(); + } + + protected final ModuleReference module; + + ///protected final JdiModelTargetSymbolContainer symbols; + + public JdiModelTargetModule(JdiModelTargetModuleContainer modules, ModuleReference module) { + super(modules, module); + this.module = module; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getUniqueId(module), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + public CompletableFuture init() { + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + return module == null ? super.getDisplay() : getUniqueId(module); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetModuleContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetModuleContainer.java new file mode 100644 index 0000000000..4deccb3bf2 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetModuleContainer.java @@ -0,0 +1,124 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.ModuleReference; + +import ghidra.async.AsyncFence; +import ghidra.async.AsyncUtils; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetModuleContainer; +import ghidra.lifecycle.Internal; +import ghidra.util.Msg; + +public class JdiModelTargetModuleContainer extends JdiModelTargetObjectImpl + implements TargetModuleContainer { + + protected final JdiModelTargetVM vm; + + // TODO: Is it possible to load the same object twice? + protected final Map modulesByName = new HashMap<>(); + + public JdiModelTargetModuleContainer(JdiModelTargetVM vm) { + super(vm, "Modules"); + this.vm = vm; + } + + @Internal + public JdiModelTargetModule libraryLoaded(String name) { + List allModules = vm.vm.allModules(); + for (ModuleReference ref : allModules) { + if (JdiModelTargetModule.getUniqueId(ref).equals(name)) { + JdiModelTargetModule module = getTargetModule(ref); + changeElements(List.of(), List.of(module), Map.of(), "Loaded"); + return module; + } + } + return null; + } + + @Internal + public void libraryUnloaded(String name) { + synchronized (this) { + modulesByName.remove(name); + } + changeElements(List.of(name), List.of(), Map.of(), "Unloaded"); + } + + @Override + public boolean supportsSyntheticModules() { + return false; + } + + @Override + public CompletableFuture> addSyntheticModule(String name) { + throw new DebuggerUserException("GDB Does not support synthetic modules"); + } + + protected CompletableFuture updateUsingModules(Map byName) { + List modules; + synchronized (this) { + modules = + byName.values().stream().map(this::getTargetModule).collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetModule mod : modules) { + fence.include(mod.init()); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), modules, Map.of(), "Refreshed"); + }); + } + + @Override + protected CompletableFuture requestElements(boolean refresh) { + return doRefresh(); + } + + protected CompletableFuture doRefresh() { + Map map = new HashMap<>(); + List allModules = vm.vm.allModules(); + for (ModuleReference ref : allModules) { + map.put(JdiModelTargetModule.getUniqueId(ref), ref); + } + modulesByName.keySet().retainAll(map.keySet()); + return updateUsingModules(map); + } + + protected synchronized JdiModelTargetModule getTargetModule(ModuleReference module) { + return modulesByName.computeIfAbsent(JdiModelTargetModule.getUniqueId(module), + n -> new JdiModelTargetModule(this, module)); + } + + public synchronized JdiModelTargetModule getTargetModuleIfPresent(String name) { + return modulesByName.get(name); + } + + public CompletableFuture refresh() { + if (!isObserved()) { + return AsyncUtils.NIL; + } + return doRefresh().exceptionally(ex -> { + Msg.error(this, "Problem refreshing inferior's modules", ex); + return null; + }); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectImpl.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectImpl.java new file mode 100644 index 0000000000..bd0dbf8232 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectImpl.java @@ -0,0 +1,181 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.Mirror; +import com.sun.jdi.ThreadReference; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.jdi.manager.JdiCause; +import ghidra.dbg.jdi.manager.JdiStateListener; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; + +public class JdiModelTargetObjectImpl extends + DefaultTargetObject implements JdiModelTargetObject { + + public static String keyObject(String id) { + return PathUtils.makeKey(id); + } + + protected final JdiModelImpl impl; + protected final Mirror mirror; + protected final Object object; + protected String display; + + protected final JdiStateListener accessListener = this::checkExited; + protected JdiModelTargetVM targetVM; + private boolean modified; + + public JdiModelTargetObjectImpl(JdiModelTargetObject parent, String id) { + super(parent.getModel(), parent, id, "Object"); + this.impl = parent.getModelImpl(); + this.mirror = (Mirror) parent.getObject(); + this.object = null; + this.display = id; + + if (mirror != null) { + targetVM = impl.getTargetVM(mirror); + targetVM.setTargetObject(parent + ":" + id, null, this); + } + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, display = getDisplay(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + public JdiModelTargetObjectImpl(JdiModelTargetObject parent, String id, Object object) { + super(parent.getModel(), parent, keyObject(id), "Object"); + this.impl = parent.getModelImpl(); + this.mirror = object instanceof Mirror ? (Mirror) object : null; + this.object = object; + this.display = id; + + if (mirror != null) { + if (this instanceof JdiModelTargetVM) { + targetVM = (JdiModelTargetVM) this; + } + else { + targetVM = impl.getTargetVM(mirror); + } + targetVM.setTargetObject(id, object == null ? id : object, this); + } + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, display = getDisplay(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + public JdiModelTargetObjectImpl(JdiModelTargetSectionContainer parent) { + super(parent.getModel(), parent, keyObject("NULL_SPACE"), "Object"); + this.impl = parent.getModelImpl(); + this.mirror = parent.mirror; + this.display = "NULL_SPACE"; + this.object = display; + } + + public CompletableFuture init() { + return CompletableFuture.completedFuture(null); + } + + @Override + public JdiModelImpl getModelImpl() { + return impl; + } + + @Override + public String getDisplay() { + return display; + } + + @Override + public Object getObject() { + return object; + } + + @Override + public JdiModelTargetObject getTargetObject(Object obj) { + if (targetVM != null) { + return targetVM.getTargetObject(obj); + } + System.err.println("Attempt to getTargetObject from class without Mirror " + this); + return null; + } + + protected void checkExited(Integer state, JdiCause cause) { + switch (state) { + case ThreadReference.THREAD_STATUS_NOT_STARTED: { + break; + } + case ThreadReference.THREAD_STATUS_MONITOR: { + break; + } + case ThreadReference.THREAD_STATUS_WAIT: { + onStopped(); + break; + } + case ThreadReference.THREAD_STATUS_ZOMBIE: { + break; + } + case ThreadReference.THREAD_STATUS_SLEEPING: { + break; + } + case ThreadReference.THREAD_STATUS_RUNNING: { + resetModified(); + onRunning(); + break; + } + case ThreadReference.THREAD_STATUS_UNKNOWN: { + break; + } + } + } + + protected void onRunning() { + // Nothing to do here + } + + protected void onStopped() { + Map existingAttributes = getCachedAttributes(); + Boolean autoupdate = (Boolean) existingAttributes.get("autoupdate"); + if (autoupdate != null && autoupdate) { + requestAttributes(true); + requestElements(true); + } + } + + public void setModified(boolean modified) { + if (modified) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, modified // + ), "Refreshed"); + listeners.fire.displayChanged(this, getDisplay()); + } + } + + public void resetModified() { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, false // + ), "Refreshed"); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectReference.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectReference.java new file mode 100644 index 0000000000..c0bea1f9fd --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectReference.java @@ -0,0 +1,122 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.*; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public class JdiModelTargetObjectReference extends JdiModelTargetValue { + + private static final long MAX_REFERRERS = 100; + + protected final ObjectReference objref; + + protected JdiModelTargetThread owner; + private JdiModelTargetReferenceType referenceType; + + public JdiModelTargetObjectReference(JdiModelTargetObject object, ObjectReference objref) { + this(object, Long.toHexString(objref.uniqueID()), objref); + } + + public JdiModelTargetObjectReference(JdiModelTargetObject object, String id, + ObjectReference objref) { + super(object, id, objref); + this.objref = objref; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + "UID", objref.uniqueID(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + this.referenceType = (JdiModelTargetReferenceType) getInstance(objref.referenceType()); + + changeAttributes(List.of(), List.of(), Map.of( // + referenceType.getName(), referenceType // + ), "Initialized"); + + try { + ThreadReference owningThread = objref.owningThread(); + if (owningThread != null) { + owner = (JdiModelTargetThread) getInstance(owningThread); + changeAttributes(List.of(), List.of(), Map.of( // + "Owner", owner // + ), "Initialized"); + } + } + catch (IncompatibleThreadStateException e) { + // Ignore + } + try { + List waitingThreads = objref.waitingThreads(); + if (waitingThreads != null) { + JdiModelTargetThreadContainer targetWaitingThreads = + new JdiModelTargetThreadContainer(this, "Waiting Threads", waitingThreads); + changeAttributes(List.of(), List.of( // + targetWaitingThreads // + ), Map.of(), "Initialized"); + } + } + catch (IncompatibleThreadStateException e) { + // Ignore + } + try { + JdiModelTargetObjectReferenceContainer referringObjects = + new JdiModelTargetObjectReferenceContainer(this, "Referring Objects", + objref.referringObjects(MAX_REFERRERS)); + changeAttributes(List.of(), List.of( // + referringObjects // + ), Map.of(), "Initialized"); + } + catch (UnsupportedOperationException e) { + // Ignore + } + catch (IllegalArgumentException e) { + // Ignore + } + try { + int entryCount = objref.entryCount(); + changeAttributes(List.of(), List.of(), Map.of( // + "Entry Count", entryCount // + ), "Initialized"); + } + catch (IncompatibleThreadStateException e) { + // Ignore + } + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture init() { + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + return objref == null ? super.getDisplay() : objref.toString(); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectReferenceContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectReferenceContainer.java new file mode 100644 index 0000000000..b100cf1bdf --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetObjectReferenceContainer.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.ObjectReference; + +import ghidra.async.AsyncFence; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public class JdiModelTargetObjectReferenceContainer extends JdiModelTargetObjectImpl { + + protected final List refs; + + // TODO: Is it possible to load the same object twice? + protected final Map objectsByName = new HashMap<>(); + + public JdiModelTargetObjectReferenceContainer(JdiModelTargetObject parent, String name, + List refs) { + super(parent, name); + this.refs = refs; + } + + protected CompletableFuture updateUsingReferences(Map byName) { + List objects; + synchronized (this) { + objects = + byName.values().stream().map(this::getTargetObject).collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetObjectReference m : objects) { + fence.include(m.init()); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), objects, Map.of(), "Refreshed"); + }); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + Map map = new HashMap<>(); + for (ObjectReference ref : refs) { + map.put(ref.toString(), ref); + } + objectsByName.keySet().retainAll(map.keySet()); + return updateUsingReferences(map); + } + + protected synchronized JdiModelTargetObjectReference getTargetObject(ObjectReference ref) { + return objectsByName.computeIfAbsent(ref.toString(), + n -> (JdiModelTargetObjectReference) getInstance(ref)); + } + + public synchronized JdiModelTargetObjectReference getTargetObjectIfPresent(String name) { + return objectsByName.get(name); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetProcess.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetProcess.java new file mode 100644 index 0000000000..1068f57319 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetProcess.java @@ -0,0 +1,126 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.io.*; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.jdi.manager.JdiConsoleOutputListener; +import ghidra.dbg.jdi.model.iface1.JdiModelSelectableObject; +import ghidra.dbg.jdi.model.iface1.JdiModelTargetConsole; +import ghidra.dbg.target.TargetConsole; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.dbg.target.TargetInterpreter.TargetInterpreterListener; +import ghidra.lifecycle.Internal; +import ghidra.util.Msg; + +public class JdiModelTargetProcess extends JdiModelTargetObjectImpl implements // + JdiModelTargetConsole, // + JdiConsoleOutputListener, // + JdiModelSelectableObject { + + public static String getUniqueId(Process obj) { + return Long.toHexString(obj.pid()); + } + + String STATE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "state"; + + protected final Process process; + + //private PrintWriter writer; + private Thread input; + private Thread error; + + public JdiModelTargetProcess(JdiModelTargetVM vm, Process process) { + super(vm, getUniqueId(process), process); + this.process = process; + + //writer = new PrintWriter(process.getOutputStream()); + input = new Thread(() -> readStream(process.getInputStream(), TargetConsole.Channel.STDOUT), + "JDI process input reader"); + input.start(); + error = new Thread(() -> readStream(process.getErrorStream(), TargetConsole.Channel.STDERR), + "JDI process error reader"); + error.start(); + + changeAttributes(List.of(), List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, convertState(process.isAlive()), // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public String getDisplay() { + if (process == null) { + return super.getDisplay(); + } + StringBuilder sb = new StringBuilder(); + sb.append("Process " + process.pid()); + sb.append(" alive="); + sb.append(process.isAlive()); + return sb.toString(); + } + + protected TargetExecutionState convertState(boolean isAlive) { + return isAlive ? TargetExecutionState.ALIVE : TargetExecutionState.TERMINATED; + } + + @Override + @Internal + public CompletableFuture select() { + return CompletableFuture.completedFuture(null); + ///return thread.select(); + } + + @Override + public void output(TargetConsole.Channel channel, String out) { + switch (channel) { + case STDOUT: + channel = TargetConsole.Channel.STDOUT; + break; + case STDERR: + channel = TargetConsole.Channel.STDERR; + break; + default: + throw new AssertionError(); + } + listeners.fire(TargetInterpreterListener.class).consoleOutput(this, channel, out); + } + + private void readStream(InputStream in, TargetConsole.Channel channel) { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + try { + String line; + while (process.isAlive() && null != (line = reader.readLine())) { + System.err.println(line); + output(channel, line); + } + } + catch (Throwable e) { + Msg.debug(this, channel + ", reader exiting because " + e); + //throw new AssertionError(e); + } + } + + @Override + public CompletableFuture write(byte[] data) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetReferenceType.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetReferenceType.java new file mode 100644 index 0000000000..d08055b7a4 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetReferenceType.java @@ -0,0 +1,167 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.*; + +import ghidra.async.AsyncFence; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetModule; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressRangeImpl; + +public class JdiModelTargetReferenceType extends JdiModelTargetType implements // + TargetModule { + + protected final ReferenceType reftype; + private long maxInstances = 100; + + private JdiModelTargetFieldContainer allFields; + private JdiModelTargetMethodContainer allMethods; + private JdiModelTargetFieldContainer fields; + private JdiModelTargetMethodContainer methods; + private JdiModelTargetObjectReferenceContainer instances; + private JdiModelTargetObjectReference classObject; + private JdiModelTargetObjectReference classLoader; + private JdiModelTargetLocationContainer locations; + + private JdiModelTargetAttributesContainer addedAttributes; + protected JdiModelTargetSectionContainer sections; + + public JdiModelTargetReferenceType(JdiModelTargetObject parent, ReferenceType reftype) { + this(parent, reftype.name(), reftype); + } + + public JdiModelTargetReferenceType(JdiModelTargetObject parent, String id, + ReferenceType reftype) { + super(parent, id, reftype); + this.reftype = reftype; + + if (reftype instanceof ClassType) { + this.sections = new JdiModelTargetSectionContainer(this); + if (sections != null) { + changeAttributes(List.of(), List.of( // + sections // + ), Map.of(), "Initialized"); + } + } + + // NB. Relevant ranges are in sections + Address zero = impl.getAddressSpace("ram").getAddress(0L); + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, reftype.name(), // + RANGE_ATTRIBUTE_NAME, new AddressRangeImpl(zero, zero), // + MODULE_NAME_ATTRIBUTE_NAME, reftype.name(), // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + + } + + private void populateAttributes() { + this.addedAttributes = new JdiModelTargetAttributesContainer(this, "Attributes"); + Map attrs = new HashMap<>(); + if (reftype instanceof ArrayType) { + return; + } + try { + attrs.put("isAbstract", reftype.isAbstract()); + attrs.put("isFinal", reftype.isFinal()); + attrs.put("isInitialized", reftype.isInitialized()); + attrs.put("isPackagePrivate", reftype.isPackagePrivate()); + attrs.put("isPrepared", reftype.isPrepared()); + attrs.put("isPrivate", reftype.isPrivate()); + attrs.put("isProtected", reftype.isProtected()); + attrs.put("isPublic", reftype.isPublic()); + attrs.put("isStatic", reftype.isStatic()); + attrs.put("isVerified", reftype.isVerified()); + } + catch (Exception e) { + if (e instanceof ClassNotLoadedException) { + attrs.put("status", "Class not loaded"); + } + } + attrs.put("defaultStratum", reftype.defaultStratum()); + attrs.put("availableStata", reftype.availableStrata()); + attrs.put("failedToInitialize", reftype.failedToInitialize()); + addedAttributes.addAttributes(attrs); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + this.allFields = new JdiModelTargetFieldContainer(this, true); + this.allMethods = new JdiModelTargetMethodContainer(this, true); + this.fields = new JdiModelTargetFieldContainer(this, false); + this.methods = new JdiModelTargetMethodContainer(this, false); + this.instances = new JdiModelTargetObjectReferenceContainer(this, "Objects", + reftype.instances(maxInstances)); + this.classLoader = reftype.classLoader() == null ? null + : new JdiModelTargetObjectReference(this, reftype.classLoader()); + this.classObject = (JdiModelTargetObjectReference) getInstance(reftype.classObject()); + + populateAttributes(); + + changeAttributes(List.of(), List.of( // + allFields, // + allMethods, // + fields, // + methods, // + instances, // + addedAttributes // + ), Map.of( // + "Class Object", classObject // + ), "Initialized"); + + try { + this.locations = + new JdiModelTargetLocationContainer(this, "Locations", reftype.allLineLocations()); + if (locations != null) { + changeAttributes(List.of(), List.of( // + locations // + ), Map.of(), "Initialized"); + } + } + catch (AbsentInformationException e) { + // Ignore + } + if (classLoader != null) { + changeAttributes(List.of(), List.of(), Map.of( // + "Class Loader", classLoader // + ), "Initialized"); + } + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture init() { + AsyncFence fence = new AsyncFence(); + //fence.include(methods.requestElements(true)); + return fence.ready(); + } + + @Override + public String getDisplay() { + return reftype == null ? super.getDisplay() : reftype.name(); + } + + public JdiModelTargetMethodContainer getAllMethods() { + return allMethods; + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRegister.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRegister.java new file mode 100644 index 0000000000..969860a944 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRegister.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.Location; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetRegister; +import ghidra.dbg.util.ConversionUtils; +import ghidra.program.model.address.Address; + +public class JdiModelTargetRegister extends JdiModelTargetObjectImpl implements // + TargetRegister { + + protected final String name; + protected Address addr; + + public JdiModelTargetRegister(JdiModelTargetObject parent, String name) { + super(parent, name, name); + this.name = name; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + CONTAINER_ATTRIBUTE_NAME, parent, // + LENGTH_ATTRIBUTE_NAME, Long.SIZE // + ), "Initialized"); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay() // + ), "Initialized"); + + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture init() { + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + if (name == null) { + return super.getDisplay(); + } + return addr == null ? name : name + ":" + addr; + } + + public byte[] readRegister(Location location) { + JdiModelTargetLocation targetLocation = new JdiModelTargetLocation(this, location); + Address oldval = (Address) getCachedAttribute(VALUE_ATTRIBUTE_NAME); + addr = targetLocation.getAddress(); + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + VALUE_ATTRIBUTE_NAME, addr // + ), "Initialized"); + setModified(!addr.equals(oldval)); + + byte[] bytes = + ConversionUtils.bigIntegerToBytes(getBitLength() / 8, addr.getOffsetAsBigInteger()); + return bytes; + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRegisterContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRegisterContainer.java new file mode 100644 index 0000000000..daa1358d8f --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRegisterContainer.java @@ -0,0 +1,135 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.Location; + +import ghidra.async.AsyncUtils; +import ghidra.dbg.target.TargetRegisterBank; +import ghidra.dbg.target.TargetRegisterContainer; +import ghidra.util.Msg; + +public class JdiModelTargetRegisterContainer extends JdiModelTargetObjectImpl + implements TargetRegisterBank, + TargetRegisterContainer { + + private final Map registersByName = new HashMap<>(); + + private JdiModelTargetThread thread; + private JdiModelTargetRegister pc; + private JdiModelTargetRegister sp; + private JdiModelTargetRegister retAddr; + + public JdiModelTargetRegisterContainer(JdiModelTargetThread thread) { + super(thread, "Registers"); + this.thread = thread; + this.pc = new JdiModelTargetRegister(this, "PC"); + this.sp = new JdiModelTargetRegister(this, "SP"); + this.retAddr = new JdiModelTargetRegister(this, "return_address"); + registersByName.put(pc.getName(), pc); + registersByName.put(sp.getName(), sp); + registersByName.put(retAddr.getName(), retAddr); + changeAttributes(List.of(), List.of( // + pc, // + sp, // + retAddr // + ), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getName(), // + DESCRIPTIONS_ATTRIBUTE_NAME, this // + ), "Initialized"); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + changeAttributes(List.of(), List.of( // + pc, // + sp, // + retAddr // + ), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getName(), // + DESCRIPTIONS_ATTRIBUTE_NAME, this // + ), "Initialized"); + + return CompletableFuture.completedFuture(null); + } + + protected synchronized JdiModelTargetRegister getTargetRegister(String rname) { + return registersByName.computeIfAbsent(rname, n -> new JdiModelTargetRegister(this, rname)); + } + + public synchronized JdiModelTargetRegister getTargetMethodIfPresent(String rname) { + return registersByName.get(rname); + } + + @Override + + public CompletableFuture> readRegistersNamed( + Collection names) { + Map map = new HashMap<>(); + Location pcLoc = thread.getLocation(); + Location spLoc = null; + Location raLoc = null; + + JdiModelTargetStackFrame targetFrame = thread.stack.getTargetFrame(1); + if (targetFrame != null) { + JdiModelTargetLocation loc = targetFrame.location; + if (loc != null) { + raLoc = loc.location; + } + } + + if (pcLoc != null) { + byte[] bytes = pc.readRegister(pcLoc); + map.put(pc.getIndex(), bytes); + } + //if (spLoc != null) { + // byte[] bytes = sp.readRegister(spLoc); + // map.put(sp.getIndex(), bytes); + //} + if (raLoc != null) { + byte[] bytes = retAddr.readRegister(raLoc); + map.put(retAddr.getIndex(), bytes); + } + if (!map.isEmpty()) { + listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, map); + } + return CompletableFuture.completedFuture(map); + } + + @Override + public CompletableFuture writeRegistersNamed(Map values) { + // TODO Auto-generated method stub + return null; + } + + public void invalidateRegisterCaches() { + listeners.fire.invalidateCacheRequested(this); + } + + protected CompletableFuture update() { + if (!isObserved()) { + return AsyncUtils.NIL; + } + return fetchElements(true).exceptionally(e -> { + Msg.error(this, "Could not update registers " + this + " on STOPPED"); + return null; + }); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRoot.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRoot.java new file mode 100644 index 0000000000..dc0b986ebc --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetRoot.java @@ -0,0 +1,296 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.*; +import com.sun.jdi.connect.*; +import com.sun.jdi.connect.Connector.Argument; + +import ghidra.async.AsyncUtils; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.agent.AbstractTargetObject; +import ghidra.dbg.agent.DefaultTargetModelRoot; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.jdi.manager.*; +import ghidra.dbg.jdi.model.iface1.*; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; +import ghidra.dbg.util.PathUtils; +import ghidra.util.Msg; + +/** + *

+ * TODO: It would probably be better to implement {@link TargetLauncher} on each connector, rather + * than using "focus" (a UI concept) to decide which to use. Additionally, for each connector, we + * can decide whether to implement {@link TargetLauncher} or {@link TargetAttacher} based on whether + * it's a {@link LaunchingConnector} or an {@link AttachingConnector}. Granted, there are some UI + * hiccups to work out when/if we take that approach, since + * {@link DebugModelConventions#findSuitable(Class, TargetObject)} requires a unique answer. That + * would mean neither attach nor launch will be enabled anywhere except on a connector.... + */ +public class JdiModelTargetRoot extends DefaultTargetModelRoot implements // + JdiModelTargetAccessConditioned, // + //JdiModelTargetAttacher, // + JdiModelTargetFocusScope, // + //TargetFocusScope, // + //JdiModelTargetInterpreter, // + JdiModelTargetInterruptible, // + JdiModelTargetLauncher, // + JdiModelTargetEventScope, // + JdiEventsListenerAdapter { + protected static final String JDB_PROMPT = ">"; + + protected final JdiModelImpl impl; + protected String display = "JDI"; + + protected final VirtualMachineManager vmm; + protected final JdiModelTargetVMContainer vms; + protected final JdiModelTargetConnectorContainer connectors; + protected JdiModelTargetAttributesContainer addedAttributes; + + private TargetAccessibility accessibility = TargetAccessibility.ACCESSIBLE; + protected JdiModelSelectableObject focus; + + protected String debugger = "Jdi"; // Used by JdiModelTargetEnvironment + + public JdiModelTargetRoot(JdiModelImpl impl) { + super(impl, "VirtualMachineManager"); + this.impl = impl; + this.vmm = this.impl.getManager().getVirtualMachineManager(); + + this.vms = new JdiModelTargetVMContainer(this); + this.connectors = new JdiModelTargetConnectorContainer(this); + + populateAttributes(); + + changeAttributes(List.of(), List.of( // + vms, // + connectors, // + addedAttributes // + ), Map.of( // + ACCESSIBLE_ATTRIBUTE_NAME, accessibility == TargetAccessibility.ACCESSIBLE, // + DISPLAY_ATTRIBUTE_NAME, display, // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, TargetCmdLineLauncher.PARAMETERS, // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + + impl.getManager().addEventsListener(null, this); + //impl.getManager().addConsoleOutputListener(this); + } + + @Override + public JdiModelImpl getModelImpl() { + return impl; + } + + private void populateAttributes() { + this.addedAttributes = new JdiModelTargetAttributesContainer(this, "Attributes"); + Map attrs = new HashMap<>(); + attrs.put("Major Version", vmm.majorInterfaceVersion()); + attrs.put("Minor Version", vmm.minorInterfaceVersion()); + addedAttributes.addAttributes(attrs); + } + + @Override + public String getDisplay() { + return display; + } + + /*/// + @Override + public void output(JdiManager.Channel JdiChannel, String out) { + TargetInterpreter.Channel dbgChannel; + switch (JdiChannel) { + case STDOUT: + dbgChannel = TargetInterpreter.Channel.STDOUT; + break; + case STDERR: + dbgChannel = TargetInterpreter.Channel.STDERR; + break; + default: + throw new AssertionError(); + } + listeners.fire(TargetInterpreterListener.class).consoleOutput(this, dbgChannel, out); + } + */ + + @Override + public void vmSelected(VirtualMachine vm, JdiCause cause) { + if (vm.allThreads().isEmpty()) { + JdiModelTargetVM targetVM = vms.getTargetVM(vm); + setFocus(targetVM); + } + // Otherwise, we'll presumably get the =thread-selected event + } + + @Override + public void threadSelected(ThreadReference thread, StackFrame frame, JdiCause cause) { + JdiModelTargetVM vm = vms.getTargetVM(thread.threadGroup().virtualMachine()); + JdiModelTargetThread t = vm.threads.getTargetThread(thread); + if (frame == null) { + setFocus(t); + return; + } + JdiModelTargetStackFrame f = t.stack.getTargetFrame(frame); + setFocus(f); + } + + public void setAccessibility(TargetAccessibility accessibility) { + synchronized (attributes) { + if (this.accessibility == accessibility) { + return; + } + this.accessibility = accessibility; + changeAttributes(List.of(), List.of(), Map.of( // + ACCESSIBLE_ATTRIBUTE_NAME, accessibility == TargetAccessibility.ACCESSIBLE // + ), "Accessibility changed"); + } + listeners.fire(TargetAccessibilityListener.class).accessibilityChanged(this, accessibility); + } + + @Override + public TargetAccessibility getAccessibility() { + return accessibility; + } + + @Override + public CompletableFuture launch(Map args) { + JdiManager manager = impl.getManager(); + JdiModelTargetConnector targetConnector = connectors.getDefaultConnector(); + Connector cx = (targetConnector != null) ? targetConnector.cx + : manager.getVirtualMachineManager().defaultConnector(); + Map defaultArguments = cx.defaultArguments(); + Map jdiArgs = JdiModelTargetLauncher.getArguments(defaultArguments, + JdiModelTargetLauncher.getParameters(defaultArguments), args); + return getManager().addVM(cx, jdiArgs).thenApply(__ -> null); + } + + /** + * {@inheritDoc} + * + *

+ * TODO: Technically, this should be done by setting + * {@link TargetMethod#PARAMTERS_ATTRIBUTE_NAME} whenever the default connector changes. + * However, that's really only needed if this is to be marshalled over GADP, and that is not the + * case. Listening for parameter description changes doesn't seem like a normal thing to do + * otherwise. + */ + @Override + public TargetParameterMap getParameters() { + JdiManager manager = impl.getManager(); + JdiModelTargetConnector targetConnector = connectors.getDefaultConnector(); + Connector cx = (targetConnector != null) ? targetConnector.cx + : manager.getVirtualMachineManager().defaultConnector(); + Map defaultArguments = cx.defaultArguments(); + return TargetParameterMap.copyOf(JdiModelTargetLauncher.getParameters(defaultArguments)); + } + + public CompletableFuture attach(long pid) { + JdiManager manager = impl.getManager(); + JdiModelTargetConnector targetConnector = + connectors.getTargetConnectorIfPresent("SocketAttach"); + if (targetConnector == null) { + Msg.error(this, "No match found in connectors for SocketAttach"); + } + else { + Connector cx = targetConnector.cx; + manager.addVM(cx, List.of(Long.toString(pid))); + } + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture interrupt() { + try { + impl.getManager().sendInterruptNow(); + } + catch (IOException e) { + Msg.error(this, "Could not interrupt", e); + } + return AsyncUtils.NIL; + } + + @Override + public CompletableFuture requestFocus(TargetObjectRef ref) { + impl.assertMine(TargetObjectRef.class, ref); + /** + * Yes, this is pointless, since I'm the root, but do it right (TM), since this may change + * or be used as an example for other implementations. + */ + if (!PathUtils.isAncestor(this.getPath(), ref.getPath())) { + throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope"); + } + return ref.fetch().thenCompose(obj -> { + TargetObject cur = obj; + while (cur != null) { + if (cur instanceof JdiModelSelectableObject) { + JdiModelSelectableObject sel = (JdiModelSelectableObject) cur; + return sel.select(); + } + if (cur instanceof AbstractTargetObject) { + AbstractTargetObject def = (AbstractTargetObject) cur; + cur = def.getImplParent(); + continue; + } + throw new AssertionError(); + } + return AsyncUtils.NIL; + }); + } + + protected void invalidateMemoryAndRegisterCaches() { + vms.invalidateMemoryAndRegisterCaches(); + } + + @Override + public boolean setFocus(JdiModelSelectableObject sel) { + boolean doFire; + synchronized (this) { + doFire = !Objects.equals(this.focus, sel); + this.focus = sel; + } + if (doFire) { + changeAttributes(List.of(), List.of(), Map.of( // + FOCUS_ATTRIBUTE_NAME, focus // + ), "Focus changed"); + listeners.fire(TargetFocusScopeListener.class).focusChanged(this, sel); + return true; + } + return false; + } + + @Override + public JdiModelSelectableObject getFocus() { + return focus; + } + + @Override + public Object getObject() { + return null; + } + + @Override + public JdiModelTargetObject getTargetObject(Object object) { + return null; + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetSection.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetSection.java new file mode 100644 index 0000000000..0d2daa731f --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetSection.java @@ -0,0 +1,94 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.Method; + +import ghidra.dbg.target.TargetMemoryRegion; +import ghidra.dbg.target.TargetSection; +import ghidra.program.model.address.AddressRange; + +public class JdiModelTargetSection extends JdiModelTargetObjectImpl implements // + //TargetMemory, + TargetMemoryRegion, TargetSection { + + protected final Method method; + private AddressRange range; + + public JdiModelTargetSection(JdiModelTargetSectionContainer parent, Method method) { + super(parent, method.toString(), method); + this.method = method; + + this.range = impl.getAddressRange(method); + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + MODULE_ATTRIBUTE_NAME, parent.getClassType(), // + READABLE_ATTRIBUTE_NAME, true, // + MEMORY_ATTRIBUTE_NAME, parent, TargetMemoryRegion.RANGE_ATTRIBUTE_NAME, range, // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + public JdiModelTargetSection(JdiModelTargetSectionContainer parent) { + super(parent); + this.method = null; + + range = impl.defaultRange; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + MODULE_ATTRIBUTE_NAME, parent.getClassType(), // + TargetMemoryRegion.RANGE_ATTRIBUTE_NAME, range, // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + this.range = method == null ? impl.defaultRange : impl.getAddressRange(method); + if (range != null) { + changeAttributes(List.of(), List.of(), Map.of(), "Initialized"); + } + + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture init() { + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + if (method == null) + return "NULL"; + JdiModelTargetReferenceType classType = + ((JdiModelTargetSectionContainer) parent).getClassType(); + return classType.getName() + ":" + method.signature(); + } + + @Override + public AddressRange getRange() { + return range; + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetSectionContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetSectionContainer.java new file mode 100644 index 0000000000..911a6e9578 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetSectionContainer.java @@ -0,0 +1,134 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.Method; + +import ghidra.async.AsyncFence; +import ghidra.dbg.target.TargetMemory; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; + +public class JdiModelTargetSectionContainer extends JdiModelTargetObjectImpl + implements TargetMemory { + + protected final JdiModelTargetReferenceType reftype; + + private final Map sectionsByName = new HashMap<>(); + private JdiModelTargetConstantPool constantPool; + + //private JdiModelTargetConstantPool constantPool; + + public JdiModelTargetSectionContainer(JdiModelTargetReferenceType reftype) { + super(reftype, "Sections"); + this.reftype = reftype; + new JdiModelTargetSection(this); // default section + } + + protected void updateUsingSections(List methods) { + setElements(sectionsByName.values(), Map.of(), "Refreshed"); + } + + @Override + protected CompletableFuture requestAttributes(boolean refresh) { + + constantPool = new JdiModelTargetConstantPool(this, reftype.reftype.constantPool()); + changeAttributes(List.of(), List.of( // + constantPool // + ), Map.of(), "Initialized"); + + return CompletableFuture.completedFuture(null); + } + + @Override + protected CompletableFuture requestElements(boolean refresh) { + updateUsingSections(reftype.reftype.methods()); + return CompletableFuture.completedFuture(null); + } + + protected synchronized JdiModelTargetSection getTargetSection(Method method) { + return sectionsByName.computeIfAbsent(JdiModelImpl.methodToKey(method), + n -> new JdiModelTargetSection(this, method)); + } + + public synchronized JdiModelTargetSection getTargetSectionIfPresent(String name) { + return sectionsByName.get(name); + } + + @Override + public CompletableFuture init() { + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetSection method : sectionsByName.values()) { + fence.include(method.init()); + } + return fence.ready(); + } + + public JdiModelTargetReferenceType getClassType() { + return reftype; + } + + public void addSection(Method method) { + if (impl.getAddressRange(method) != null) { + JdiModelTargetSection targetSection = getTargetSection(method); + sectionsByName.put(JdiModelImpl.methodToKey(method), targetSection); + changeElements(List.of(), List.of(targetSection), Map.of(), "Refreshed"); + } + else { + System.err.println("addSection returned null: " + method.location()); + } + } + + @Override + public CompletableFuture readMemory(Address address, int length) { + AddressSpace addressSpace = address.getAddressSpace(); + if (addressSpace.equals(impl.getAddressSpace("ram"))) { + byte[] bytes = new byte[length]; + Method method = impl.getMethodForAddress(address); + if (method != null && targetVM.vm.canGetBytecodes()) { + byte[] bytecodes = method.bytecodes(); + int i = 0; + for (byte b : bytecodes) { + bytes[i++] = b; + if (i >= length) + break; + } + } + else { + for (int i = 0; i < length; i++) { + bytes[i] = (byte) 0xFF; + } + } + listeners.fire(TargetMemoryListener.class).memoryUpdated(this, address, bytes); + return CompletableFuture.completedFuture(bytes); + } + if (addressSpace.equals(impl.getAddressSpace("constantPool"))) { + byte[] bytes = constantPool.getPool(); + listeners.fire(TargetMemoryListener.class).memoryUpdated(this, address, bytes); + return CompletableFuture.completedFuture(bytes); + } + throw new RuntimeException(); + } + + @Override + public CompletableFuture writeMemory(Address address, byte[] data) { + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetStack.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetStack.java new file mode 100644 index 0000000000..a4c9ac0aae --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetStack.java @@ -0,0 +1,109 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.*; + +import ghidra.async.AsyncUtils; +import ghidra.dbg.target.TargetStack; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +public class JdiModelTargetStack extends JdiModelTargetObjectImpl + implements TargetStack { + + protected final JdiModelTargetThread thread; + + protected final Map framesByLocation = + new WeakValueHashMap<>(); + protected final Map framesByLevel = new WeakValueHashMap<>(); + + public JdiModelTargetStack(JdiModelTargetThread thread) { + super(thread, "Stack"); + this.thread = thread; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + List targetFrames = new ArrayList<>(); + List frames; + try { + frames = thread.thread.frames(); + int i = 0; + for (StackFrame frame : frames) { + JdiModelTargetStackFrame targetFrame = getTargetFrame(i++, frame); + targetFrames.add(targetFrame); + } + setElements(targetFrames, Map.of(), "Refreshed"); + } + catch (IncompatibleThreadStateException e) { + // Ignore e.printStackTrace(); + } + return CompletableFuture.completedFuture(null); + } + + protected synchronized JdiModelTargetStackFrame getTargetFrame(int level, StackFrame frame) { + return framesByLocation.compute(frame.location(), (l, f) -> { + if (f == null || f.getFrameLevel() != level) { + JdiModelTargetStackFrame tf = + new JdiModelTargetStackFrame(this, thread, level, frame); + framesByLevel.put(level, tf); + return tf; + } + framesByLevel.put(level, f); + f.setFrame(level, frame); + return f; + }); + } + + public JdiModelTargetStackFrame getTargetFrame(StackFrame frame) { + for (JdiModelTargetStackFrame f : framesByLocation.values()) { + if (f.frame.equals(frame)) { + return f; + } + } + return null; + } + + public JdiModelTargetStackFrame getTargetFrame(int level) { + return framesByLevel.get(Integer.valueOf(level)); + } + + /** + * Re-fetch the stack frames, generating events for updates + * + * JDI doesn't produce stack change events, but they should only ever happen by running a + * target. Thus, every time we're STOPPED, this method should be called. + * + * @return null + */ + protected CompletableFuture update() { + if (!isObserved()) { + return AsyncUtils.NIL; + } + return fetchElements(true).exceptionally(e -> { + Msg.error(this, "Could not update stack " + this + " on STOPPED"); + return null; + }); + } + + public void invalidateRegisterCaches() { + listeners.fire.invalidateCacheRequested(this); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetStackFrame.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetStackFrame.java new file mode 100644 index 0000000000..bee1ecd3fb --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetStackFrame.java @@ -0,0 +1,154 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import com.sun.jdi.*; + +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.jdi.manager.JdiCause; +import ghidra.dbg.jdi.manager.JdiEventsListenerAdapter; +import ghidra.dbg.jdi.model.iface1.JdiModelSelectableObject; +import ghidra.dbg.jdi.model.iface1.JdiModelTargetFocusScope; +import ghidra.dbg.target.TargetStackFrame; +import ghidra.program.model.address.Address; +import ghidra.util.Msg; + +public class JdiModelTargetStackFrame extends JdiModelTargetObjectImpl + implements TargetStackFrame, // + //TargetRegisterBank, // + JdiEventsListenerAdapter, // + JdiModelSelectableObject { + + public static String getUniqueId(int level) { + return Integer.toString(level); + } + + public static final String FUNC_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "function"; + public static final String FROM_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "from"; // TODO + + protected final JdiModelTargetThread thread; + protected JdiModelTargetLocalVariableContainer variables; + protected JdiModelTargetValueMap values; + protected JdiModelTargetObjectReference thisObject; + protected JdiModelTargetLocation location; + protected JdiModelTargetValueContainer arguments; + + protected StackFrame frame; + protected Address pc; + protected long level; + + public JdiModelTargetStackFrame(JdiModelTargetStack stack, JdiModelTargetThread thread, + int level, StackFrame frame) { + super(stack, getUniqueId(level), frame); + this.thread = thread; + this.level = level; + this.frame = frame; + + this.location = new JdiModelTargetLocation(this, frame.location()); + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + LOCATION_ATTRIBUTE_NAME, location, // + THREAD_ATTRIBUTE_NAME, thread.getName(), // + PC_ATTRIBUTE_NAME, location.getAddress() // + ), "Initialized"); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + //this.arguments = new JdiModelTargetValueContainer(this, "Arguments", frame.getArgumentValues()); + + ObjectReference obj = frame.thisObject(); + if (obj != null) { + this.thisObject = (JdiModelTargetObjectReference) getInstance(obj); + changeAttributes(List.of(), List.of(), Map.of( // + THIS_OBJECT_ATTRIBUTE_NAME, thisObject // + ), "Initialized"); + } + try { + this.variables = new JdiModelTargetLocalVariableContainer(this, "Visible Variables", + frame.visibleVariables()); + Map map = frame.getValues(frame.visibleVariables()); + this.values = new JdiModelTargetValueMap(this, map); + changeAttributes(List.of(), List.of( // + variables, // + values // + ), Map.of(), "Initialized"); + } + catch (AbsentInformationException e) { + // Ignore + } + + return CompletableFuture.completedFuture(null); + } + + @Override + public String getDisplay() { + if (frame == null) { + return super.getDisplay(); + } + Location loc = null; + try { + loc = frame.location(); + } + catch (InvalidStackFrameException e) { + Msg.error(this, "Invalid stack frame"); + } + return String.format("#%d %s", level, loc); + } + + @Override + public void threadSelected(ThreadReference eventThread, StackFrame eventFrame, JdiCause cause) { + if (eventThread.equals(thread.thread) && eventFrame.equals(frame)) { + AtomicReference> scope = new AtomicReference<>(); + AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + DebugModelConventions.findSuitable(JdiModelTargetFocusScope.class, this) + .handle(seq::next); + }, scope).then(seq -> { + scope.get().setFocus(this); + }).finish(); + } + } + + @Override + public CompletableFuture select() { + ///return frame.select(); + return CompletableFuture.completedFuture(null); + } + + public long getFrameLevel() { + return level; + } + + public void setFrameLevel(long level) { + this.level = level; + } + + public void setFrame(int level, StackFrame frame) { + this.frame = frame; + targetVM.setTargetObject(getUniqueId(level), frame, this); + setModified(true); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThread.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThread.java new file mode 100644 index 0000000000..dc7e27d155 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThread.java @@ -0,0 +1,389 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import com.sun.jdi.*; +import com.sun.jdi.event.*; +import com.sun.jdi.request.EventRequestManager; +import com.sun.jdi.request.StepRequest; + +import ghidra.async.*; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.jdi.manager.*; +import ghidra.dbg.jdi.model.iface1.*; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetThread; +import ghidra.lifecycle.Internal; +import ghidra.util.Msg; + +public class JdiModelTargetThread extends JdiModelTargetObjectReference implements // + TargetThread, // + JdiModelTargetAccessConditioned, // + JdiModelTargetExecutionStateful, // + JdiModelTargetInterruptible, // + JdiModelTargetKillable, // + JdiModelTargetResumable, // + JdiModelTargetSteppable, // + //TargetSuspendable, + JdiEventsListenerAdapter, // + JdiModelSelectableObject { + + protected static final TargetStepKindSet SUPPORTED_KINDS = TargetStepKindSet.of( // + TargetStepKind.ADVANCE, TargetStepKind.FINISH, TargetStepKind.LINE, TargetStepKind.OVER, + TargetStepKind.OVER_LINE, TargetStepKind.RETURN, TargetStepKind.UNTIL); + + private EventRequestManager eventManager; + protected final ThreadReference thread; + + protected final JdiModelTargetStack stack; + protected final JdiModelTargetRegisterContainer registers; + protected JdiModelTargetLocation location; + + protected JdiModelTargetThreadGroupContainer threadGroup; + protected JdiModelTargetObjectReference currentContendedMonitor; + protected JdiModelTargetObjectReferenceContainer ownedMonitors; + protected JdiModelTargetAttributesContainer addedAttributes; + + public JdiModelTargetThread(JdiModelTargetObject parent, ThreadReference thread) { + super(parent, thread.name(), thread); + this.thread = thread; + this.eventManager = thread.virtualMachine().eventRequestManager(); + + this.stack = new JdiModelTargetStack(this); + this.registers = new JdiModelTargetRegisterContainer(this); + + impl.getManager().addEventsListener(targetVM.vm, this); + + TargetExecutionState targetState = convertState(thread.status()); + changeAttributes(List.of(), List.of( // + registers, // + stack // + ), Map.of( // + STATE_ATTRIBUTE_NAME, targetState, // + "Status", thread.status(), // + ACCESSIBLE_ATTRIBUTE_NAME, thread.isSuspended(), // + SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS, // + DISPLAY_ATTRIBUTE_NAME, display = getDisplay() // + ), "Initialized"); + listeners.fire(TargetExecutionStateListener.class).executionStateChanged(this, targetState); + + getManager().addStateListener(thread.virtualMachine(), accessListener); + } + + private void populateAttributes() { + this.addedAttributes = new JdiModelTargetAttributesContainer(this, "Attributes"); + Map attrs = new HashMap<>(); + attrs.put("isAtBreakpoint", thread.isAtBreakpoint()); + attrs.put("isCollected", thread.isCollected()); + attrs.put("isSuspended", thread.isSuspended()); + try { + attrs.put("entryCount", thread.entryCount()); + } + catch (IncompatibleThreadStateException e) { + // Ignore + } + try { + attrs.put("frameCount", thread.frameCount()); + } + catch (IncompatibleThreadStateException e) { + // Ignore + } + attrs.put("suspendCount", thread.suspendCount()); + addedAttributes.addAttributes(attrs); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + populateAttributes(); + + changeAttributes(List.of(), List.of( // + addedAttributes // + ), Map.of(), "Initialized"); + + if (targetVM.vm.canGetCurrentContendedMonitor()) { + try { + ObjectReference monitor = thread.currentContendedMonitor(); + if (monitor != null) { + currentContendedMonitor = (JdiModelTargetObjectReference) getInstance(monitor); + if (currentContendedMonitor != null) { + changeAttributes(List.of(), List.of(), Map.of( // + "Current Contended Monitor", currentContendedMonitor // + ), "Initialized"); + } + } + } + catch (IncompatibleThreadStateException e2) { + // Ignore + } + } + if (targetVM.vm.canGetOwnedMonitorInfo()) { + try { + this.ownedMonitors = new JdiModelTargetObjectReferenceContainer(this, + "Owned Monitors", thread.ownedMonitors()); + if (ownedMonitors != null) { + changeAttributes(List.of(), List.of( // + ownedMonitors // + ), Map.of(), "Initialized"); + } + } + catch (IncompatibleThreadStateException e1) { + // Ignore + } + } + ThreadGroupReference tg = thread.threadGroup(); + this.threadGroup = tg == null ? null : new JdiModelTargetThreadGroupContainer(this, tg); + if (threadGroup != null) { + changeAttributes(List.of(), List.of(), Map.of( // + "Thread Group", thread.threadGroup() // + ), "Initialized"); + } + return CompletableFuture.completedFuture(null); + } + + public CompletableFuture init() { + AsyncFence fence = new AsyncFence(); + //fence.include(requestAttributes(true)); + return fence.ready(); + } + + @Override + public String getDisplay() { + if (thread == null) { + return super.getDisplay(); + } + StringBuilder sb = new StringBuilder(); + sb.append(thread.name()); + if (location != null) { + sb.append(" in "); + sb.append(location); + } + JdiModelTargetStackFrame top = stack.framesByLevel.get(0); + if (top != null && top.location != null) { + sb.append(" in "); + sb.append(top.location.getDisplay()); + } + return sb.toString(); + } + + protected TargetExecutionState convertState(int state) { + switch (state) { + case ThreadReference.THREAD_STATUS_RUNNING: + case ThreadReference.THREAD_STATUS_WAIT: + return thread.isSuspended() ? TargetExecutionState.STOPPED + : TargetExecutionState.RUNNING; + case ThreadReference.THREAD_STATUS_NOT_STARTED: + return TargetExecutionState.ALIVE; + default: + return TargetExecutionState.STOPPED; + } + } + + @Override + public void stepComplete(StepEvent evt, JdiCause cause) { + if (evt.thread().equals(thread)) { + setLocation(evt.location()); + changeAttributes(List.of(), List.of(), Map.of( // + LOCATION_ATTRIBUTE_NAME, location // + ), "Refreshed"); + stateChanged(thread.status(), JdiReason.Reasons.STEP); + } + } + + @Override + public void breakpointHit(BreakpointEvent evt, JdiCause cause) { + if (evt.thread().equals(thread)) { + setLocation(evt.location()); + changeAttributes(List.of(), List.of(), Map.of( // + LOCATION_ATTRIBUTE_NAME, location // + ), "Refreshed"); + stateChanged(thread.status(), JdiReason.Reasons.BREAKPOINT_HIT); + } + } + + // Which of these is actually going to fire, i.e. are separate events generated for subclasses? + + @Override + public void watchpointHit(WatchpointEvent evt, JdiCause cause) { + if (evt.thread().equals(thread)) { + setLocation(evt.location()); + changeAttributes(List.of(), List.of(), Map.of( // + LOCATION_ATTRIBUTE_NAME, location // + ), "Refreshed"); + stateChanged(thread.status(), JdiReason.Reasons.WATCHPOINT_HIT); + } + } + + @Override + public void accessWatchpointHit(AccessWatchpointEvent evt, JdiCause cause) { + if (evt.thread().equals(thread)) { + setLocation(evt.location()); + changeAttributes(List.of(), List.of(), Map.of( // + LOCATION_ATTRIBUTE_NAME, location // + ), "Refreshed"); + stateChanged(thread.status(), JdiReason.Reasons.ACCESS_WATCHPOINT_HIT); + } + } + + @Override + public void threadSelected(ThreadReference eventThread, StackFrame frame, JdiCause cause) { + if (eventThread.equals(thread) && frame == null) { + AtomicReference> scope = new AtomicReference<>(); + AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + DebugModelConventions.findSuitable(JdiModelTargetFocusScope.class, this) + .handle(seq::next); + }, scope).then(seq -> { + scope.get().setFocus(this); + }).finish(); + } + } + + private void stateChanged(int state, JdiReason reason) { + TargetExecutionState targetState = convertState(state); + if (targetState.equals(TargetExecutionState.STOPPED)) { + update(); + threadSelected(thread, null, JdiCause.Causes.UNCLAIMED); + } + targetVM.vmStateChanged(targetState, reason); + JdiEventHandler eventHandler = getManager().getEventHandler(targetVM.vm); + eventHandler.listenersEvent.fire.threadStateChanged(thread, state, + JdiCause.Causes.UNCLAIMED, reason); + } + + public void threadStateChanged(TargetExecutionState targetState) { + changeAttributes(List.of(), List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, targetState // + ), "Refreshed"); + listeners.fire(TargetExecutionStateListener.class).executionStateChanged(this, targetState); + } + + protected CompletableFuture update() { + //Msg.debug(this, "Updating stack for " + this); + registers.update(); + return stack.update().thenAccept(__ -> { + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay() // + ), "Refreshed"); + }).exceptionally(ex -> { + Msg.error(this, "Could not update stack for thread " + this, ex); + return null; + }); + } + + @Override + @Internal + public CompletableFuture select() { + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture kill() { + thread.interrupt(); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture interrupt() { + thread.suspend(); + stateChanged(thread.status(), JdiReason.Reasons.INTERRUPT); + return CompletableFuture.completedFuture(null); + } + + //@Override + public CompletableFuture popFrame(StackFrame frame) { + try { + thread.popFrames(frame); + } + catch (IncompatibleThreadStateException e) { + e.printStackTrace(); + } + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture resume() { + targetVM.vmStateChanged(TargetExecutionState.RUNNING, JdiReason.Reasons.RESUMED); + invalidateAndContinue(); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture step(TargetStepKind kind) { + int size = StepRequest.STEP_MIN; + int depth; + switch (kind) { + case INTO: + depth = StepRequest.STEP_INTO; + break; + case LINE: + depth = StepRequest.STEP_LINE; + break; + case FINISH: + case ADVANCE: + depth = StepRequest.STEP_OUT; + break; + case SKIP: + depth = StepRequest.STEP_OVER; + break; + default: + depth = StepRequest.STEP_MIN; + break; + } + + StepRequest request = eventManager.createStepRequest(thread, size, depth); + request.enable(); + invalidateAndContinue(); + return CompletableFuture.completedFuture(null); + } + + private void invalidateAndContinue() { + targetVM.invalidateMemoryAndRegisterCaches(); + stack.invalidateRegisterCaches(); + registers.invalidateRegisterCaches(); + thread.resume(); + //EventSet.resume()? + } + + public JdiModelTargetStack getStack() { + return stack; + } + + public Location getLocation() { + return location == null ? null : location.location; + } + + public void setLocation(Location location) { + this.location = new JdiModelTargetLocation(this, location); + Method method = location.method(); + impl.registerMethod(method); + } + + @Override + public void threadStarted(ThreadStartEvent evt, JdiCause cause) { + threadSelected(evt.thread(), null, JdiCause.Causes.UNCLAIMED); + } + + @Override + public TargetAccessibility getAccessibility() { + return thread.isSuspended() ? TargetAccessibility.ACCESSIBLE + : TargetAccessibility.INACCESSIBLE; + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThreadContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThreadContainer.java new file mode 100644 index 0000000000..a5fc83ac8d --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThreadContainer.java @@ -0,0 +1,124 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.ThreadReference; + +import ghidra.async.AsyncFence; +import ghidra.dbg.jdi.manager.*; +import ghidra.dbg.jdi.model.iface1.JdiModelTargetEventScope; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.util.datastruct.WeakValueHashMap; + +public class JdiModelTargetThreadContainer extends JdiModelTargetObjectImpl implements // + JdiModelTargetEventScope, // + JdiEventsListenerAdapter { + + private List threads; + + protected final Map threadsById = new WeakValueHashMap<>(); + + public JdiModelTargetThreadContainer(JdiModelTargetObject object, String name, + List threads) { + super(object, name); + this.threads = threads; + + impl.getManager().addEventsListener(targetVM.vm, this); + + } + + public JdiModelTargetThread threadCreated(ThreadReference thread) { + JdiModelTargetThread targetThread = getTargetThread(thread); + changeElements(List.of(), List.of(targetThread), Map.of(), "Created"); + return targetThread; + } + + public void threadExited(ThreadReference thread) { + synchronized (this) { + threadsById.remove(thread.name()); + } + changeElements(List.of(thread.name()), List.of(), Map.of(), "Exited"); + } + + @Override + public void threadStateChanged(ThreadReference thread, Integer state, JdiCause cause, + JdiReason reason) { + JdiModelTargetThread targetThread = getTargetThread(thread); + TargetExecutionState targetState = targetThread.convertState(state); + targetThread.threadStateChanged(targetState); + TargetEventType eventType = getEventType(reason); + getListeners().fire(TargetEventScopeListener.class) + .event(this, targetThread, eventType, + "Thread " + targetThread.getName() + " state changed", List.of(targetThread)); + } + + private TargetEventType getEventType(JdiReason reason) { + if (reason == JdiReason.Reasons.STEP) { + return TargetEventType.STEP_COMPLETED; + } + if (reason == JdiReason.Reasons.BREAKPOINT_HIT) { + return TargetEventType.BREAKPOINT_HIT; + } + if (reason == JdiReason.Reasons.ACCESS_WATCHPOINT_HIT) { + return TargetEventType.BREAKPOINT_HIT; + } + if (reason == JdiReason.Reasons.WATCHPOINT_HIT) { + return TargetEventType.BREAKPOINT_HIT; + } + if (reason == JdiReason.Reasons.INTERRUPT) { + return TargetEventType.SIGNAL; + } + if (reason == JdiReason.Reasons.RESUMED) { + return TargetEventType.RUNNING; + } + return TargetEventType.STOPPED; + } + + protected CompletableFuture updateUsingThreads(List refs) { + List targetThreads; + synchronized (this) { + targetThreads = refs.stream().map(this::getTargetThread).collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetThread t : targetThreads) { + fence.include(t.init()); + } + return fence.ready().thenAccept(__ -> { + setElements(targetThreads, Map.of(), "Refreshed"); + }); + } + + @Override + protected CompletableFuture requestElements(boolean refresh) { + return updateUsingThreads(threads); + } + + public synchronized JdiModelTargetThread getTargetThread(ThreadReference thread) { + return threadsById.computeIfAbsent(thread.name(), + i -> (JdiModelTargetThread) getInstance(thread)); + } + + public synchronized JdiModelTargetThread getTargetThreadIfPresent(String name) { + return threadsById.get(name); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThreadGroupContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThreadGroupContainer.java new file mode 100644 index 0000000000..9001b4d2ef --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetThreadGroupContainer.java @@ -0,0 +1,89 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.ThreadGroupReference; + +import ghidra.dbg.jdi.manager.JdiEventsListenerAdapter; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.util.PathUtils; +import ghidra.util.datastruct.WeakValueHashMap; + +public class JdiModelTargetThreadGroupContainer extends JdiModelTargetObjectImpl + implements JdiEventsListenerAdapter { + + protected static String keyGroup(ThreadGroupReference group) { + return PathUtils.makeKey(group.name()); + } + + protected final ThreadGroupReference baseGroup; + + protected final Map threadGroupsById = + new WeakValueHashMap<>(); + + public JdiModelTargetThreadGroupContainer(JdiModelTargetVM parent) { + super(parent, "Thread Groups"); + this.baseGroup = null; + } + + public JdiModelTargetThreadGroupContainer(JdiModelTargetObject parent, + ThreadGroupReference group) { + super(parent, keyGroup(group)); + this.baseGroup = group; + } + + @Override + protected CompletableFuture requestElements(boolean refresh) { + List groups; + if (parent instanceof JdiModelTargetVM) { + JdiModelTargetVM vm = (JdiModelTargetVM) parent; + groups = vm.vm.topLevelThreadGroups(); + } + else { + groups = baseGroup.threadGroups(); + if (!baseGroup.threads().isEmpty()) { + JdiModelTargetThreadContainer c = + new JdiModelTargetThreadContainer(this, "Threads", baseGroup.threads()); + this.changeAttributes(List.of(), List.of( // + c // + ), Map.of(), "Refreshed"); + } + } + updateUsingThreadGroups(groups); + return CompletableFuture.completedFuture(null); + } + + protected void updateUsingThreadGroups(List refs) { + List threadGroups; + synchronized (this) { + threadGroups = + refs.stream().map(this::getTargetThreadGroup).collect(Collectors.toList()); + } + setElements(threadGroups, Map.of(), "Refreshed"); + } + + public synchronized JdiModelTargetThreadGroupContainer getTargetThreadGroup( + ThreadGroupReference group) { + return threadGroupsById.computeIfAbsent(group.name(), + i -> new JdiModelTargetThreadGroupContainer(this, group)); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetType.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetType.java new file mode 100644 index 0000000000..fc8e97ea22 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetType.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.List; +import java.util.Map; + +import com.sun.jdi.Type; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public class JdiModelTargetType extends JdiModelTargetObjectImpl { + + protected final Type type; + + public JdiModelTargetType(JdiModelTargetObject object, Type type) { + this(object, type.toString(), type); + } + + public JdiModelTargetType(JdiModelTargetObject object, String id, Type type) { + super(object, id, type); + this.type = type; + + changeAttributes(List.of(), List.of(), Map.of( // + "Signature", type.signature() // + ), "Initialized"); + } + + @Override + public String getDisplay() { + return type == null ? super.getDisplay() : type.name(); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetTypeContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetTypeContainer.java new file mode 100644 index 0000000000..7ec0fe6cc4 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetTypeContainer.java @@ -0,0 +1,77 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.Type; + +import ghidra.async.AsyncFence; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public class JdiModelTargetTypeContainer extends JdiModelTargetObjectImpl { + + private List types; + + // TODO: Is it possible to load the same object twice? + protected final Map typesByName = new HashMap<>(); + + public JdiModelTargetTypeContainer(JdiModelTargetObject parent, String name, + List typeList) { + super(parent, name); + this.types = typeList; + } + + protected CompletableFuture updateUsingTypes(Map byName) { + List vals; + synchronized (this) { + vals = byName.values().stream().map(this::getTargetType).collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetType val : vals) { + fence.include(val.init()); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), vals, Map.of(), "Refreshed"); + }); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + Map map = new HashMap<>(); + try { + for (Type type : types) { + map.put(type.name(), type); + } + typesByName.keySet().retainAll(map.keySet()); + } + catch (Exception e) { + e.printStackTrace(); + } + return updateUsingTypes(map); + } + + protected synchronized JdiModelTargetType getTargetType(Type type) { + return typesByName.computeIfAbsent(type.name(), + n -> (JdiModelTargetType) getInstance(type)); + } + + public synchronized JdiModelTargetType getTargetTypeIfPresent(String name) { + return typesByName.get(name); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetVM.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetVM.java new file mode 100644 index 0000000000..248b9504b1 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetVM.java @@ -0,0 +1,433 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.lang.ProcessHandle.Info; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import com.sun.jdi.PathSearchingVirtualMachine; +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.connect.Connector; +import com.sun.jdi.connect.Connector.Argument; +import com.sun.jdi.event.*; +import com.sun.jdi.request.*; + +import ghidra.async.*; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.jdi.manager.*; +import ghidra.dbg.jdi.manager.impl.JdiManagerImpl; +import ghidra.dbg.jdi.model.iface1.*; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; +import ghidra.lifecycle.Internal; + +/** + * + *

+ * TODO: Implementing {@link TargetLauncher} here doesn't seem right. While it's convenient from a + * UI perspective, it doesn't make sense semantically. + */ +public class JdiModelTargetVM extends JdiModelTargetObjectImpl implements // + TargetProcess, // + TargetAggregate, // + JdiModelTargetEnvironment, // + JdiModelTargetAccessConditioned, // + JdiModelTargetExecutionStateful, // + JdiModelTargetLauncher, // + JdiModelTargetDeletable, // + JdiModelTargetKillable, // + JdiModelTargetResumable, // + JdiModelTargetInterruptible, // + JdiEventsListenerAdapter, // + JdiModelSelectableObject { + + public static final String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid"; + public static final String EXIT_CODE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "exit_code"; + + protected final VirtualMachine vm; + protected boolean trackMonitor = false; + + private Map objectMap; + private Map object2key; + + protected final JdiModelTargetThreadContainer threads; + protected JdiModelTargetThreadGroupContainer threadGroups; + protected JdiModelTargetModuleContainer modules; + protected JdiModelTargetClassContainer classes; + protected final JdiModelTargetProcess process; + protected JdiModelTargetBreakpointContainer breakpoints; + protected JdiModelTargetAttributesContainer addedAttributes; + + private final EventRequestManager eventManager; + private final ThreadStartRequest threadStartRequest; + private final ThreadDeathRequest threadStopRequest; + private final MonitorWaitRequest monitorWaitRequest; + private final MonitorWaitedRequest monitorWaitedRequest; + private final MonitorContendedEnterRequest monitorEnterRequest; + private final MonitorContendedEnteredRequest monitorEnteredRequest; + + public JdiModelTargetVM(JdiModelTargetVMContainer vms, VirtualMachine vm) { + super(vms, vm.name(), vm); + vms.vmsById.put(vm.name(), this); + this.vm = vm; + this.eventManager = vm.eventRequestManager(); + + threadStartRequest = eventManager.createThreadStartRequest(); + threadStopRequest = eventManager.createThreadDeathRequest(); + if (vm.canRequestMonitorEvents() && trackMonitor) { + monitorWaitRequest = eventManager.createMonitorWaitRequest(); + monitorWaitedRequest = eventManager.createMonitorWaitedRequest(); + monitorEnterRequest = eventManager.createMonitorContendedEnterRequest(); + monitorEnteredRequest = eventManager.createMonitorContendedEnteredRequest(); + } + else { + trackMonitor = false; + monitorWaitRequest = null; + monitorWaitedRequest = null; + monitorEnterRequest = null; + monitorEnteredRequest = null; + } + + threadStartRequest.enable(); + threadStopRequest.enable(); + if (vm.canRequestMonitorEvents() && trackMonitor) { + monitorWaitRequest.enable(); + monitorWaitedRequest.enable(); + monitorEnterRequest.enable(); + monitorEnteredRequest.enable(); + } + + Process proc = vm.process(); + if (proc != null) { + this.process = new JdiModelTargetProcess(this, proc); + } + else { + this.process = null; + } + this.threads = new JdiModelTargetThreadContainer(this, "Threads", vm.allThreads()); + + changeAttributes(List.of(), List.of( // + threads // + ), Map.of( // + STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE, // + ACCESSIBLE_ATTRIBUTE_NAME, getAccessibility() == TargetAccessibility.ACCESSIBLE, // + DISPLAY_ATTRIBUTE_NAME, updateDisplay(), // + ARCH_ATTRIBUTE_NAME, vm.name(), // + DEBUGGER_ATTRIBUTE_NAME, vm.description(), // + OS_ATTRIBUTE_NAME, "JRE " + vm.version(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, TargetCmdLineLauncher.PARAMETERS, // + UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // + ), "Initialized"); + listeners.fire(TargetExecutionStateListener.class) + .executionStateChanged(this, TargetExecutionState.ALIVE); + + if (process != null) { + changeAttributes(List.of(), List.of( // + process // + ), Map.of(), "Initialized"); + } + + } + + private void populateAttributes() { + this.addedAttributes = new JdiModelTargetAttributesContainer(this, "Attributes"); + Map attrs = new HashMap<>(); + attrs.put("version", vm.version()); + attrs.put("description", vm.description()); + attrs.put("canAddMethods", Boolean.valueOf(vm.canAddMethod())); + attrs.put("canBeModified", Boolean.valueOf(vm.canBeModified())); + attrs.put("canForceEarlyReturn", Boolean.valueOf(vm.canForceEarlyReturn())); + attrs.put("canGetBytecodes", Boolean.valueOf(vm.canGetBytecodes())); + attrs.put("canGetClassFileVersion", Boolean.valueOf(vm.canGetClassFileVersion())); + attrs.put("canGetConstantPool", Boolean.valueOf(vm.canGetConstantPool())); + attrs.put("canGetCurrentContendedMonitor", + Boolean.valueOf(vm.canGetCurrentContendedMonitor())); + attrs.put("canGetInstanceInfo", Boolean.valueOf(vm.canGetInstanceInfo())); + attrs.put("canGetMethodReturnValues", Boolean.valueOf(vm.canGetMethodReturnValues())); + attrs.put("canGetModuleInfo", Boolean.valueOf(vm.canGetModuleInfo())); + attrs.put("canGetMonitorFrameInfo", Boolean.valueOf(vm.canGetMonitorFrameInfo())); + attrs.put("canGetMonitorInfo", Boolean.valueOf(vm.canGetMonitorInfo())); + attrs.put("canGetOwnedMonitorInfo", Boolean.valueOf(vm.canGetOwnedMonitorInfo())); + attrs.put("canGetSourceDebugExtension", Boolean.valueOf(vm.canGetSourceDebugExtension())); + attrs.put("canGetSyntheticAttribute", Boolean.valueOf(vm.canGetSyntheticAttribute())); + attrs.put("canPopFrames", Boolean.valueOf(vm.canPopFrames())); + attrs.put("canRedefineClasses", Boolean.valueOf(vm.canRedefineClasses())); + attrs.put("canRequestMonitorEvents", Boolean.valueOf(vm.canRequestMonitorEvents())); + attrs.put("canRequestVMDeathEvent", Boolean.valueOf(vm.canRequestVMDeathEvent())); + attrs.put("canUnrestrictedlyRedefineClasses", + Boolean.valueOf(vm.canUnrestrictedlyRedefineClasses())); + attrs.put("canUseInstanceFilters", Boolean.valueOf(vm.canUseInstanceFilters())); + attrs.put("canUseSourceNameFilters", Boolean.valueOf(vm.canUseSourceNameFilters())); + attrs.put("canWatchFieldAccess", Boolean.valueOf(vm.canWatchFieldAccess())); + attrs.put("canWatchFieldModification", Boolean.valueOf(vm.canWatchFieldModification())); + if (vm instanceof PathSearchingVirtualMachine) { + PathSearchingVirtualMachine psvm = (PathSearchingVirtualMachine) vm; + attrs.put("classPath", psvm.classPath()); + attrs.put("baseDirectory", psvm.baseDirectory()); + attrs.put("baseDirectory", psvm.baseDirectory()); + } + addedAttributes.addAttributes(attrs); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + + this.threadGroups = new JdiModelTargetThreadGroupContainer(this); + this.modules = new JdiModelTargetModuleContainer(this); + this.classes = new JdiModelTargetClassContainer(this); + this.breakpoints = new JdiModelTargetBreakpointContainer(this); + + populateAttributes(); + + changeAttributes(List.of(), List.of( // + modules, // + threadGroups, // + classes, // + breakpoints, // + addedAttributes // + ), Map.of(), "Initialized"); + + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture launch(Map args) { + JdiManagerImpl manager = (JdiManagerImpl) impl.getManager(); + Connector cx = manager.getConnector(vm); + Map defaultArguments = cx.defaultArguments(); + Map jdiArgs = JdiModelTargetLauncher.getArguments(defaultArguments, + JdiModelTargetLauncher.getParameters(defaultArguments), args); + return getManager().addVM(cx, jdiArgs).thenApply(__ -> null); + } + + @Override + public TargetParameterMap getParameters() { + JdiManagerImpl manager = (JdiManagerImpl) impl.getManager(); + Connector cx = manager.getConnector(vm); + Map defaultArguments = cx.defaultArguments(); + return TargetParameterMap.copyOf(JdiModelTargetLauncher.getParameters(defaultArguments)); + } + + @Override + public CompletableFuture interrupt() { + vm.suspend(); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture resume() { + vmStateChanged(TargetExecutionState.RUNNING, JdiReason.Reasons.RESUMED); + invalidateMemoryAndRegisterCaches(); + vm.resume(); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture kill() { + vm.exit(0); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture delete() { + vm.dispose(); + return CompletableFuture.completedFuture(null); + } + + public CompletableFuture started(String id) { + AsyncFence fence = new AsyncFence(); + return fence.ready().thenAccept(__ -> { + if (id != null) { + changeAttributes(List.of(), List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE, // + PID_ATTRIBUTE_NAME, id, // + DISPLAY_ATTRIBUTE_NAME, updateDisplay() // + ), "Started"); + } + else { + changeAttributes(List.of(), List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE, // + DISPLAY_ATTRIBUTE_NAME, updateDisplay() // + ), "Started"); + } + listeners.fire(TargetExecutionStateListener.class) + .executionStateChanged(this, TargetExecutionState.ALIVE); + vmSelected(vm, JdiCause.Causes.UNCLAIMED); + }); + } + + protected void exited(VirtualMachine vm2) { + if (vm2 != null) { + changeAttributes(List.of(), List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, TargetExecutionState.TERMINATED, // + EXIT_CODE_ATTRIBUTE_NAME, vm2, // + DISPLAY_ATTRIBUTE_NAME, updateDisplay() // + ), "Exited"); + } + else { + changeAttributes(List.of(), List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, TargetExecutionState.TERMINATED, // + DISPLAY_ATTRIBUTE_NAME, updateDisplay() // + ), "Exited"); + } + listeners.fire(TargetExecutionStateListener.class) + .executionStateChanged(this, TargetExecutionState.TERMINATED); + } + + @Override + public void vmSelected(VirtualMachine eventVM, JdiCause cause) { + if (eventVM.equals(vm)) { + AtomicReference> scope = new AtomicReference<>(); + AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + DebugModelConventions.findSuitable(JdiModelTargetFocusScope.class, this) + .handle(seq::next); + }, scope).then(seq -> { + scope.get().setFocus(this); + }).finish(); + } + } + + public void vmStateChanged(TargetExecutionState targetState, JdiReason reason) { + changeAttributes(List.of(), List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, targetState // + ), reason.desc()); + listeners.fire(TargetExecutionStateListener.class).executionStateChanged(this, targetState); + } + + @Override + public void monitorContendedEntered(MonitorContendedEnteredEvent evt, JdiCause cause) { + System.err.println(this + ":" + evt); + } + + @Override + public void monitorContendedEnter(MonitorContendedEnterEvent evt, JdiCause cause) { + System.err.println(this + ":" + evt); + } + + @Override + public void monitorWaited(MonitorWaitedEvent evt, JdiCause cause) { + System.err.println(this + ":" + evt); + } + + @Override + public void monitorWait(MonitorWaitEvent evt, JdiCause cause) { + System.err.println(this + ":" + evt); + } + + protected void updateDisplayAttribute() { + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, updateDisplay() // + ), "Display changed"); + } + + protected String updateDisplay() { + if (vm.process() == null) { + return vm.toString(); + } + String name = "VM(" + JdiModelTargetProcess.getUniqueId(vm.process()) + ") "; + Info info = vm.process().info(); + Optional arguments = info.arguments(); + if (!arguments.isEmpty()) { + String[] args = arguments.get(); + for (String arg : args) { + if (!arg.startsWith("-")) { + String[] split = arg.split("/"); + name += split.length == 0 ? arg : split[split.length - 1]; + } + } + } + return String.format("%s", name); + } + + @Override + public String getDisplay() { + return vm == null ? super.getDisplay() : updateDisplay(); + } + + protected void invalidateMemoryAndRegisterCaches() { + ///memory.invalidateMemoryCaches(); + } + + protected void updateMemory() { + // This is a little ew. Wish I didn't have to list regions every STOP + /* + memory.update().exceptionally(ex -> { + Msg.error(this, "Could not update process memory mappings", ex); + return null; + }); + */ + } + + @Override + @Internal + public CompletableFuture select() { + return CompletableFuture.completedFuture(null); + } + + public JdiModelTargetClassContainer getClasses() { + return classes; + } + + @Override + public void refresh() { + // TODO Auto-generated method stub + + } + + // OBJECT MAP METHODS + + public synchronized JdiModelTargetObject getTargetObject(String key) { + return objectMap.get(key); + } + + @Override + public synchronized JdiModelTargetObject getTargetObject(Object obj) { + return objectMap.get(object2key.get(obj)); + } + + public synchronized void setTargetObject(String id, Object key, JdiModelTargetObject object) { + if (objectMap == null) { + objectMap = new HashMap<>(); + object2key = new HashMap<>(); + } + if (objectMap.containsKey(id) && key != null) { + if (!(object instanceof JdiModelTargetValue) && + !(object instanceof JdiModelTargetLocation) && + !(object instanceof JdiModelTargetRegister) && + !(object instanceof JdiModelTargetStackFrame)) { + // new RuntimeException(this+":"+key); + System.err.println("setTargetObject: " + key); + } + } + if (key != null) { + object2key.put(key, id); + } + objectMap.put(id, object); + } + + @Override + public TargetAccessibility getAccessibility() { + for (JdiModelTargetThread thread : threads.threadsById.values()) { + if (thread.getAccessibility() == TargetAccessibility.ACCESSIBLE) + return TargetAccessibility.ACCESSIBLE; + } + return TargetAccessibility.INACCESSIBLE; + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetVMContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetVMContainer.java new file mode 100644 index 0000000000..f6e1492889 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetVMContainer.java @@ -0,0 +1,181 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.ThreadReference; +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.event.*; + +import ghidra.async.AsyncUtils; +import ghidra.dbg.jdi.manager.JdiCause; +import ghidra.dbg.jdi.manager.JdiEventsListenerAdapter; +import ghidra.dbg.target.TargetEventScope.TargetEventScopeListener; +import ghidra.dbg.target.TargetEventScope.TargetEventType; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +public class JdiModelTargetVMContainer extends JdiModelTargetObjectImpl + implements JdiEventsListenerAdapter { + + private JdiModelTargetRoot session; + protected final Map vmsById = new WeakValueHashMap<>(); + + public JdiModelTargetVMContainer(JdiModelTargetRoot session) { + super(session, "VirtualMachines"); + this.session = session; + + impl.getManager().addEventsListener(null, this); + } + + @Override + public void vmStarted(VMStartEvent event, JdiCause cause) { + VirtualMachine vm = event.virtualMachine(); + JdiModelTargetVM target = getTargetVM(vm); + // TODO: Move PROCESS_CREATED here to restore proper order of event reporting + // Pending some client-side changes to handle architecture selection, though. + target.started(vm.name()).thenAccept(__ -> { + session.getListeners() + .fire(TargetEventScopeListener.class) + .event(session, null, TargetEventType.PROCESS_CREATED, + "VM " + vm.name() + " started " + vm.process() + " pid=" + vm.name(), + List.of(vm)); + }).exceptionally(ex -> { + Msg.error(this, "Could not notify vm started", ex); + return null; + }); + + changeElements(List.of(), List.of(target), Map.of(), "Added"); + } + + @Override + public void vmDied(VMDeathEvent event, JdiCause cause) { + VirtualMachine vm = event.virtualMachine(); + JdiModelTargetVM tgtVM = vmsById.get(vm.name()); + session.getListeners() + .fire(TargetEventScopeListener.class) + .event(session, null, TargetEventType.PROCESS_EXITED, "VM " + vm.name(), + List.of(tgtVM)); + tgtVM.exited(vm); + synchronized (this) { + vmsById.remove(vm.name()); + getManager().removeVM(vm); + } + changeElements(List.of(vm.name()), List.of(), Map.of(), "Removed"); + } + + protected void gatherThreads(List into, JdiModelTargetVM vm, + Collection from) { + for (ThreadReference t : from) { + JdiModelTargetThread p = vm.threads.getTargetThread(t); + if (p != null) { + into.add(p); + } + } + } + + @Override + public void threadStarted(ThreadStartEvent event, JdiCause cause) { + ThreadReference thread = event.thread(); + JdiModelTargetVM vm = getTargetVM(thread.threadGroup().virtualMachine()); + if (!vmsById.containsValue(vm)) { + Msg.info(this, event + " ignored as vm may have exited"); + return; + } + JdiModelTargetThread targetThread = vm.threads.threadCreated(thread); + session.getListeners() + .fire(TargetEventScopeListener.class) + .event(session, targetThread, TargetEventType.THREAD_CREATED, + "Thread " + thread.name() + " started", List.of(targetThread)); + } + + @Override + public void threadExited(ThreadDeathEvent event, JdiCause cause) { + ThreadReference thread = event.thread(); + JdiModelTargetVM tgtVM = vmsById.get(thread.virtualMachine().name()); + JdiModelTargetThread targetThread = tgtVM.threads.threadsById.get(thread.name()); + session.getListeners() + .fire(TargetEventScopeListener.class) + .event(session, targetThread, TargetEventType.THREAD_EXITED, + "Thread " + thread.name() + " exited", List.of(targetThread)); + tgtVM.threads.threadExited(thread); + } + + @Override + public void libraryLoaded(VirtualMachine vm, String name, JdiCause cause) { + /* + JdiModelTargetVM vm = getTargetInferior(inf); + JdiModelTargetModule module = vm.modules.libraryLoaded(name); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, null, TargetEventType.MODULE_LOADED, + "Library " + name + " loaded", List.of(module)); + */ + } + + @Override + public void libraryUnloaded(VirtualMachine vm, String name, JdiCause cause) { + /* + JdiModelTargetVM vm = getTargetInferior(inf); + JdiModelTargetModule module = vm.modules.getTargetModuleIfPresent(name); + parent.getListeners() + .fire(TargetEventScopeListener.class) + .event(parent, null, TargetEventType.MODULE_UNLOADED, + "Library " + name + " unloaded", List.of(module)); + vm.modules.libraryUnloaded(name); + */ + } + + private void updateUsingVMs(Map byName) { + List vms; + synchronized (this) { + vms = byName.values().stream().map(this::getTargetVM).collect(Collectors.toList()); + } + setElements(vms, Map.of(), "Refreshed"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (!refresh) { + updateUsingVMs(impl.getManager().getKnownVMs()); + return AsyncUtils.NIL; + } + return impl.getManager().listVMs().thenAccept(this::updateUsingVMs); + } + + // NOTE: Does no good to override fetchElement + // Cache should be kept in sync all the time, anyway + + public synchronized JdiModelTargetVM getTargetVM(VirtualMachine vm) { + return vmsById.computeIfAbsent(vm.name(), + i -> new JdiModelTargetVM(this, impl.getManager().getKnownVMs().get(i))); + } + + public synchronized JdiModelTargetVM getTargetVMByName(String name) { + return vmsById.computeIfAbsent(name, + i -> new JdiModelTargetVM(this, impl.getManager().getKnownVMs().get(i))); + } + + protected void invalidateMemoryAndRegisterCaches() { + for (JdiModelTargetVM inf : vmsById.values()) { + inf.invalidateMemoryAndRegisterCaches(); + } + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValue.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValue.java new file mode 100644 index 0000000000..4b6b9742ed --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValue.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import com.sun.jdi.*; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public class JdiModelTargetValue extends JdiModelTargetObjectImpl { + + protected final Value value; + protected final Type type; + + public JdiModelTargetValue(JdiModelTargetObject object, Value value) { + this(object, value.toString(), value); + } + + public JdiModelTargetValue(JdiModelTargetObject object, String id, Value value) { + super(object, id, value); + this.value = value; + this.type = value.type(); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValueContainer.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValueContainer.java new file mode 100644 index 0000000000..af66e19703 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValueContainer.java @@ -0,0 +1,77 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.sun.jdi.Value; + +import ghidra.async.AsyncFence; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public class JdiModelTargetValueContainer extends JdiModelTargetObjectImpl { + + private List values; + + // TODO: Is it possible to load the same object twice? + protected final Map valuesByName = new HashMap<>(); + + public JdiModelTargetValueContainer(JdiModelTargetObject parent, String name, + List vals) { + super(parent, name); + this.values = vals; + } + + protected CompletableFuture updateUsingValues(Map byName) { + List vals; + synchronized (this) { + vals = byName.values().stream().map(this::getTargetValue).collect(Collectors.toList()); + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetValue val : vals) { + fence.include(val.init()); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), vals, Map.of(), "Refreshed"); + }); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + Map map = new HashMap<>(); + try { + for (Value val : values) { + map.put(val.toString(), val); + } + valuesByName.keySet().retainAll(map.keySet()); + } + catch (Exception e) { + e.printStackTrace(); + } + return updateUsingValues(map); + } + + protected synchronized JdiModelTargetValue getTargetValue(Value val) { + return valuesByName.computeIfAbsent(val.toString(), + n -> new JdiModelTargetValue(this, val)); + } + + public synchronized JdiModelTargetValue getTargetValueIfPresent(String name) { + return valuesByName.get(name); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValueMap.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValueMap.java new file mode 100644 index 0000000000..11e00d63fd --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/JdiModelTargetValueMap.java @@ -0,0 +1,71 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.LocalVariable; +import com.sun.jdi.Value; + +import ghidra.async.AsyncFence; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public class JdiModelTargetValueMap extends JdiModelTargetObjectImpl { + + private Map values; + + // TODO: Is it possible to load the same object twice? + protected final Map valuesByVariable = new HashMap<>(); + + public JdiModelTargetValueMap(JdiModelTargetObject parent, Map vals) { + super(parent, "Value Map"); + this.values = vals; + } + + protected CompletableFuture updateUsingValues(Map byName) { + Map vals = new HashMap<>(); + synchronized (this) { + for (LocalVariable key : byName.keySet()) { + Value val = byName.get(key); + if (val != null) { + JdiModelTargetValue targetValue = getTargetValue(key, val); + vals.put(key.name(), targetValue); + } + } + } + AsyncFence fence = new AsyncFence(); + for (JdiModelTargetValue var : vals.values()) { + fence.include(var.init()); + } + return fence.ready().thenAccept(__ -> { + setAttributes(List.of(), vals, "Refreshed"); + }); + } + + @Override + public CompletableFuture requestAttributes(boolean refresh) { + return updateUsingValues(values); + } + + protected synchronized JdiModelTargetValue getTargetValue(LocalVariable var, Value val) { + return valuesByVariable.computeIfAbsent(var, n -> new JdiModelTargetValue(this, val)); + } + + public synchronized JdiModelTargetValue getTargetValueIfPresent(LocalVariable var) { + return valuesByVariable.get(var); + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelSelectableObject.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelSelectableObject.java new file mode 100644 index 0000000000..1f2415cb74 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelSelectableObject.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; + +public interface JdiModelSelectableObject extends JdiModelTargetObject { + + public CompletableFuture select(); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetAccessConditioned.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetAccessConditioned.java new file mode 100644 index 0000000000..5cca69c099 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetAccessConditioned.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetAccessConditioned; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetAccessConditioned> + extends JdiModelTargetObject, TargetAccessConditioned { + + @Override + public TargetAccessibility getAccessibility(); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetAttacher.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetAttacher.java new file mode 100644 index 0000000000..cd4d499653 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetAttacher.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetAttachable; +import ghidra.dbg.target.TargetAttacher; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetAttacher> + extends JdiModelTargetObject, TargetAttacher { + + @Override + public CompletableFuture attach( + TypedTargetObjectRef> ref); + + @Override + public CompletableFuture attach(long pid); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetConsole.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetConsole.java new file mode 100644 index 0000000000..9d684fc878 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetConsole.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetConsole; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetConsole> + extends JdiModelTargetObject, TargetConsole { + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetDeletable.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetDeletable.java new file mode 100644 index 0000000000..601761ef03 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetDeletable.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetDeletable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetDeletable> + extends JdiModelTargetObject, TargetDeletable { + + @Override + public CompletableFuture delete(); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetDetachable.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetDetachable.java new file mode 100644 index 0000000000..c76f0d22ce --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetDetachable.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetDetachable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetDetachable> + extends JdiModelTargetObject, TargetDetachable { + + @Override + public CompletableFuture detach(); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetEnvironment.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetEnvironment.java new file mode 100644 index 0000000000..65ae05e199 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetEnvironment.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetEnvironment; + +public interface JdiModelTargetEnvironment> + extends JdiModelTargetObject, TargetEnvironment { + + public void refresh(); + + @Override + public default String getArchitecture() { + return getTypedAttributeNowByName(ARCH_ATTRIBUTE_NAME, String.class, ""); + } + + @Override + public default String getDebugger() { + return getTypedAttributeNowByName(DEBUGGER_ATTRIBUTE_NAME, String.class, ""); + } + + @Override + public default String getOperatingSystem() { + return getTypedAttributeNowByName(OS_ATTRIBUTE_NAME, String.class, ""); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetEventScope.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetEventScope.java new file mode 100644 index 0000000000..581169d28b --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetEventScope.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetEventScope; + +/** + * The object can emit events affecting itself and its successors + * + * @param type for this + */ +public interface JdiModelTargetEventScope> + extends JdiModelTargetObject, TargetEventScope { + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetExecutionStateful.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetExecutionStateful.java new file mode 100644 index 0000000000..4376742a0b --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetExecutionStateful.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import java.util.List; +import java.util.Map; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetExecutionStateful; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetExecutionStateful> + extends JdiModelTargetObject, TargetExecutionStateful { + + public default void setExecutionState(TargetExecutionState state, String reason) { + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, state // + ), reason); + getListeners().fire(TargetExecutionStateListener.class).executionStateChanged(this, state); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetFocusScope.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetFocusScope.java new file mode 100644 index 0000000000..17629f308f --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetFocusScope.java @@ -0,0 +1,86 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetFocusScope; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetFocusScope> + extends JdiModelTargetObject, TargetFocusScope { + + @Override + public JdiModelSelectableObject getFocus(); + + // NB: setFocus changes attributes - propagates up to client + public boolean setFocus(JdiModelSelectableObject sel); + + // NB: requestFocus request change in active object - propagates down to manager + // (but, of course, may then cause change in state) + @Override + public default CompletableFuture requestFocus(TargetObjectRef ref) { + getModel().assertMine(TargetObjectRef.class, ref); + if (ref.equals(getFocus())) { + return CompletableFuture.completedFuture(null); + } + if (!PathUtils.isAncestor(this.getPath(), ref.getPath())) { + throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope"); + } + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + ref.fetch().handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + TargetObject cur = obj; + while (cur != null) { + if (cur instanceof JdiModelSelectableObject) { + JdiModelSelectableObject sel = (JdiModelSelectableObject) cur; + sel.select().handle(seq::exit); + AtomicReference> scope = new AtomicReference<>(); + AsyncUtils.sequence(TypeSpec.VOID).then(seqx -> { + DebugModelConventions.findSuitable(JdiModelTargetFocusScope.class, sel) + .handle(seqx::next); + }, scope).then(seqx -> { + scope.get().setFocus(sel); + }).finish(); + break; + } + if (cur instanceof JdiModelTargetObject) { + JdiModelTargetObject def = (JdiModelTargetObject) cur; + cur = def.getImplParent(); + continue; + } + throw new AssertionError(); + } + seq.exit(); + }).finish(); + } + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetInterruptible.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetInterruptible.java new file mode 100644 index 0000000000..09540853dd --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetInterruptible.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetInterruptible; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetInterruptible> + extends JdiModelTargetObject, TargetInterruptible { + + @Override + public CompletableFuture interrupt(); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetKillable.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetKillable.java new file mode 100644 index 0000000000..e23fd09f6b --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetKillable.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetKillable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetKillable> + extends JdiModelTargetObject, TargetKillable { + + @Override + public CompletableFuture kill(); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetLauncher.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetLauncher.java new file mode 100644 index 0000000000..72d29dc54d --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetLauncher.java @@ -0,0 +1,95 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import java.util.Map; +import java.util.stream.Collectors; + +import com.sun.jdi.connect.Connector.*; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetLauncher; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.TargetMethod.ParameterDescription; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetLauncher> + extends JdiModelTargetObject, TargetLauncher { + + static ParameterDescription createBooleanParameter(BooleanArgument arg) { + return ParameterDescription.create(Boolean.class, arg.name(), arg.mustSpecify(), + arg.booleanValue(), arg.label(), arg.description()); + } + + static ParameterDescription createIntegerParameter(IntegerArgument arg) { + return ParameterDescription.create(Integer.class, arg.name(), arg.mustSpecify(), + arg.intValue(), arg.label(), arg.description()); + } + + static ParameterDescription createStringParameter(StringArgument arg) { + return createGenericParameter(arg); + } + + static ParameterDescription createSelectedParameter(SelectedArgument arg) { + return ParameterDescription.choices(String.class, arg.name(), arg.choices(), + arg.label(), arg.description()); + } + + static ParameterDescription createGenericParameter(Argument arg) { + return ParameterDescription.create(String.class, arg.name(), arg.mustSpecify(), + arg.value(), arg.label(), arg.description()); + } + + static ParameterDescription createParameter(Argument arg) { + if (arg instanceof BooleanArgument) { + return createBooleanParameter((BooleanArgument) arg); + } + if (arg instanceof IntegerArgument) { + return createIntegerParameter((IntegerArgument) arg); + } + if (arg instanceof StringArgument) { + return createStringParameter((StringArgument) arg); + } + if (arg instanceof SelectedArgument) { + return createSelectedParameter((SelectedArgument) arg); + } + return createGenericParameter(arg); + } + + static Map> getParameters( + Map defaultArguments) { + return defaultArguments.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> createParameter(e.getValue()))); + } + + static Map getArguments(Map defaultArguments, + Map> parameters, Map arguments) { + Map validated = TargetMethod.validateArguments(parameters, arguments, false); + for (Argument arg : defaultArguments.values()) { + Object val = parameters.get(arg.name()).get(validated); + // Eh, we could probably avoid the round-trip string conversion. Is it needed? No. + arg.setValue(val == null ? null : val.toString()); + } + return defaultArguments; + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetMethod.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetMethod.java new file mode 100644 index 0000000000..be9f0fc3d6 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetMethod.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetMethod; + +/** + * An interface which indicates this object is a method in its parent. + * + * @param type for this + */ +public interface JdiModelTargetMethod> + extends JdiModelTargetObject, TargetMethod { + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetResumable.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetResumable.java new file mode 100644 index 0000000000..e48a3518c7 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetResumable.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetResumable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetResumable> + extends JdiModelTargetObject, TargetResumable { + + @Override + public CompletableFuture resume(); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetSteppable.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetSteppable.java new file mode 100644 index 0000000000..c76771b9d4 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface1/JdiModelTargetSteppable.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject; +import ghidra.dbg.target.TargetSteppable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface JdiModelTargetSteppable> + extends JdiModelTargetObject, TargetSteppable { + + @Override + CompletableFuture step(TargetStepKind kind); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface2/JdiModelTargetAttachable.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface2/JdiModelTargetAttachable.java new file mode 100644 index 0000000000..685bc07c92 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface2/JdiModelTargetAttachable.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface2; + +import ghidra.dbg.target.TargetAttachable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + */ +public interface JdiModelTargetAttachable + extends JdiModelTargetObject, TargetAttachable { + + public long getId(); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface2/JdiModelTargetObject.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface2/JdiModelTargetObject.java new file mode 100644 index 0000000000..4a4bf6aeb2 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/model/iface2/JdiModelTargetObject.java @@ -0,0 +1,97 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model.iface2; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.sun.jdi.*; + +import ghidra.dbg.agent.InvalidatableTargetObjectIf; +import ghidra.dbg.jdi.manager.JdiManager; +import ghidra.dbg.jdi.model.*; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.CollectionUtils.Delta; +import ghidra.util.datastruct.ListenerSet; + +public interface JdiModelTargetObject extends TargetObject, InvalidatableTargetObjectIf { + + String THREAD_ATTRIBUTE_NAME = "Thread"; + String THIS_OBJECT_ATTRIBUTE_NAME = "This"; + String LOCATION_ATTRIBUTE_NAME = "Location"; + + public JdiModelImpl getModelImpl(); + + public default JdiManager getManager() { + return getModelImpl().getManager(); + } + + public default CompletableFuture init(Map map) { + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture> fetchElements(); + + @Override + public CompletableFuture> fetchAttributes(); + + public TargetObject getImplParent(); + + public Delta changeAttributes(List remove, Map add, String reason); + + public ListenerSet getListeners(); + + public default JdiModelTargetObject getInstance(Mirror object) { + JdiModelTargetObject targetObject = getTargetObject(object); + if (targetObject == null) { + if (object instanceof ThreadReference) { + ThreadReference thread = (ThreadReference) object; + targetObject = new JdiModelTargetThread(this, thread); + } + else if (object instanceof ObjectReference) { + ObjectReference ref = (ObjectReference) object; + targetObject = new JdiModelTargetObjectReference(this, ref); + } + else if (object instanceof ReferenceType) { + ReferenceType reftype = (ReferenceType) object; + targetObject = new JdiModelTargetReferenceType(this, reftype); + } + else if (object instanceof Field) { + Field field = (Field) object; + targetObject = new JdiModelTargetField(this, field); + } + else if (object instanceof Method) { + Method method = (Method) object; + targetObject = new JdiModelTargetMethod(this, method); + } + else if (object instanceof Type) { + Type type = (Type) object; + targetObject = new JdiModelTargetType(this, type); + } + else { + throw new RuntimeException(); + } + } + return targetObject; + } + + public JdiModelTargetObject getTargetObject(Object object); + + public Object getObject(); + +} diff --git a/Ghidra/Debug/Debugger-jpda/src/test/java/ghidra/dbg/jdi/JdiExperimentsTest.java b/Ghidra/Debug/Debugger-jpda/src/test/java/ghidra/dbg/jdi/JdiExperimentsTest.java new file mode 100644 index 0000000000..9ed5f9463e --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/test/java/ghidra/dbg/jdi/JdiExperimentsTest.java @@ -0,0 +1,302 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.List; +import java.util.Map; + +import org.junit.Ignore; +import org.junit.Test; + +import com.sun.jdi.*; +import com.sun.jdi.connect.*; +import com.sun.jdi.connect.Connector.Argument; +import com.sun.jdi.event.*; +import com.sun.jdi.request.*; + +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; + +@Ignore("These crash in Gradle") +public class JdiExperimentsTest { + protected VirtualMachineManager vmm = Bootstrap.virtualMachineManager(); + + @Test + public void testArguments() throws Exception { + LaunchingConnector conn = vmm.defaultConnector(); + Map args = conn.defaultArguments(); + Msg.debug(this, "Args: " + args); + } + + public static class HelloWorld { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } + } + + @Test + public void testSimpleLaunch() throws Exception { + LaunchingConnector conn = vmm.defaultConnector(); + Map args = conn.defaultArguments(); + args.get("options").setValue("-cp \"" + System.getProperty("java.class.path") + "\""); + args.get("main").setValue(HelloWorld.class.getName()); + VirtualMachine vm = conn.launch(args); + + for (Event evt : vm.eventQueue().remove(1000)) { + Msg.debug(this, "Event: " + evt); + assertTrue(evt instanceof VMStartEvent); + } + + Msg.info(this, "Version: " + vm.version()); + Msg.info(this, "Description: " + vm.description()); + Msg.info(this, "Name: " + vm.name()); + + vm.resume(); + + BufferedReader reader = + new BufferedReader(new InputStreamReader(vm.process().getInputStream())); + String hw = reader.readLine(); + assertEquals("Hello, World!", hw); + + vm.dispose(); + } + + @Test + public void testSimpleSocketAttachJDWP() throws Exception { + // Launch a VM so that we have an "existing process" + ProcessBuilder pb = new ProcessBuilder("java", + "-agentlib:jdwp=transport=dt_socket,address=0,server=y,suspend=y", + "-cp", System.getProperty("java.class.path"), + HelloWorld.class.getName()); + Process hwProc = pb.start(); + + // The JDWP Agent will print the open port before suspending + BufferedReader reader = + new BufferedReader(new InputStreamReader(hwProc.getInputStream())); + String listenLine = reader.readLine(); + Msg.info(this, listenLine); + assertTrue(listenLine.startsWith("Listening")); + String[] parts = listenLine.split("\\s+"); + String port = parts[parts.length - 1]; + // OK, everything above simulates existing process, now the real connection begins + + AttachingConnector tcpConn = vmm.attachingConnectors() + .stream() + .filter(c -> c.defaultArguments().containsKey("hostname")) + .findFirst() + .orElse(null); + Map args = tcpConn.defaultArguments(); + args.get("hostname").setValue("localhost"); + args.get("port").setValue(port); + VirtualMachine vm = tcpConn.attach(args); + + for (Event evt : vm.eventQueue().remove(1000)) { + Msg.debug(this, "Event: " + evt); + assertTrue(evt instanceof VMStartEvent); + } + + vm.resume(); + + String hw = reader.readLine(); + assertEquals("Hello, World!", hw); + + vm.dispose(); + } + + @Test + public void testSimpleProcessAttachJDWP() throws Exception { + // Launch a VM so that we have an "existing process" + ProcessBuilder pb = new ProcessBuilder("java", + "-agentlib:jdwp=transport=dt_socket,address=0,server=y,suspend=y", + "-cp", System.getProperty("java.class.path"), + HelloWorld.class.getName()); + Process hwProc = pb.start(); + + // The JDWP Agent will print the open port before suspending + BufferedReader reader = + new BufferedReader(new InputStreamReader(hwProc.getInputStream())); + String listenLine = reader.readLine(); + // We don't need the port, but we still use this to wait for listen + Msg.info(this, listenLine); + // OK, everything above simulates existing process, now the real connection begins + + AttachingConnector procConn = vmm.attachingConnectors() + .stream() + .filter(c -> c.defaultArguments().containsKey("pid")) + .findFirst() + .orElse(null); + Map args = procConn.defaultArguments(); + args.get("pid").setValue("" + hwProc.pid()); + VirtualMachine vm = procConn.attach(args); + + for (Event evt : vm.eventQueue().remove(1000)) { + Msg.debug(this, "Event: " + evt); + assertTrue(evt instanceof VMStartEvent); + } + + vm.resume(); + + String hw = reader.readLine(); + assertEquals("Hello, World!", hw); + + vm.dispose(); + } + + @Test + public void testSimpleListenAttachJDWP() throws Exception { + ListeningConnector lConn = vmm.listeningConnectors() + .stream() + .filter(c -> c.defaultArguments().containsKey("localAddress")) + .findFirst() + .orElse(null); + + Map args = lConn.defaultArguments(); + args.get("port").setValue("0"); + args.get("localAddress").setValue("localhost"); + String addr = lConn.startListening(args); + + ProcessBuilder pb = new ProcessBuilder("java", + "-agentlib:jdwp=transport=dt_socket,address=" + addr + ",server=n,suspend=y", + "-cp", System.getProperty("java.class.path"), + HelloWorld.class.getName()); + Process hwProc = pb.start(); + + VirtualMachine vm = lConn.accept(args); + + for (Event evt : vm.eventQueue().remove(1000)) { + Msg.debug(this, "Event: " + evt); + assertTrue(evt instanceof VMStartEvent); + } + + vm.resume(); + + BufferedReader reader = + new BufferedReader(new InputStreamReader(hwProc.getInputStream())); + String hw = reader.readLine(); + assertEquals("Hello, World!", hw); + + vm.dispose(); + } + + @Test + @Ignore("Enable after you've manually launched a target") + public void testAtttachJDWP() throws Exception { + AttachingConnector tcpConn = vmm.attachingConnectors() + .stream() + .filter(c -> c.defaultArguments().containsKey("hostname")) + .findFirst() + .orElse(null); + Map args = tcpConn.defaultArguments(); + args.get("hostname").setValue("localhost"); + args.get("port").setValue("8000"); + VirtualMachine vm = tcpConn.attach(args); + + /*for (Event evt : vm.eventQueue().remove(1000)) { + Msg.debug(this, "Event: " + evt); + assertTrue(evt instanceof VMStartEvent); + }*/ + + Msg.info(this, "Version: " + vm.version()); + Msg.info(this, "Description: " + vm.description()); + Msg.info(this, "Name: " + vm.name()); + } + + @Test + public void testWhatIsCodeIndex() throws Exception { + LaunchingConnector conn = vmm.defaultConnector(); + Map args = conn.defaultArguments(); + args.get("options").setValue("-cp \"" + System.getProperty("java.class.path") + "\""); + args.get("main").setValue(HelloWorld.class.getName()); + + VirtualMachine vm = conn.launch(args); + for (Event evt : vm.eventQueue().remove(1000)) { + Msg.debug(this, "Event: " + evt); + assertTrue(evt instanceof VMStartEvent); + } + + Msg.debug(this, "Resuming with request"); + //MethodEntryRequest meReq = vm.eventRequestManager().createMethodEntryRequest(); + //meReq.enable(); + + ClassPrepareRequest cpReq = vm.eventRequestManager().createClassPrepareRequest(); + cpReq.enable(); + + vm.resume(); + + untilHw: while (true) { + for (Event evt : vm.eventQueue().remove(1000)) { + Msg.debug(this, "Event: " + evt); + if (evt instanceof ClassPrepareEvent) { + ClassPrepareEvent cpEvt = (ClassPrepareEvent) evt; + Msg.debug(this, " Type: " + cpEvt.referenceType()); + if (cpEvt.referenceType().name().contains(HelloWorld.class.getSimpleName())) { + break untilHw; + } + } + } + vm.resume(); + } + + cpReq.disable(); + + List hwClasses = vm.classesByName(HelloWorld.class.getName()); + assertEquals(1, hwClasses.size()); + + ReferenceType hwClass = hwClasses.get(0); + List hwMainMethods = hwClass.methodsByName("main"); + assertEquals(1, hwMainMethods.size()); + Method hwMainMethod = hwMainMethods.get(0); + + Msg.debug(this, "Code: " + NumericUtilities.convertBytesToString(hwMainMethod.bytecodes())); + + BreakpointRequest bpMainReq = + vm.eventRequestManager().createBreakpointRequest(hwMainMethod.location()); + bpMainReq.enable(); + + vm.resume(); + + ThreadReference thread = null; + for (Event evt : vm.eventQueue().remove(1000)) { + Msg.debug(this, "Event: " + evt); + assertTrue(evt instanceof BreakpointEvent); + BreakpointEvent bpMainEvt = (BreakpointEvent) evt; + thread = bpMainEvt.thread(); + } + StepRequest stepReq = vm.eventRequestManager() + .createStepRequest(thread, StepRequest.STEP_MIN, + StepRequest.STEP_INTO); + stepReq.enable(); + + while (thread.frame(0).location().method() == hwMainMethod) { + Location loc = thread.frame(0).location(); + Msg.debug(this, String.format("Mth=%s,Idx=%s", loc.method(), loc.codeIndex())); + vm.resume(); + } + stepReq.disable(); + vm.resume(); + + BufferedReader reader = + new BufferedReader(new InputStreamReader(vm.process().getInputStream())); + String hw = reader.readLine(); + assertEquals("Hello, World!", hw); + + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/test/java/ghidra/dbg/jdi/model/JdiModelTest.java b/Ghidra/Debug/Debugger-jpda/src/test/java/ghidra/dbg/jdi/model/JdiModelTest.java new file mode 100644 index 0000000000..2056b5dea5 --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/test/java/ghidra/dbg/jdi/model/JdiModelTest.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.model; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.*; + +import generic.Unique; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.jdi.JdiExperimentsTest; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.util.DebuggerModelTestUtils; +import ghidra.dbg.util.PathUtils; +import ghidra.util.Msg; + +public class JdiModelTest implements DebuggerModelTestUtils { + DebuggerObjectModel model; + + @Before + public void setUp() { + model = new JdiModelImpl(); + } + + @After + public void tearDown() { + model.close(); + } + + @Test + public void testConnectorParameterReflection() throws Throwable { + for (TargetObjectRef connRef : waitOn(model.fetchObjectElements("Connectors")).values()) { + TargetLauncher launcher = waitOn(connRef.as(TargetLauncher.tclass).fetch()); + Msg.info(this, "Launcher: " + launcher); + for (ParameterDescription desc : launcher.getParameters().values()) { + Msg.info(this, " " + desc); + } + } + } + + @Test + @Ignore("TODO") // Not important + public void testCommandLineLauncher() throws Throwable { + TargetLauncher launcher = (TargetLauncher) waitOn( + model.fetchModelObject(PathUtils.parse("Connectors[com.sun.jdi.CommandLineLaunch]"))); + Map parameters = new HashMap<>(); + parameters.put("main", JdiExperimentsTest.HelloWorld.class.getName()); + parameters.put("quote", "\""); + parameters.put("vmexec", "java"); + waitOn(launcher.launch(parameters)); + + TargetObjectRef vmRef = + Unique.assertOne(waitOn(model.fetchObjectElements("VirtualMachines")).values()); + TargetObject vm = waitOn(vmRef.fetch()); + waitOn(((TargetKillable) vm).kill()); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/Module.manifest b/Ghidra/Debug/Debugger-sctl/Module.manifest new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Ghidra/Debug/Debugger-sctl/build.gradle b/Ghidra/Debug/Debugger-sctl/build.gradle new file mode 100644 index 0000000000..412bedff7c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/build.gradle @@ -0,0 +1,21 @@ +apply from: "${rootProject.projectDir}/gradle/javaProject.gradle" +apply from: "${rootProject.projectDir}/gradle/jacocoProject.gradle" +apply from: "${rootProject.projectDir}/gradle/javaTestProject.gradle" +apply from: "${rootProject.projectDir}/gradle/distributableGhidraModule.gradle" + +apply plugin: 'eclipse' +eclipse.project.name = 'Debug Debugger-sctl' + +dependencies { + compile project(':Framework-AsyncComm') + compile project(':Framework-Debugging') + compile project(':ProposedUtils') + + testCompile project(path: ':Framework-Debugging', configuration: 'testArtifacts') +} + +test { + if ("linux64".equals(getCurrentPlatformName())) { + dependsOn(":Framework-Debugging:testSpecimenLinux64") + } +} diff --git a/Ghidra/Debug/Debugger-sctl/certification.manifest b/Ghidra/Debug/Debugger-sctl/certification.manifest new file mode 100644 index 0000000000..f97a3aacc3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/certification.manifest @@ -0,0 +1,5 @@ +##VERSION: 2.0 +.classpath||NONE||reviewed||END| +.project||NONE||reviewed||END| +Module.manifest||GHIDRA||||END| +build.gradle||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlClient.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlClient.java new file mode 100644 index 0000000000..2851ab1587 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlClient.java @@ -0,0 +1,1824 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import static ghidra.async.AsyncUtils.loop; +import static ghidra.async.AsyncUtils.sequence; +import static ghidra.lifecycle.Unfinished.TODO; + +import java.io.EOFException; +import java.io.InvalidObjectException; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.commons.lang3.StringUtils; + +import com.google.common.cache.RemovalNotification; + +import ghidra.async.*; +import ghidra.comm.packet.AsynchronousPacketChannel; +import ghidra.comm.packet.AsynchronousPacketDebugChannel; +import ghidra.dbg.DebuggerModelClosedReason; +import ghidra.dbg.agent.AbstractDebuggerObjectModel; +import ghidra.dbg.sctl.client.depr.DebuggerAddressMapper; +import ghidra.dbg.sctl.client.depr.DefaultDebuggerAddressMapper; +import ghidra.dbg.sctl.client.err.*; +import ghidra.dbg.sctl.dialect.SctlDialect; +import ghidra.dbg.sctl.err.SctlError; +import ghidra.dbg.sctl.protocol.*; +import ghidra.dbg.sctl.protocol.common.*; +import ghidra.dbg.sctl.protocol.common.notify.*; +import ghidra.dbg.sctl.protocol.common.notify.AbstractSctlListsLibrariesEventNotification.PathBase; +import ghidra.dbg.sctl.protocol.common.reply.*; +import ghidra.dbg.sctl.protocol.common.request.*; +import ghidra.dbg.sctl.protocol.v2012base.Sctl2012SnapNotification; +import ghidra.dbg.sctl.protocol.v2012ext.SctlExecuteReply; +import ghidra.dbg.sctl.protocol.v2012ext.SctlExecuteRequest; +import ghidra.dbg.sctl.protocol.v2012ext.x86.linux.Sctl2012ExtLinuxX86Dialect; +import ghidra.dbg.sctl.protocol.v2018base.*; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetObject.TargetObjectListener; +import ghidra.dbg.target.TargetSymbol; +import ghidra.dbg.util.PathUtils; +import ghidra.lifecycle.Internal; +import ghidra.program.model.address.*; +import ghidra.util.Msg; +import ghidra.util.datastruct.ListenerSet; + +/** + * A debugger client implementation that communicates with an external debugger via SCTL-bus + * + * SCTL-bus, created by the GHIDRA team, is an extension to SCTL. The SCTL protocol is specified for + * Cinquecento. Details are available at their website + * cqctworld.org. Details for the SCTL-bus extension are + * available within {@link Sctl2012ExtLinuxX86Dialect}. It is backward compatible with standard + * SCTL, so this client can control the sctl process control server for Linux out-of-box. + * + * This fully implements the Ghidra debugging API with some notable limitations, including but + * certainly not limited to the following: + * + * 1) The step command is not properly confirmed. The SCTL protocol's {@code Tstep} command requires + * the step to complete before the {@code Rstep} response is given. Thus, it is difficult to tell if + * the command was dropped, or if the step is taking a long time. This can happen if, e.g., the + * stepped instruction is a system call. As a result, the step instruction may time out, even though + * the target thread has entered the {@link TargetExecutionState#RUNNING} state. A better model is + * exemplified by GDB, which may provide a model for extending SCTL further. The step should + * complete as soon as the target is running. When the step completes, a stop notification should be + * sent indicating "step completed" as the reason. This is a minor issue, though. As a workaround, + * this implementation assumes the thread is {@link TargetExecutionState#RUNNING} immediately after + * {@code Tstep} is sent. Once the result is returned (whether successful, erroneous, or timed out), + * the thread re-enters the {@link TargetExecutionState#STOPPED} state. In most circumstances, this + * workaround is unnoticeable. When it does time out, it is usually because the remote thread is + * actually running. When it times out, the client will go out of sync. However, when the step + * completes on the remote side, it will generate a notification. The reason may not make sense, but + * at least the client and server will be back in sync. + * + * 2) It may not be possible to attach to a multi-threaded process. SCTL's {@code Tattach} request + * accepts only a PID, not a thread ID, but it {@code Rattach} response only gives a single CTLID, + * which corresponds to a single thread. Thus, it's impossible to attach to a multi-threaded process + * without a workaround. For the standard sctl server, there is no workaround. In fact, the server + * behaves oddly and may crash in these circumstances. A workaround if SCTL-bus is used is for the + * server to synthesize additional {@code Tattach} requests for the same PID, providing a unique + * CTLID in the {@code Rattach} for each thread. This may seem odd, though, since the repeated + * requests all look identical, but it works well. + * + * 3) Ghidra does not (yet) support or understand the snapshot command. This is one of the hallmarks + * of SCTL, but this feature is not necessary to synchronize the GUI with the debugger's CLI. This + * client implementation does, however, correctly interpret snapshot requests, replies, and + * notifications sent by other clients on the SCTL-bus. It will create a copy of the target, as + * specified by SCTL, but it will be presented to Ghidra as a fork event. Use of this feature would + * require a SCTL-bus to standard SCTL proxy, or a bus-aware process control server that implements + * snapshots. + * + * 4) SCTL only transfers a fixed set of x86 registers. It currently provides no mechanism to + * request other arbitrary registers. As a result, this client only supports x86 architectures + * (including x86_64), and only those specific registers may be read or written. Future versions + * will likely introduce a more generic "dialect" of SCTL, permitting the client to control targets + * of any architecture and access all known registers. The protocol is also currently limited to + * registers of at most 64 bits. + * + * 5) SCTL only allows reading and writing bytes to a single memory. Granted, there is a lot of + * flexibility in mapping addresses to a single spaces, esp. with 64 address bits, but this can lead + * to confusion if the client and server do not agree on the mapping scheme. Currently, this is not + * an issue, since x86 supports only one memory. As new architectures are added, this design + * limitation must be addressed. Notably, the {@code Tread} request has an {@code fd[8]} field, + * which might be used to encode alternative address spaces. + * + * 6) SCTL allows the client to request any arbitrary type known to a given namespace. The Ghidra + * {@link LegacyDebuggerClient} API does not support this. A type can only be retrieved via a + * {@link TargetSymbol} object or by requesting all types. Thus, a type that is never used cannot be + * retrieved singly. + * + * 7) This client introduces two assumptions to Ghidra's debugging model. Threads belonging to the + * same process share the same memory and namespaces. Suppose a process contains threads A and B. If + * A loads a new library, that same library is assumed to be available in B. If memory is read from + * A, it is stored in a cache shared with B. This assumption should be true for processes on x86 + * Linux. When other systems are supported, these assumptions may need to be re-addressed. + * + * 8) SCTL provides a means of transferring data types and their definitions, if available, from the + * target. It even provides descriptions of the base C data types. Currently, those base + * descriptions are ignored in favor of what is defined by Ghidra's language modules. Furthermore, + * bit fields are not yet supported by Ghidra, yet SCTL can describe them, so they are simply not + * converted. + */ +public class SctlClient extends AbstractDebuggerObjectModel { + private static final int MAX_OUTSTANDING_REQUESTS = 1000; + private static final int REQUEST_TIMEOUT_MILLIS = Integer.MAX_VALUE; // TODO: Consider no timeout + + private enum ConnectionState { + INITIAL(false, false), + CONNECTING(true, false), + CONNECTED(true, false), + DISCONNECTED(false, true); + + final boolean isOpen; + final boolean isTerminate; + + ConnectionState(boolean isOpen, boolean isTerminate) { + this.isOpen = isOpen; + this.isTerminate = isTerminate; + } + } + + protected class SctlPairingCache extends AsyncPairingCache { + public SctlPairingCache() { + super(4, REQUEST_TIMEOUT_MILLIS, MAX_OUTSTANDING_REQUESTS); + } + + @Override + protected void resultRemoved(RemovalNotification rn) { + if (rn.wasEvicted()) { + Msg.error(this, + "Received SCTL reply for unmatched tag: " + rn.getKey()); + } + } + + @Override + protected void promiseRemoved( + RemovalNotification> rn) { + if (rn.wasEvicted()) { + String message = + "Command with tag " + rn.getKey() + " evicted because " + rn.getCause(); + Msg.error(this, message); + // This thread may hold a lock. Offload completion to avoid deadlocks. + AsyncUtils.FRAMEWORK_EXECUTOR.execute( + () -> rn.getValue().completeExceptionally(new TimeoutException(message))); + } + } + } + + private Collection restrictedDialects = SctlVersionInfo.KNOWN_DIALECTS; + + private ConnectionState connectionState = ConnectionState.INITIAL; + private final String description; + private final AsynchronousPacketChannel packetChannel; + private final SctlMarshaller marshaller = new SctlMarshaller(); + + private SctlDialect activeDialect = SctlDialect.NULL_DIALECT; + + private int idOnBus = 0; + private int nextTag = 0; + + // TODO: These are meant to be per-object, not global + protected final ListenerSet listenersObject = + new ListenerSet<>(TargetObjectListener.class); + + protected final SctlTargetSession session; + + private final SctlPairingCache packetMatcher = new SctlPairingCache(); + + @Internal + public final DebuggerAddressMapper addrMapper; + + /** + * Construct a new client on the given channel with the given address mapper + * + * SCTL is usually carried over TCP/IP. More than likely, {@code channel} should be an instance + * of {@link AsynchronousSocketChannel} that is already connected to the server. Though the TCP + * connection should already be established, no other messages should be sent before the channel + * is given to the client. The client will perform the SCTL version negotiation upon calling + * {@link #connect()}. To close the SCTL connection, simply close the channel. By + * {@link LegacyDebuggerClient} convention, {@link #disconnect()} should be called before + * closing the channel. + * + * @param channel a channel connected to the process control server + * @param addrMapper a mapper of Ghidra addresses to debugger addresses + */ + public SctlClient(String description, AsynchronousByteChannel channel, + DebuggerAddressMapper addrMapper) { + this.description = description; + this.packetChannel = new AsynchronousPacketDebugChannel<>(channel, marshaller); + marshaller.setPacketFactory(activeDialect.getPacketFactory()); + this.addrMapper = addrMapper; + this.session = new SctlTargetSession(this); + } + + /** + * Construct a new client on the given channel with the default address mapper + * + * @see #SctlClient(AsynchronousByteChannel, DebuggerAddressMapper) + * @param channel a channel connected to the process control server + */ + public SctlClient(String description, AsynchronousByteChannel channel) { + this(description, channel, DefaultDebuggerAddressMapper.INSTANCE); + } + + public SctlClient setDialects(SctlDialect... dialects) { + return this.setDialects(Arrays.asList(dialects)); + } + + public SctlClient setDialects(Collection dialects) { + this.restrictedDialects = dialects; + return this; + } + + /** + * Change the client's SCTL-bus ID + * + * This concept is an extension to SCTL. When multiple clients are issuing commands, the bus ID + * allows each client to identify the other clients. This does not often need to be changed. + * Future versions may incorporate a mechanism for changing this automatically to avoid ID + * conflicts among multiple clients. + * + * @param idOnBus the new ID + */ + public void setIdOnBus(int idOnBus) { + this.idOnBus = idOnBus; + } + + /** + * Get the client's SCTL-bus ID + * + * @see #setIdOnBus(int) + * @return the current bus ID + */ + public int getIdOnBus() { + return idOnBus; + } + + @Override + public boolean isAlive() { + return connectionState == ConnectionState.CONNECTED; + } + + protected void checkOpen() { + if (!connectionState.isOpen) { + throw new SctlError("Connection is not open"); + } + } + + protected void fireConnectionEstablished() { + connectionState = ConnectionState.CONNECTED; + listeners.fire.modelOpened(); + } + + protected void fireConnectionClosed(DebuggerModelClosedReason reason) { + connectionState = ConnectionState.DISCONNECTED; + packetMatcher.flush(new SctlError("Client disconnected while waiting for reply")); + listeners.fire.modelClosed(reason); + } + + protected T reportOthersErrors(Throwable exc) { + Msg.info(this, "Ignoring an error caused by another controller: " + exc); + return null; + } + + /** + * Handle a request sent by another client + * + * This just prepares the client to receive the server's response and process both as if this + * client sent the original request. + * + * @param tag the tag of the request + * @param sel the request + */ + private void processBusRequest(int tag, AbstractSctlRequest sel) { + //Msg.debug(this, "Bus: " + sel); + if (!activeDialect.isBusSupported()) { + Msg.error(this, + "This SCTL channel is not a bus. Nevertheless, the controller received a request."); + return; + } + + int id = (tag >> 24) & 0x0ff; + if (id == idOnBus) { + Msg.warn(this, + "There appears to be another controller with my id (" + id + ") on this bus"); + } + //Msg.info(this, "Controller " + id + " sent: " + sel); + final CompletableFuture future; + if (sel instanceof SctlVersionRequest) { + future = processBusConnect(tag, (SctlVersionRequest) sel); + } + else if (sel instanceof SctlPingRequest) { + future = processBusPing(tag, (SctlPingRequest) sel); + } + else if (sel instanceof SctlExecuteRequest) { + future = processBusExecute(tag, (SctlExecuteRequest) sel); + } + else if (sel instanceof SctlProcessListRequest) { + future = processBusListAttachable(tag, (SctlProcessListRequest) sel); + } + else if (sel instanceof SctlStatusRequest) { + future = processBusStat(tag, (SctlStatusRequest) sel); + } + else if (sel instanceof SctlAttachRequest) { + future = processBusAttach(tag, (SctlAttachRequest) sel); + } + else if (sel instanceof SctlLaunchRequest) { + future = processBusLaunch(tag, (SctlLaunchRequest) sel); + } + else if (sel instanceof SctlContinueRequest) { + future = processBusResume(tag, (SctlContinueRequest) sel); + } + else if (sel instanceof SctlStepRequest) { + future = processBusStep(tag, (SctlStepRequest) sel); + } + else if (sel instanceof SctlSnapshotRequest) { + future = processBusSnap(tag, (SctlSnapshotRequest) sel); + } + else if (sel instanceof SctlStopRequest) { + future = processBusInterrupt(tag, (SctlStopRequest) sel); + } + else if (sel instanceof SctlReadRequest) { + future = processBusReadMemory(tag, (SctlReadRequest) sel); + } + else if (sel instanceof SctlWriteRequest) { + future = processBusWriteMemory(tag, (SctlWriteRequest) sel); + } + else if (sel instanceof SctlGetContextRequest) { + future = processBusGetContext(tag, (SctlGetContextRequest) sel); + } + else if (sel instanceof SctlSetContextRequest) { + future = processBusSetContext(tag, (SctlSetContextRequest) sel); + } + else if (sel instanceof SctlEnumerateContextRequest) { + future = processBusEnumerateContext(tag, (SctlEnumerateContextRequest) sel); + } + else if (sel instanceof SctlChooseContextRequest) { + future = processBusChooseContext(tag, (SctlChooseContextRequest) sel); + } + else if (sel instanceof SctlSetTrapRequest) { + future = processBusSetTrap(id, tag, (SctlSetTrapRequest) sel); + } + else if (sel instanceof SctlClearTrapRequest) { + future = processBusClearTrap(tag, (SctlClearTrapRequest) sel); + } + else if (sel instanceof SctlDetachRequest) { + future = processBusDetachThread(tag, (SctlDetachRequest) sel); + } + else if (sel instanceof SctlKillRequest) { + future = processBusKillThread(tag, (SctlKillRequest) sel); + } + else if (sel instanceof SctlTraceRequest) { + future = processBusTraceEvents(tag, (SctlTraceRequest) sel); + } + else if (sel instanceof SctlLookupSymbolRequest) { + SctlLookupSymbolRequest looksym = (SctlLookupSymbolRequest) sel; + SctlTargetModule ns = session.processes.getModule(looksym.nsid); + if (ns == null) { + future = processUnknownNSID(id, tag, looksym.nsid, sel); + } + else { + future = ns.symbols.processBusGetSymbol(tag, looksym); + } + } + else if (sel instanceof SctlEnumerateSymbolsRequest) { + SctlEnumerateSymbolsRequest enumsym = (SctlEnumerateSymbolsRequest) sel; + SctlTargetModule ns = session.processes.getModule(enumsym.nsid); + if (ns == null) { + future = processUnknownNSID(id, tag, enumsym.nsid, sel); + } + else { + future = ns.symbols.processBusGetAllSymbols(tag, enumsym); + } + } + else if (sel instanceof SctlLookupTypeRequest) { + SctlLookupTypeRequest looktype = (SctlLookupTypeRequest) sel; + SctlTargetModule ns = session.processes.getModule(looktype.nsid); + if (ns == null) { + future = processUnknownNSID(id, tag, looktype.nsid, sel); + } + else { + future = ns.types.processBusGetTypeDef(tag, looktype); + } + } + else if (sel instanceof SctlEnumerateSymbolsRequest) { + SctlEnumerateTypesRequest enumtype = (SctlEnumerateTypesRequest) sel; + SctlTargetModule ns = session.processes.getModule(enumtype.nsid); + if (ns == null) { + future = processUnknownNSID(id, tag, enumtype.nsid, sel); + } + else { + future = ns.types.processBusGetAllTypeDefs(tag, enumtype); + } + } + else if (sel instanceof SctlFocusRequest) { + future = processBusFocusThread(tag, (SctlFocusRequest) sel); + } + else if (sel instanceof SctlGetAttributesRequest) { + future = processBusGetAttributes(tag, (SctlGetAttributesRequest) sel); + } + else if (sel instanceof SctlGetElementsRequest) { + future = processBusGetElements(tag, (SctlGetElementsRequest) sel); + } + else { + future = processBusUnknown(id, tag, sel); + } + future.exceptionally(this::reportOthersErrors); + } + + private void processEventNotifyListsLibraries(long ctlid, TargetExecutionState state, + AbstractSctlListsLibrariesEventNotification lists) { + + // Use module names to decide whether or not to invalidate our stat + List moduleNames = new ArrayList<>(); + for (PathBase lib : lists.libs) { + moduleNames.add(lib.path.str); + } + + SctlTargetThread thread = session.processes.requireThread(ctlid); + try { + thread.registers.updateContextIfPresent(lists.getCtx()); + + // NOTE: no nsids are provided with the lib paths, so just re-stat + if (!thread.process.modules.getCachedElements().keySet().containsAll(moduleNames)) { + thread.process.invalidateStat(); + } + } + finally { + thread.setState(state); + } + } + + private void processEventNotifyForkClone(long ctlid, TargetExecutionState state, + AbstractSctlForkCloneNotification fc, String reason) { + SctlTargetThread thread = session.processes.requireThread(ctlid); + try { + thread.registers.updateContextIfPresent(fc.getCtx()); + + SctlTargetProcess spwnProc; + if (fc instanceof AbstractSctlForkNotification) { + AbstractSctlForkNotification forked = (AbstractSctlForkNotification) fc; + Long pid = forked.supportsProcessID() ? forked.getProcessID() : null; + spwnProc = session.processes.create(forked.spwnid, pid, thread.process.platform); + } + else { + spwnProc = thread.process; + } + SctlTargetThread spwnThread = spwnProc.createThread(fc.spwnid, reason); + spwnThread.registers.updateContextIfPresent(fc.spwnctx); + spwnThread.copyBreakpointsFrom(thread); + // NOTE: Adding a breakpoint action post-clone/-fork, applies only to one thread + } + finally { + thread.setState(state); + } + } + + private void processEventNotifyTrap(long ctlid, TargetExecutionState state, + SctlTrapNotification trapped) { + SctlTargetThread thread = session.processes.requireThread(ctlid); + try { + thread.registers.updateContextIfPresent(trapped.getCtx()); + + SctlTargetBreakpoint bpt = thread.breakpoints.getByTrpid(trapped.trpid); + if (bpt == null) { + throw new NoSuchElementException("Trap " + trapped.trpid + " is not known"); + } + bpt.hit(); + } + finally { + thread.setState(state); + } + } + + private void processEventNotifySnap(long ctlid, TargetExecutionState state, + AbstractSctlSnapNotification snapped) { + SctlTargetThread thread = session.processes.requireThread(ctlid); + try { + thread.registers.updateContextIfPresent(snapped.getCtx()); + Long pid = snapped.supportsProcessID() ? snapped.getProcessID() : null; + + // Create a handle for the new process and thread + SctlTargetProcess snapProc = + session.processes.create(snapped.spwnid, pid, thread.process.platform); + SctlTargetThread snapThread = snapProc.createThread(snapped.spwnid, "Snapshotted"); + // The two should have the same context, so no spwnctx + snapThread.registers.updateContextIfPresent(snapped.getCtx()); + + // Snap events happen because of "snap points" + SctlTargetBreakpoint bpt = thread.breakpoints.getByTrpid(snapped.trpid); + if (bpt == null) { + throw new NoSuchElementException("Trap " + snapped.trpid + " is not known"); + } + bpt.hit(); + } + finally { + thread.setState(state); + } + } + + private void processEventNotifyExit(long ctlid, TargetExecutionState state, + SctlExitNotification exit) { + SctlTargetThread thread = session.processes.requireThread(ctlid); + try { + thread.registers.updateContextIfPresent(exit.getCtx()); + + thread.setExitStatusCode(exit.status); + thread.destroy("Exited"); + } + finally { + thread.setState(state); + } + } + + private void processEventNotifyExec(long ctlid, TargetExecutionState state, + SctlExecNotification exec) { + SctlTargetThread thread = session.processes.requireThread(ctlid); + try { + thread.registers.updateContextIfPresent(exec.getCtx()); + + // Destroy all but the calling thread, according to execve man page + // NOTE: Sctl assumes the client knows that exec destroys all other threads + thread.process.threads.removeOthers(thread, "Other execed"); + thread.process.invalidateStat(); // my best guess based on what exec does + thread.breakpoints.clear(); // From the SCTL manual + } + finally { + thread.setState(state); + } + } + + private void processEventNotifySignal(long ctlid, TargetExecutionState state, + SctlSignalNotification signal) { + SctlTargetThread thread = session.processes.requireThread(ctlid); + try { + thread.registers.updateContextIfPresent(signal.getCtx()); + } + finally { + thread.setState(state); + } + } + + private void processEventNotifySyscall(long ctlid, TargetExecutionState state, + SctlSyscallNotification syscall) { + SctlTargetThread thread = session.processes.requireThread(ctlid); + try { + thread.registers.updateContextIfPresent(syscall.getCtx()); + } + finally { + thread.setState(state); + } + } + + private void processEventNotify(SctlEventNotify sel) { + //Msg.debug(this, "Event: " + sel); + + Set events = sel.getAllEvents(); + if (events.size() > 1) { + Msg.warn(this, "More than one event in a notification....: " + events); + } + AbstractSctlEventNotification details = events.iterator().next(); + TargetExecutionState state = activeDialect.stateAfterEvent(sel.flags); + if (details instanceof AbstractSctlListsLibrariesEventNotification) { + processEventNotifyListsLibraries(sel.ctlid, state, + (AbstractSctlListsLibrariesEventNotification) details); + } + else if (details instanceof AbstractSctlForkNotification) { + processEventNotifyForkClone(sel.ctlid, state, + (AbstractSctlForkNotification) details, "Forked"); + } + else if (details instanceof SctlCloneNotification) { + processEventNotifyForkClone(sel.ctlid, state, + (SctlCloneNotification) details, "Cloned"); + } + else if (details instanceof SctlTrapNotification) { + processEventNotifyTrap(sel.ctlid, state, (SctlTrapNotification) details); + } + else if (details instanceof Sctl2012SnapNotification) { + processEventNotifySnap(sel.ctlid, state, (Sctl2012SnapNotification) details); + } + else if (details instanceof SctlExitNotification) { + processEventNotifyExit(sel.ctlid, state, (SctlExitNotification) details); + } + else if (details instanceof SctlExecNotification) { + processEventNotifyExec(sel.ctlid, state, (SctlExecNotification) details); + } + else if (details instanceof SctlSignalNotification) { + processEventNotifySignal(sel.ctlid, state, (SctlSignalNotification) details); + } + else if (details instanceof SctlSyscallNotification) { + processEventNotifySyscall(sel.ctlid, state, (SctlSyscallNotification) details); + } + else { + throw new IllegalArgumentException("details of type " + details.getClass().toString()); + } + } + + /** + * Generate the next tag + * + * This always generates a tag in the SCTL-bus form. Even in non-bus dialects. Since the server + * need only echo the same tag in the reply, this should be backward compatible. + * + * @return the next tag + */ + @SctlExtension("This generates bus tags, but they are compatible with non-bus tags") + private synchronized int tag() { + int result = nextTag; + nextTag++; + // This scheme should not cause a problem for non-bus implementations + // TODO: Should I just incorporate this directly into the packet structure? + nextTag &= 0x00ffffff; + return result | (idOnBus << 24); + } + + /** + * Performs generic checks on a SCTL reply + * + * Namely, this checks if the reply is an {@code Rerror}. If it is, it copies the message and + * throws a {@link SctlError}. Then, it checks if the reply is of the expected type. If not, it + * throws a {@link SctlIncorrectReply}. Otherwise, it returns the reply cast to the expected + * type. + * + * @param cmd the command whose tag matches that of the reply + * @param pktType the expected type of the reply + * @param reply the reply actually received + * @return the reply cast to the expected type + * @throws SctlError if the reply is an {@code Rerror} + * @throws SctlIncorrectReply if the reply is not of the expected type + */ + protected static R checkReply(SctlPacket cmd, Class pktType, + SctlPacket reply) { + if (reply instanceof SctlErrorReply) { + throw new SctlError((SctlErrorReply) reply); + } + if (pktType.isAssignableFrom(reply.getClass())) { + return pktType.cast(reply); + } + throw new SctlIncorrectReply(cmd, reply); + } + + /* + * Methods for Tversion + */ + + public CompletableFuture connect() { + SctlVersionRequest req = SctlVersionInfo.makeRequest(restrictedDialects); + return sequence(TypeSpec.VOID).then((seq) -> { + if (connectionState != ConnectionState.INITIAL) { + throw new SctlError("Client has already connected"); + } + Msg.trace(this, "Connecting"); + + connectionState = ConnectionState.CONNECTING; + CompletableFuture cmd = sendCommand(req); + cmd.handle(seq::next); + // receive loop is not yet running, so receive exactly the version reply in parallel + packetChannel.read(AbstractSelSctlPacket.class).thenAccept((pkt) -> { + packetMatcher.fulfill(pkt.tag, pkt.sel); + }); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + SctlVersionReply version = finishConnect(req, reply); + // This doesn't go in finishConnect, because a bus connect is not our connect + SctlDialect dialect = SctlVersionInfo.agreeDialect(version.version); + synchronized (this) { + activeDialect = dialect; + marshaller.setPacketFactory(dialect.getPacketFactory()); + } + receiveLoop(); // in parallel. Performs command callbacks + fireConnectionEstablished(); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusConnect(int tag, SctlVersionRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishConnect(req, reply); + seq.exit(); + }).finish(); + } + + private SctlVersionReply finishConnect(SctlVersionRequest req, SctlPacket reply) { + return checkReply(req, SctlVersionReply.class, reply); + } + + /* + * Misc message-handling + */ + + private void receiveLoop() { + loop(TypeSpec.VOID, (loop) -> { + if (connectionState.isTerminate) { + loop.exit(); + return; + } + //Msg.trace(this, "Queueing receive"); + packetChannel.read(AbstractSelSctlPacket.class).handle(loop::consume); + }, TypeSpec.cls(AbstractSelSctlPacket.class), (rcvd, loop) -> { + loop.repeat(); // Don't get hung up servicing listeners before processing next packet + //Msg.debug(this, "Received: " + rcvd); + + if (rcvd.sel instanceof SctlEventNotify) { + processEventNotify((SctlEventNotify) rcvd.sel); + } + else if (rcvd.sel instanceof AbstractSctlRequest) { + processBusRequest(rcvd.tag, (AbstractSctlRequest) rcvd.sel); + } + else { + packetMatcher.fulfill(rcvd.tag, rcvd.sel); + } + }).exceptionally((exc) -> { + if (exc instanceof CompletionException) { + exc = exc.getCause(); + } + if (exc instanceof NotYetConnectedException) { + throw new AssertionError("INTERNAL: Connect first, please", exc); + } + else if (exc instanceof EOFException) { + Msg.error(this, "Server closed connection"); + fireConnectionClosed(DebuggerModelClosedReason.abnormal(exc)); + } + else if (exc instanceof ClosedChannelException) { + Msg.info(this, "Client closed connection"); + connectionState = ConnectionState.DISCONNECTED; + } + else if (exc instanceof CancelledKeyException) { + Msg.info(this, "Terminating receive loop: Connection closed."); + connectionState = ConnectionState.DISCONNECTED; + } + else { + Msg.error(this, "Receive failed for an unknown reason", exc); + fireConnectionClosed(DebuggerModelClosedReason.abnormal(exc)); + } + return null; + }); + } + + @Override + public CompletableFuture close() { + connectionState = ConnectionState.DISCONNECTED; + fireConnectionClosed(DebuggerModelClosedReason.normal()); + // No close notifications to send, and I'm not responsible to close the channel + return AsyncUtils.NIL; + } + + /** + * Send the given command and wait on its reply + * + * The method follows the asynchronous pattern: It immediately returns a future that will + * complete at a later time. It completes with the corresponding reply received from the server. + * This is accomplished using an {@link AsyncPairingCache} on the tag. A tag is assigned + * automatically, and the cache matches it to that of the received reply. If the reply is not + * received within a given timeout, the returned future is completed exceptionally. Multiple + * commands may be sent before any reply is received. + * + * @param cmd the command to send + * @return a future that completes with the corresponding reply + */ + protected CompletableFuture sendCommand(SctlPacket cmd) { + AbstractSelSctlPacket pkt = activeDialect.createSel(tag(), cmd); + //Msg.debug(this, "Sending: " + pkt); + checkOpen(); + return packetChannel.write(pkt).thenCompose(__ -> { + //Msg.trace(this, "Sent tag " + pkt.tag); + checkOpen(); + return packetMatcher.waitOn(pkt.tag); + }); + } + + /** + * Wait for a reply to a command that is not sent by this client + * + * This method is used for bus commands. It can also be used in circumstances where the command + * is sent by some means other than {@link #sendCommand(SctlPacket)}. It does the same thing + * without sending any command. It returns a future which completes with the reply having the + * given tag. + * + * @param tag the tag from the command + * @return a future that completes with the corresponding reply + */ + protected CompletableFuture recvTag(int tag) { + return sequence(TypeSpec.cls(SctlPacket.class)).then(seq -> { + checkOpen(); + packetMatcher.waitOn(tag).handle(seq::exit); + }).finish(); + } + + /* + * Methods for Tping + */ + + @Override + public CompletableFuture ping(String content) { + SctlPingRequest req = new SctlPingRequest(content); + return sequence(TypeSpec.VOID).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishPing(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusPing(int tag, SctlPingRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishPing(req, reply); + seq.exit(); + }).finish(); + } + + protected void finishPing(SctlPingRequest req, SctlPacket reply) { + SctlPingReply pinged = checkReply(req, SctlPingReply.class, reply); + int expected = req.bytes.length; + if (pinged.cnt != expected) { + throw new SctlIncorrectPingResponse(req.bytes, expected, (int) pinged.cnt); + } + } + + /* + * Methods for Texec + * + * NOTE: This command is not actually part of the SCTL specification. + */ + + @SctlExtension("requests execution of a CLI command") + protected CompletableFuture executeCapture(String cmd) { + SctlExecuteRequest req = new SctlExecuteRequest(cmd); + return sequence(TypeSpec.STRING).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishExecute(req, reply)); + }).finish(); + } + + private CompletableFuture processBusExecute(int tag, SctlExecuteRequest req) { + return sequence(TypeSpec.STRING).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishExecute(req, reply)); + }).finish(); + } + + protected String finishExecute(SctlExecuteRequest req, SctlPacket reply) { + SctlExecuteReply executed = SctlClient.checkReply(req, SctlExecuteReply.class, reply); + return executed.out.str; + } + + /* + * Methods for Tps + */ + protected CompletableFuture> listAttachable() { + SctlProcessListRequest req = new SctlProcessListRequest(); + return sequence(TypeSpec.cls(SctlTargetAttachable.class).list()).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishListAttachable(req, reply)); + }).finish(); + } + + protected List finishListAttachable(SctlProcessListRequest req, + SctlPacket reply) { + SctlProcessListReply procs = SctlClient.checkReply(req, SctlProcessListReply.class, reply); + return readProcessList(procs); + } + + private CompletableFuture> processBusListAttachable(int tag, + SctlProcessListRequest req) { + return sequence(TypeSpec.cls(SctlTargetAttachable.class).list()).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishListAttachable(req, reply)); + }).finish(); + } + + protected List readProcessList(SctlProcessListReply reply) { + List procList = reply.pslist.getProcesses(); + List result = new ArrayList<>(procList.size()); + for (AbstractSctlProcessEntry procEnt : procList) { + result.add(new SctlTargetAttachable(session.attachable, procEnt.getProcessID(), + procEnt.getCommand())); + } + session.attachable.changeElements(List.of(), result, "Retreived process list"); + return result; + } + + /* + * Methods for Tstat + */ + + // Doesn't return anything, rather updates internal info + protected CompletableFuture stat(long ctlid) { + SctlStatusRequest req = new SctlStatusRequest(ctlid); + return sequence(TypeSpec.VOID).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishStat(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusStat(int tag, SctlStatusRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishStat(req, reply); + seq.exit(); + }).finish(); + } + + protected void finishStat(SctlStatusRequest req, SctlPacket reply) { + SctlStatusReply stats = checkReply(req, SctlStatusReply.class, reply); + //Msg.info(this, stats); + synchronized (this) { + SctlTargetProcess proc = session.processes.getByCtlid(req.ctlid); + if (proc == null) { + Msg.warn(this, "Process removed before stat reply: ctlid=" + req.ctlid); + return; + } + populateStat(proc, stats.status); + } + } + + protected void populateStat(SctlTargetProcess proc, AbstractSctlStatus status) { + if (status.supportsProcessID()) { + proc.setPid(status.getProcessID()); + } + + for (AbstractSctlRegion region : status.getRegions()) { + try { + proc.createMemoryRegion(region.getName(), region.getAddress(), region.getLength(), + region.getProtections()); + } + catch (AddressOverflowException e) { + Msg.error(this, "Invalid region in SCTL response: " + region.getName() + ": " + e); + } + } + + for (AbstractSctlBinary bin : status.getBinaries()) { + Address base = null; + if (bin.supportsBase()) { + base = addrMapper.mapOffsetToAddress(bin.getBase()); + } + SctlTargetModule mod = + proc.modules.create(bin.getNamespaceID(), bin.getPath(), base, bin.isExecutable()); + if (bin.supportsSections()) { + for (AbstractSctlSection s : bin.getSections()) { + Address start = addrMapper.mapOffsetToAddress(s.getAddress()); + mod.addSection(s.getName(), start, s.getLength()); + } + mod.updateRange(); + } + } + } + + /* + * Methods for Tattach + */ + + protected CompletableFuture attach(long pid) { + SctlAttachRequest req = new SctlAttachRequest(pid); + return sequence(TypeSpec.cls(SctlTargetThread.class)).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishAttach(req, reply)); + }).finish(); + } + + private CompletableFuture processBusAttach(int tag, SctlAttachRequest req) { + return sequence(TypeSpec.cls(SctlTargetThread.class)).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishAttach(req, reply)); + }).finish(); + } + + protected SctlTargetThread finishAttach(SctlAttachRequest req, SctlPacket reply) { + AbstractSctlAttachReply attached = checkReply(req, AbstractSctlAttachReply.class, reply); + SctlTargetProcess proc = session.processes.getByPid(req.pid); + if (proc == null) { + String platform = attached.supportsPlatform() ? attached.getPlatform() + : activeDialect.getSolePlatform(); + proc = session.processes.create(attached.ctlid, req.pid, platform); + } + SctlTargetThread newThread = proc.createThread(attached.ctlid, "Attached"); + newThread.registers.updateContextIfPresent(attached.ctx); + return newThread; + } + + /* + * Methods for Tlaunch + */ + + protected CompletableFuture launch(List args) { + SctlLaunchRequest req = new SctlLaunchRequest(args); + return sequence(TypeSpec.cls(SctlTargetThread.class)).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishLaunch(req, reply)); + }).finish(); + } + + private CompletableFuture processBusLaunch(int tag, SctlLaunchRequest req) { + return sequence(TypeSpec.cls(SctlTargetThread.class)).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishLaunch(req, reply)); + }).finish(); + } + + protected SctlTargetThread finishLaunch(SctlLaunchRequest req, SctlPacket reply) { + AbstractSctlLaunchReply launched = checkReply(req, AbstractSctlLaunchReply.class, reply); + long ctlid = launched.ctlid; + Long pid = launched.supportsProcessID() ? launched.getProcessID() : null; + String platform = launched.supportsPlatform() ? launched.getPlatform() + : activeDialect.getSolePlatform(); + SctlTargetProcess newProc = session.processes.create(ctlid, pid, platform); + SctlTargetThread newThread = newProc.createThread(ctlid, "Launched"); + newThread.registers.updateContextIfPresent(launched.ctx); + return newThread; + } + + /* + * Methods for Tcont + */ + + protected CompletableFuture resume(long ctlid) { + SctlContinueRequest req = new SctlContinueRequest(ctlid); + return sequence(TypeSpec.VOID).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishResume(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusResume(int tag, SctlContinueRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishResume(req, reply); + seq.exit(); + }).finish(); + } + + protected void finishResume(SctlContinueRequest req, SctlPacket reply) { + checkReply(req, SctlContinueReply.class, reply); + session.processes.requireThread(req.ctlid).setState(TargetExecutionState.RUNNING); + } + + /* + * Methods for Tstep + */ + + CompletableFuture step(long ctlid) { + SctlStepRequest req = new SctlStepRequest(ctlid); + return sequence(TypeSpec.VOID).then((seq) -> { + sendCommand(req).handle(seq::next); + // Assume it's running at this point, but I don't really know + session.processes.requireThread(ctlid).setState(TargetExecutionState.RUNNING); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishStep(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusStep(int tag, SctlStepRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + // Assume it's running at this point, but I don't really know + session.processes.requireThread(req.ctlid).setState(TargetExecutionState.RUNNING); + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishStep(req, reply); + seq.exit(); + }).finish(); + } + + protected void finishStep(SctlStepRequest req, SctlPacket reply) { + SctlTargetThread thread = session.processes.requireThread(req.ctlid); + try { + SctlStepReply stepped = checkReply(req, SctlStepReply.class, reply); + thread.registers.updateContextIfPresent(stepped.ctx); + } + finally { + // Assume we're stopped whether or not there's an error + thread.setState(TargetExecutionState.STOPPED); + } + } + + /* + * Methods for Tsnap + */ + + protected CompletableFuture snap(long ctlid) { + SctlSnapshotRequest req = new SctlSnapshotRequest(ctlid); + return sequence(TypeSpec.cls(SctlTargetThread.class)).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishSnap(req, reply)); + }).finish(); + } + + private CompletableFuture processBusSnap(int tag, + SctlSnapshotRequest req) { + return sequence(TypeSpec.cls(SctlTargetThread.class)).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishSnap(req, reply)); + }).finish(); + } + + protected SctlTargetThread finishSnap(SctlSnapshotRequest req, SctlPacket reply) { + AbstractSctlSnapshotReply snapped = checkReply(req, AbstractSctlSnapshotReply.class, reply); + SctlTargetThread origThread = session.processes.requireThread(req.ctlid); + // snap is implemented via fork, so a new process! + Long pid = snapped.supportsProcessID() ? snapped.getProcessID() : null; + SctlTargetProcess newProc = + session.processes.create(snapped.spwnid, pid, origThread.process.platform); + SctlTargetThread newThread = newProc.createThread(snapped.spwnid, "Snapshotted"); + newThread.registers.updateContextIfPresent(snapped.ctx); + return newThread; + } + + /* + * Methods for Tstop + */ + + protected CompletableFuture interrupt(long ctlid) { + SctlStopRequest req = new SctlStopRequest(ctlid); + return sequence(TypeSpec.VOID).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishInterrupt(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusInterrupt(int tag, SctlStopRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishInterrupt(req, reply); + seq.exit(); + }).finish(); + } + + protected void finishInterrupt(SctlStopRequest req, SctlPacket reply) { + checkReply(req, SctlStopReply.class, reply); + session.processes.requireThread(req.ctlid).setState(TargetExecutionState.STOPPED); + } + + /* + * Methods for Tread + */ + + protected CompletableFuture readMemory(long ctlid, long addr, int len) { + SctlReadRequest req = new SctlReadRequest(ctlid, -1, addr, len); + return sequence(TypeSpec.BYTE_ARRAY).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishReadMemory(req, reply)); + }).finish(); + } + + private CompletableFuture processBusReadMemory(int tag, SctlReadRequest req) { + return sequence(TypeSpec.BYTE_ARRAY).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + byte[] data = finishReadMemory(req, reply); + SctlTargetThread thread = session.processes.requireThread(req.ctlid); + thread.process.memory.notifyUpdate(req.offset, data); + seq.exit(data); + }).finish(); + } + + protected byte[] finishReadMemory(SctlReadRequest req, SctlPacket reply) { + SctlReadReply read = checkReply(req, SctlReadReply.class, reply); + return read.bytes; + } + + /* + * Methods for Twrite + */ + + protected CompletableFuture writeMemory(long ctlid, long addr, byte[] data) { + SctlWriteRequest req = new SctlWriteRequest(ctlid, -1, addr, data); + return sequence(TypeSpec.VOID).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishWriteMemory(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusWriteMemory(int tag, SctlWriteRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishWriteMemory(req, reply); + SctlTargetThread thread = session.processes.requireThread(req.ctlid); + thread.process.memory.notifyUpdate(req.offset, req.bytes); + seq.exit(); + }).finish(); + } + + protected void finishWriteMemory(SctlWriteRequest req, SctlPacket reply) { + SctlWriteReply written = checkReply(req, SctlWriteReply.class, reply); + if (req.bytes.length != written.cnt) { + throw new SctlPartialWriteException(req.bytes, (int) written.cnt); + } + } + + /* + * Methods for Tgetctx + * + * Actually, these use the context sent by the last notification message + */ + + private Map checkCtxAvail(SctlTargetThread thread) { + if (!thread.registers.hasContextSinceStop() || + thread.getExecutionState() != TargetExecutionState.STOPPED) { + throw new IllegalStateException("Thread is not stopped, or has no valid context"); + } + return thread.registers.getContext(); + } + + protected CompletableFuture> getContext(long ctlid) { + SctlGetContextRequest req = new SctlGetContextRequest(ctlid); + SctlTargetThread thread = session.processes.requireThread(ctlid); + return sequence(TypeSpec.map(String.class, byte[].class)).then(seq -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishGetContext(req, reply); + seq.exit(thread.registers.getContext()); + }).finish(); + } + + private CompletableFuture processBusGetContext(int tag, SctlGetContextRequest req) { + return sequence(TypeSpec.VOID).then(seq -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishGetContext(req, reply); + seq.exit(); + }).finish(); + } + + protected void finishGetContext(SctlGetContextRequest req, SctlPacket reply) { + SctlGetContextReply get = checkReply(req, SctlGetContextReply.class, reply); + SctlTargetThread thread = session.processes.requireThread(req.ctlid); + thread.registers.updateContextIfPresent(get.ctx); + } + + protected void checkRegisterSelectionSupported() { + if (!activeDialect.isRegisterSelectionSupported()) { + throw new IllegalStateException( + activeDialect.getFullVersion() + " does not support register selection"); + } + } + + protected CompletableFuture> enumerateContext(long ctlid) { + checkRegisterSelectionSupported(); + SctlEnumerateContextRequest req = new SctlEnumerateContextRequest(ctlid); + return sequence(TypeSpec.map(String.class, SctlRegisterDefinition.class)).then(seq -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishEnumerateContext(req, reply)); + }).finish(); + } + + private CompletableFuture processBusEnumerateContext(int tag, + SctlEnumerateContextRequest req) { + checkRegisterSelectionSupported(); // If not, we might be in deep deep doo doo + // The server ought to also reject it, so we'll log it and pretend it didn't happen. + SctlTargetThread thread = session.processes.requireThread(req.ctlid); + return sequence(thread.registers.lazyRegDefs.provide()).then(seq -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishEnumerateContext(req, reply)); + }).finish().thenApply(t -> null); + } + + protected Map finishEnumerateContext( + SctlEnumerateContextRequest req, SctlPacket reply) { + SctlEnumerateContextReply enumed = checkReply(req, SctlEnumerateContextReply.class, reply); + Map result = new LinkedHashMap<>(); + for (SctlRegisterDefinition def : enumed.regdefs) { + result.put(def.name.str, def); + } + return result; + } + + protected synchronized CompletableFuture chooseContext(long ctlid, + Set descs) { + checkRegisterSelectionSupported(); + SctlTargetThread thread = session.processes.requireThread(ctlid); + SctlChooseContextRequest req = new SctlChooseContextRequest(ctlid); + List selDefs = new ArrayList<>(); + return sequence(TypeSpec.VOID).then(seq -> { + thread.registers.lazyRegDefs.request().handle(seq::next); + }, TypeSpec.map(String.class, SctlRegisterDefinition.class)).then((defs, seq) -> { + for (SctlTargetRegisterDescription trd : descs) { + String name = trd.getName(); + SctlRegisterDefinition d = defs.get(name); + if (d == null) { + throw new IllegalArgumentException("No register with name " + name); + } + req.regids.add(d.regid); + selDefs.add(d); + } + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishChooseContext(req, reply, selDefs, descs, null, null); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusChooseContext(int tag, SctlChooseContextRequest req) { + checkRegisterSelectionSupported(); // If not, we might be in deep deep doo doo + // The server ought to also reject it, so we'll log it and pretend it didn't happen. + AtomicReference recvd = new AtomicReference<>(); + AtomicReference> defsById = new AtomicReference<>(); + AtomicReference> descsByName = + new AtomicReference<>(); + SctlTargetThread thread = session.processes.requireThread(req.ctlid); + return sequence(TypeSpec.VOID).then(seq -> { + AsyncFence fence = new AsyncFence(); + fence.include(recvTag(tag).thenAccept(recvd::set)); + fence.include(thread.registers.lazyRegDefsById.request().thenAccept(defsById::set)); + fence.include(thread.registers.lazyRegDescs.request().thenAccept(descsByName::set)); + fence.ready().handle(seq::next); + }).then(seq -> { + finishChooseContext(req, recvd.get(), null, null, defsById.get(), descsByName.get()); + seq.exit(); + }).finish(); + } + + void finishChooseContext(SctlChooseContextRequest req, SctlPacket reply, + List selDefs, Set descs, + Map defsById, + Map descsByName) { + SctlChooseContextReply choose = checkReply(req, SctlChooseContextReply.class, reply); + if (selDefs == null) { + selDefs = new ArrayList<>(); + descs = new LinkedHashSet<>(); + for (long id : req.regids) { + SctlRegisterDefinition def = defsById.get(id); + if (def == null) { + throw new AssertionError("Bus client chose a non-existent register id"); + } + selDefs.add(def); + SctlTargetRegisterDescription trd = descsByName.get(def.name.str); + if (trd == null) { + throw new AssertionError(); // INTERNAL consistency check + } + descs.add(trd); + } + } + SctlTargetThread thread = session.processes.requireThread(req.ctlid); + thread.registers.setSelectedRegisters(selDefs); + thread.registers.updateContextIfPresent(choose.ctx); + } + + protected CompletableFuture> readRegisters(long ctlid) { + SctlTargetThread thread = session.processes.requireThread(ctlid); + if (thread.getExecutionState() == TargetExecutionState.RUNNING) { + return CompletableFuture.failedFuture(new IllegalStateException("Thread is running")); + } + if (thread.registers.hasContextSinceStop()) { + return CompletableFuture.completedFuture(thread.registers.getContext()); + } + return getContext(ctlid); + } + + protected CompletableFuture readSingleRegister(long ctlid, String regname) { + return sequence(TypeSpec.BYTE_ARRAY).then(seq -> { + readRegisters(ctlid).handle(seq::next); + }, TypeSpec.map(String.class, byte[].class)).then((regs, seq) -> { + byte[] val = regs.get(regname); + if (val == null) { + throw new IllegalArgumentException( + "Register " + regname + " is not in the context"); + } + seq.exit(val); + }).finish(); + } + + /* + * Methods for Tsetctx + */ + + protected CompletableFuture setContext(long ctlid, AbstractSctlContext ctx) { + SctlSetContextRequest req = new SctlSetContextRequest(ctlid, ctx); + return sequence(TypeSpec.VOID).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishSetContext(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusSetContext(int tag, SctlSetContextRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishSetContext(req, reply); + seq.exit(); + }).finish(); + } + + protected void finishSetContext(SctlSetContextRequest req, SctlPacket reply) { + checkReply(req, SctlSetContextReply.class, reply); + session.processes.requireThread(req.ctlid).registers.updateContextIfPresent(req.ctx); + } + + protected CompletableFuture writeRegisters(long ctlid, Map vals) { + try { + SctlTargetThread thread = session.processes.requireThread(ctlid); + AbstractSctlContext ctx = activeDialect.create(AbstractSctlContext.class); + ctx.setSelectedRegisters(thread.registers.getSelectedRegisters()); + + if (!vals.keySet().containsAll(ctx.getRegisterNames())) { + ctx.updateFromMap(checkCtxAvail(thread)); + } + ctx.updateFromMap(vals); + return setContext(ctlid, ctx); + } + catch (Exception e) { + return CompletableFuture.failedFuture(e); + } + } + + synchronized CompletableFuture writeSingleRegister(long ctlid, String regname, + byte[] data) { + try { + SctlTargetThread thread = session.processes.requireThread(ctlid); + AbstractSctlContext ctx = activeDialect.create(AbstractSctlContext.class); + ctx.setSelectedRegisters(thread.registers.getSelectedRegisters()); + + ctx.updateFromMap(checkCtxAvail(thread)); + ctx.update(regname, data); + return setContext(ctlid, ctx); + } + catch (Exception e) { + return CompletableFuture.failedFuture(e); + } + } + + /* + * Methods for Tsettrap + */ + + protected CompletableFuture setTrap(long ctlid, Address address, + long length, boolean read, boolean write, boolean execute) { + AbstractSctlTrapSpec spec = activeDialect.create(AbstractSctlTrapSpec.class); + long offset = addrMapper.mapAddressToOffset(address); + spec.setActionStop(); + spec.setAddress(offset); + spec.setLength(length); + spec.setHardware(read, write, execute); + SctlSetTrapRequest req = new SctlSetTrapRequest(ctlid, spec); + return sequence(TypeSpec.cls(SctlTargetBreakpoint.class)).then(seq -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishSetTrap(req, reply)); + }).finish(); + } + + private CompletableFuture processBusSetTrap(int id, int tag, + SctlSetTrapRequest req) { + return sequence(TypeSpec.cls(SctlTargetBreakpoint.class)).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + SctlTargetBreakpoint bpt = finishSetTrap(req, reply); + seq.exit(bpt); + }).finish(); + } + + protected SctlTargetBreakpoint finishSetTrap(SctlSetTrapRequest req, SctlPacket reply) { + SctlSetTrapReply set = checkReply(req, SctlSetTrapReply.class, reply); + SctlTargetThread thread = session.processes.requireThread(req.ctlid); + SctlTargetBreakpoint bpt = thread.createBreakpoint(set.trpid, req.spec.getAddress()); + return bpt; + } + + /* + * Methods for Tclrtrap + */ + + protected CompletableFuture clearTrap(long ctlid, long trpid) { + SctlClearTrapRequest req = new SctlClearTrapRequest(ctlid, trpid); + return sequence(TypeSpec.VOID).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishClearTrap(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusClearTrap(int tag, SctlClearTrapRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishClearTrap(req, reply); + seq.exit(); + }).finish(); + } + + protected void finishClearTrap(SctlClearTrapRequest req, SctlPacket reply) { + checkReply(req, SctlClearTrapReply.class, reply); + session.processes.requireThread(req.ctlid).destroyBreakpoint(req.trpid); + } + + /* + * Methods for Tdetach + */ + + protected CompletableFuture detachThread(long ctlid) { + SctlDetachRequest req = new SctlDetachRequest(ctlid); + return sequence(TypeSpec.VOID).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishDetach(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusDetachThread(int tag, SctlDetachRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishDetach(req, reply); + seq.exit(); + }).finish(); + } + + protected void finishDetach(SctlDetachRequest req, SctlPacket reply) { + checkReply(req, SctlDetachReply.class, reply); + session.processes.destroyThread(req.ctlid, "Detached"); + } + + /* + * Methods for Tkill + */ + + protected CompletableFuture killThread(long ctlid) { + SctlKillRequest req = new SctlKillRequest(ctlid); + return sequence(TypeSpec.VOID).then((seq) -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishKill(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusKillThread(int tag, SctlKillRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishKill(req, reply); + seq.exit(); + }).finish(); + } + + protected void finishKill(SctlKillRequest req, SctlPacket reply) { + checkReply(req, SctlKillReply.class, reply); + session.processes.destroyThread(req.ctlid, "Killed"); + } + + @SctlExtension("Cause the debugger to select a given thread") + protected CompletableFuture focusThread(long ctlid) { + SctlFocusRequest req = new SctlFocusRequest(ctlid); + return sequence(TypeSpec.VOID).then(seq -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishFocus(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusFocusThread(int tag, SctlFocusRequest req) { + return sequence(TypeSpec.VOID).then(seq -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishFocus(req, reply); + seq.exit(); + }).finish(); + } + + protected void finishFocus(SctlFocusRequest req, SctlPacket reply) { + checkReply(req, SctlFocusReply.class, reply); + SctlTargetThread thread = session.processes.requireThread(req.ctlid); + session.fireFocused(thread); + } + + /** + * The given path must be relative to the "Objects" sub-tree + * + * @param path the path, relative to ["Objects"] + * @return the attributes of the object at the given path + */ + protected CompletableFuture> getAttributes(List path) { + String joinedPath = StringUtils.join(path, SctlTargetObject.PATH_SEPARATOR_STRING); + SctlGetAttributesRequest req = new SctlGetAttributesRequest(joinedPath); + return sequence(TypeSpec.map(String.class, TargetObject.class)).then(seq -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishGetAttributes(req, reply)); + }).finish(); + } + + private CompletableFuture processBusGetAttributes(int tag, + SctlGetAttributesRequest req) { + return sequence(TypeSpec.VOID).then(seq -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishGetAttributes(req, reply); + seq.exit(null, null); + }).finish(); + } + + protected Map finishGetAttributes(SctlGetAttributesRequest req, + SctlPacket reply) { + SctlGetAttributesReply attr = checkReply(req, SctlGetAttributesReply.class, reply); + Map map = new LinkedHashMap(); + for (AbstractSctlObjectEntry obj : attr.attributes) { + String joinedPath = obj.getPath().str; + List path = new ArrayList<>(); + path.addAll(session.objects.getPath()); + path.addAll(List.of(joinedPath.split(SctlTargetObject.PATH_SEPARATOR_REGEX))); + SctlTargetObject tobj; + try { + tobj = session.objects.create(obj.getKey().str, path, obj.getKind().str, + obj.getValue().str, obj.getType().str); + } + catch (InvalidObjectException e) { + Msg.error(this, e); + return map; + } + map.put(obj.getKey().str, tobj); + } + session.objects.notifyAttributes(req.path.str, map); + return map; + } + + protected CompletableFuture> getElements(List path) { + String joinedPath = StringUtils.join(path, SctlTargetObject.PATH_SEPARATOR_STRING); + SctlGetElementsRequest req = new SctlGetElementsRequest(joinedPath); + return sequence(TypeSpec.map(String.class, TargetObject.class)).then(seq -> { + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishGetElements(req, reply)); + }).finish(); + } + + private CompletableFuture processBusGetElements(int tag, SctlGetElementsRequest req) { + return sequence(TypeSpec.VOID).then(seq -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishGetElements(req, reply); + seq.exit(null, null); + }).finish(); + } + + protected Map finishGetElements(SctlGetElementsRequest req, + SctlPacket reply) { + SctlGetElementsReply result = checkReply(req, SctlGetElementsReply.class, reply); + Map map = new LinkedHashMap<>(); + for (AbstractSctlObjectEntry obj : result.elements) { + String joinedPath = obj.getPath().str; + List path = new ArrayList<>(); + for (String str : joinedPath.split(SctlTargetObject.PATH_SEPARATOR_REGEX)) { + path.add(str); + } + SctlTargetObject tobj; + try { + tobj = session.objects.create(obj.getKey().str, path, obj.getKind().str, + obj.getValue().str, obj.getType().str); + } + catch (InvalidObjectException e) { + Msg.error(this, e); + return map; + } + map.put(tobj.getName(), tobj); + } + session.objects.notifyElements(req.path.str, map); + return map; + } + + /* + * Methods for Ttrace + */ + + CompletableFuture traceEvents(long ctlid, SctlTrace.Mode mode, + Set events) { + SctlTraceRequest req = new SctlTraceRequest(ctlid, SctlTrace.toFlags(mode, events)); + return sequence(TypeSpec.VOID).then((seq) -> { + //Msg.debug(this, "TraceRequest: " + req); + sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishTraceEvents(req, reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusTraceEvents(int tag, SctlTraceRequest req) { + return sequence(TypeSpec.VOID).then((seq) -> { + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + finishTraceEvents(req, reply); + seq.exit(); + }).finish(); + } + + private void finishTraceEvents(SctlTraceRequest req, SctlPacket reply) { + checkReply(req, SctlTraceReply.class, reply); + } + + /* + * Methods for handling errors apparently generated by other clients on the bus + */ + + private CompletableFuture processUnknownNSID(int id, int tag, long nsid, SctlPacket req) { + return sequence(TypeSpec.VOID).then((seq) -> { + Msg.warn(this, "Client " + id + " is talking about nsid " + nsid + + ", which is unknown to this client: \n --(" + tag + ")-> " + req); + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + Msg.warn(this, " <-(" + tag + ")-- " + reply); + seq.exit(); + }).finish(); + } + + private CompletableFuture processBusUnknown(int id, int tag, SctlPacket req) { + return sequence(TypeSpec.VOID).then((seq) -> { + Msg.warn(this, "Do not know how to process command from controller " + id + + ":\n --(" + tag + ")-> " + req); + recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + Msg.warn(this, " <-(" + tag + ")-- " + reply); + seq.exit(); + }).finish(); + } + + /* + * Misc + */ + + @Override + public String toString() { + String status = connectionState.name().toLowerCase(); + return ""; + } + + @Override + public String getBrief() { + String diaStr = activeDialect.getSysVersion(); + String status = connectionState.name().toLowerCase(); + return description + " via SCTL " + diaStr + " (" + status + ")"; + } + + @Override + public CompletableFuture fetchModelRoot() { + return CompletableFuture.completedFuture(session); + } + + @Override + public CompletableFuture fetchModelObject(List path) { + if (PathUtils.isAncestor(session.objects.getPath(), path)) { + List sub = path.subList(session.objects.getPath().size(), path.size()); + return session.objects.fetchSuccessor(sub); + } + return super.fetchModelObject(path); + } + + @Override + public AddressFactory getAddressFactory() { + return addrMapper.getAddressFactory(); + } + + @Override + public void invalidateAllLocalCaches() { + TODO(); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlExtension.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlExtension.java new file mode 100644 index 0000000000..3bb22dd985 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlExtension.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import static java.lang.annotation.ElementType.*; + +import java.lang.annotation.*; + +/** + * Indicates that some portion of the code implements a feature not part of the standard SCTL spec + * + * This is for documentation purposes only + */ +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE }) +public @interface SctlExtension { + /** + * Describe the extension, very briefly + * + * @return the description + */ + String value(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlMemoryProtection.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlMemoryProtection.java new file mode 100644 index 0000000000..5708365f95 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlMemoryProtection.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import ghidra.comm.util.BitmaskUniverse; + +public enum SctlMemoryProtection implements BitmaskUniverse { + PROT_READ(1 << 0), PROT_WRITE(1 << 1), PROT_EXEC(1 << 2); + + private final long mask; + + SctlMemoryProtection(long mask) { + this.mask = mask; + } + + @Override + public long getMask() { + return mask; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetAttachable.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetAttachable.java new file mode 100644 index 0000000000..ec0bd4d692 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetAttachable.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.Map; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetAttachable; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; + +/** + * A process which can be attached via SCTL + */ +public class SctlTargetAttachable + extends DefaultTargetObject + implements TargetAttachable { + + protected static String indexAttachable(long pid) { + return PathUtils.makeIndex(pid); + } + + protected static String keyAttachable(long pid) { + return PathUtils.makeKey(indexAttachable(pid)); + } + + protected final SctlClient client; + + protected final long pid; + protected final String cmdLine; + + /** + * Construct an attachable process + * + * @param container the parent object (associated with the same client) + * @param pid the PID of the process + * @param cmdLine command line + */ + public SctlTargetAttachable(SctlTargetAttachableContainer container, long pid, String cmdLine) { + super(container.client, container, keyAttachable(pid), "AttachableProcess"); + this.client = container.client; + + this.pid = pid; + this.cmdLine = cmdLine; + + changeAttributes(List.of(), List.of(), Map.of( // + "pid", pid, // + "cmd_line", cmdLine // + ), "Initialized"); + } + + public long getPid() { + return pid; + } + + public String getCommandLine() { + return cmdLine; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetAttachableContainer.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetAttachableContainer.java new file mode 100644 index 0000000000..e81897fa95 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetAttachableContainer.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.agent.DefaultTargetObject; + +public class SctlTargetAttachableContainer + extends DefaultTargetObject { + + protected final SctlClient client; + + public SctlTargetAttachableContainer(SctlTargetSession session) { + super(session.client, session, "Attachable", "AttachableContainer"); + this.client = session.client; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return client.listAttachable().thenApply(__ -> null); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetBreakpoint.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetBreakpoint.java new file mode 100644 index 0000000000..1c3a00de1b --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetBreakpoint.java @@ -0,0 +1,181 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; +import ghidra.util.datastruct.ListenerSet; + +public class SctlTargetBreakpoint + extends DefaultTargetObject + implements TargetBreakpointSpec, + TargetDeletable, + TargetBreakpointLocation { + + protected static String keyBreakpoint(long trpid) { + return PathUtils.makeKey(indexBreakpoint(trpid)); + } + + protected static String indexBreakpoint(long trpid) { + return PathUtils.makeIndex(trpid); + } + + protected final SctlClient client; + protected final SctlTargetThread thread; + + protected final long trpid; + protected final long offset; + protected final Address address; + protected final String expression; + + protected final ListenerSet actions = + new ListenerSet<>(TargetBreakpointAction.class) { + // Strong references + protected Map createMap() { + return Collections.synchronizedMap(new LinkedHashMap<>()); + }; + }; + protected boolean cleared = false; + + public SctlTargetBreakpoint(SctlTargetBreakpointContainer breakpoints, SctlTargetThread thread, + long trpid, long offset) { + super(breakpoints.client, breakpoints, keyBreakpoint(trpid), "Breakpoint"); + this.client = breakpoints.client; + this.thread = thread; + + this.trpid = trpid; + this.offset = offset; + this.address = client.addrMapper.mapOffsetToAddress(offset); + this.expression = "0x" + Long.toHexString(offset); + + /** + * TODO: Can AFFECTS be elided since the container exists in one thread? It's a conventional + * question we should consider for all implementations. If/when the container is moved to + * the process, does the idea still apply? Either way, it seems appropriate to have the + * affects attribute provide here, i.e., implement the convention in the breakpoint rather + * than everywhere else. + */ + changeAttributes(List.of(), Map.of( + SPEC_ATTRIBUTE_NAME, this, + ADDRESS_ATTRIBUTE_NAME, address, + LENGTH_ATTRIBUTE_NAME, 1, + EXPRESSION_ATTRIBUTE_NAME, expression, + KINDS_ATTRIBUTE_NAME, SctlTargetBreakpointContainer.SOFTWARE_ONLY, + ENABLED_ATTRIBUTE_NAME, true // + ), "Initialized"); + } + + /** + * Duplicate this breakpoint into another thread's container + * + * This list of actions is copied, but each action is copied by reference. Generally, actions + * are immutable. As a consequence, if a user adds an action to a copied breakpoint, that action + * does not get applied to the original breakpoint. Thus, when a thread is forked or cloned, its + * breakpoints are all copied, but adding an action to a breakpoint will only affect the + * breakpoint active on the designated thread. To affect all threads having that breakpoint, the + * action must be added explicitly to each copy. + * + * @see SctlTargetThread#copyBreakpointsFrom(SctlTargetThread) + * @param that the breakpoint to copy + * @param to the container (of another thread) into which the copy will be placed + */ + protected SctlTargetBreakpoint(SctlTargetBreakpoint that, SctlTargetBreakpointContainer to) { + this(to, that.thread, that.trpid, that.offset); + this.actions.addAll(that.actions); + } + + @Override + public Address getAddress() { + return address; + } + + @Override + public Integer getLength() { + return 1; + } + + @Override + public TypedTargetObjectRef> getSpecification() { + return this; + } + + @Override + public String getExpression() { + return expression; + } + + @Override + public TargetBreakpointKindSet getKinds() { + return SctlTargetBreakpointContainer.SOFTWARE_ONLY; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public void addAction(TargetBreakpointAction action) { + if (cleared) { + throw new IllegalStateException("Breakpoint is cleared"); + } + actions.add(action); + } + + @Override + public void removeAction(TargetBreakpointAction action) { + if (cleared) { + throw new IllegalStateException("Breakpoint is cleared"); + } + actions.remove(action); + } + + @Override + public CompletableFuture delete() { + if (cleared) { + return CompletableFuture + .failedFuture(new IllegalStateException("Breakpoint is already cleared")); + } + actions.clear(); // Expedite gc + return client.clearTrap(thread.ctlid, trpid); + } + + @Override + public CompletableFuture disable() { + // SCTL breakpoints are always enabled, so disable by deleting + // TODO: Consider keeping a record in the client, permitting it to be easily re-enabled. + return delete(); + } + + @Override + public CompletableFuture enable() { + // SCTL breakpoints are always enabled, or they're absent + return AsyncUtils.NIL; + } + + protected void hit() { + parent.breakpointHit(this); + actions.fire.breakpointHit(this, thread, null, this); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetBreakpointContainer.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetBreakpointContainer.java new file mode 100644 index 0000000000..31eb5624d3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetBreakpointContainer.java @@ -0,0 +1,132 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetBreakpointContainer; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressRange; + +/** + * TODO: Document me + * + * TODO: Should this be a child of the process instead? Would be much easier than copying + * breakpoints around, if in fact, one set of breaks is shared by all ctls of the same process. + */ +public class SctlTargetBreakpointContainer + extends DefaultTargetObject + implements TargetBreakpointContainer { + + protected static final TargetBreakpointKindSet SOFTWARE_ONLY = + TargetBreakpointKindSet.of(TargetBreakpointKind.SOFTWARE); + + protected final SctlClient client; + + private final Map breaksByTrpid = new LinkedHashMap<>(); + + public SctlTargetBreakpointContainer(SctlTargetThread thread) { + super(thread.client, thread, "Breakpoints", "BreakpointContainer"); + this.client = thread.client; + + changeAttributes(List.of(), Map.of( // + SUPPORTED_BREAK_KINDS_ATTRIBUTE_NAME, SOFTWARE_ONLY // + ), "Initialized"); + } + + protected void put(long trpid, SctlTargetBreakpoint bpt) { + synchronized (this) { + breaksByTrpid.put(trpid, bpt); + } + changeElements(List.of(), List.of(bpt), "Placed"); + } + + protected void putAll(Map map) { + synchronized (this) { + breaksByTrpid.putAll(map); + } + changeElements(List.of(), map.values(), "Placed"); + } + + protected Collection getAll() { + return breaksByTrpid.values(); + } + + protected synchronized SctlTargetBreakpoint getByTrpid(long trpid) { + return breaksByTrpid.get(trpid); + } + + protected SctlTargetBreakpoint removeByTrpid(long trpid) { + SctlTargetBreakpoint removed; + synchronized (this) { + removed = breaksByTrpid.remove(trpid); + } + if (removed != null) { + changeElements(List.of(PathUtils.makeIndex(trpid)), List.of(), "Removed"); + } + return removed; + } + + protected void clear() { + synchronized (this) { + breaksByTrpid.clear(); + } + setElements(List.of(), "Cleared"); + } + + @Override + public CompletableFuture placeBreakpoint(String expression, + Set kinds) { + if (!Objects.equals(kinds, SOFTWARE_ONLY)) { + throw new UnsupportedOperationException("SCTL supports software breakpoints only"); + } + long offset; + try { + if (expression.startsWith("0x")) { + offset = Long.parseLong(expression.substring(2), 16); + } + else { + offset = Long.parseLong(expression, 10); + } + } + catch (NumberFormatException e) { + throw new IllegalArgumentException("SCTL requires address as 0x[hex] or [dec]"); + } + Address address = client.addrMapper.mapOffsetToAddress(offset); + return parent.setBreakpoint(address).thenApply(__ -> null); + } + + @Override + public CompletableFuture placeBreakpoint(AddressRange range, + Set kinds) { + if (!Objects.equals(kinds, SOFTWARE_ONLY)) { + throw new UnsupportedOperationException("SCTL supports software breakpoints only"); + } + if (range.getLength() != 1) { + throw new UnsupportedOperationException( + "SCTL supports single-address breakpoints only"); + } + return parent.setBreakpoint(range.getMinAddress()).thenApply(__ -> null); + } + + public void breakpointHit(SctlTargetBreakpoint bpt) { + listeners.fire(TargetBreakpointListener.class).breakpointHit(this, parent, null, bpt, bpt); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetCompositeDataType.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetCompositeDataType.java new file mode 100644 index 0000000000..ee036cafb9 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetCompositeDataType.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncFence; +import ghidra.dbg.sctl.protocol.types.*; + +public class SctlTargetCompositeDataType> + extends SctlTargetNamedDataType { + + // TODO: Dispose of this once converted? + protected final AbstractSctlAggregateTypeDefinition tdef; + + protected SctlTargetCompositeDataType(SctlTargetDataTypeNamespace types, + AbstractSctlTaggedTypeName tname, AbstractSctlAggregateTypeDefinition tdef, + NamedDataTypeKind kind, String typeHint) { + super(types, tname, kind, typeHint); + this.tdef = tdef; + } + + protected CompletableFuture collectFields() { + AsyncFence fence = new AsyncFence(); + List elems = new ArrayList<>(tdef.fields.size()); + for (int i = 0; i < tdef.fields.size(); i++) { + int pos = i; + SctlAggregateField field = tdef.fields.get(i); + fence.include(parent.getType(field.tname.sel).thenAccept(t -> { + SctlTargetCompositeField f = + new SctlTargetCompositeField(this, pos, field.off, field.id.str, t); + synchronized (elems) { + elems.add(f); + } + })); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), elems, "Initialized"); + }); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetCompositeField.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetCompositeField.java new file mode 100644 index 0000000000..11a58e0d9a --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetCompositeField.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import ghidra.dbg.attributes.TargetDataType; +import ghidra.dbg.util.PathUtils; + +public class SctlTargetCompositeField extends SctlTargetDataTypeMember { + + protected static String indexField(long position) { + return PathUtils.makeIndex(position); + } + + protected static String keyField(long position) { + return PathUtils.makeKey(indexField(position)); + } + + public SctlTargetCompositeField(SctlTargetNamedDataType type, int position, long offset, + String memberName, TargetDataType dataType) { + super(type, keyField(position), position, offset, memberName, dataType, "TypeField"); + } + + @Override + public String toString() { + return "<" + getClass().getSimpleName() + " " + getName() + " " + parent.getIndex() + "." + + getMemberName() + " @+" + getOffset() + ">"; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetDataTypeMember.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetDataTypeMember.java new file mode 100644 index 0000000000..e1f5626053 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetDataTypeMember.java @@ -0,0 +1,71 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.Map; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.attributes.TargetDataType; +import ghidra.dbg.target.TargetDataTypeMember; +import ghidra.dbg.target.TargetObject; + +public class SctlTargetDataTypeMember + extends DefaultTargetObject> + implements TargetDataTypeMember { + + protected final int position; + protected final long offset; + protected final String memberName; + protected final TargetDataType dataType; + + public SctlTargetDataTypeMember(SctlTargetNamedDataType type, String key, int position, + long offset, String memberName, TargetDataType dataType, String typeHint) { + super(type.client, type, key, typeHint); + + this.position = position; + this.offset = offset; + this.memberName = memberName; + this.dataType = dataType; + + changeAttributes(List.of(), Map.of( // + POSITION_ATTRIBUTE_NAME, position, // + MEMBER_NAME_ATTRIBUTE_NAME, memberName, // + OFFSET_ATTRIBUTE_NAME, offset, // + DATA_TYPE_ATTRIBUTE_NAME, dataType // + ), "Initialized"); + } + + @Override + public int getPosition() { + return position; + } + + @Override + public String getMemberName() { + return memberName; + } + + @Override + public long getOffset() { + return offset; + } + + @Override + public TargetDataType getDataType() { + return dataType; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetDataTypeNamespace.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetDataTypeNamespace.java new file mode 100644 index 0000000000..149eafd749 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetDataTypeNamespace.java @@ -0,0 +1,240 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import static ghidra.async.AsyncUtils.sequence; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.*; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.attributes.*; +import ghidra.dbg.attributes.TargetArrayDataType.DefaultTargetArrayDataType; +import ghidra.dbg.attributes.TargetBitfieldDataType.DefaultTargetBitfieldDataType; +import ghidra.dbg.attributes.TargetPointerDataType.DefaultTargetPointerDataType; +import ghidra.dbg.attributes.TargetPrimitiveDataType.DefaultTargetPrimitiveDataType; +import ghidra.dbg.sctl.protocol.SctlPacket; +import ghidra.dbg.sctl.protocol.common.reply.SctlEnumerateTypesReply; +import ghidra.dbg.sctl.protocol.common.reply.SctlLookupTypeReply; +import ghidra.dbg.sctl.protocol.common.request.SctlEnumerateTypesRequest; +import ghidra.dbg.sctl.protocol.common.request.SctlLookupTypeRequest; +import ghidra.dbg.sctl.protocol.consts.Cbase; +import ghidra.dbg.sctl.protocol.types.*; +import ghidra.dbg.target.TargetDataTypeNamespace; + +public class SctlTargetDataTypeNamespace + extends DefaultTargetObject, SctlTargetModule> + implements TargetDataTypeNamespace { + + protected static final CompletableFuture COMPLETED_TYPE_VOID = + CompletableFuture.completedFuture(TargetPrimitiveDataType.VOID); + + protected final SctlClient client; + + protected final AsyncLazyMap lazyDefs = + new AsyncLazyMap<>(new HashMap<>(), this::doGetTypeDef); + protected final AsyncLazyValue> allDefs = + new AsyncLazyValue<>(this::doGetAllTypeDefs); + + protected final AsyncLazyMap lazyTypes = + new AsyncLazyMap<>(new HashMap<>(), this::doGetType); + protected final AsyncLazyValue> allTypes = + new AsyncLazyValue<>(this::doGetAllTypes); + + public SctlTargetDataTypeNamespace(SctlTargetModule module) { + super(module.client, module, "Types", "DataTypeNamespace"); + this.client = module.client; + } + + public CompletableFuture getTypeDef(AbstractSctlTypeName tname) { + return lazyDefs.get(tname); + } + + protected CompletableFuture doGetTypeDef(AbstractSctlTypeName tname) { + SctlLookupTypeRequest req = new SctlLookupTypeRequest(parent.nsid, tname); + return sequence(TypeSpec.cls(SelSctlTypeDefinition.class)).then((seq) -> { + client.sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishGetTypeDef(req, reply)); + }).finish(); + } + + protected CompletableFuture processBusGetTypeDef(int tag, + SctlLookupTypeRequest req) { + CompletableFuture promise = lazyDefs.put(req.tname.sel); + return sequence(TypeSpec.cls(SelSctlTypeDefinition.class)).then((seq) -> { + client.recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + SelSctlTypeDefinition tdef = finishGetTypeDef(req, reply); + promise.complete(tdef); + seq.exit(tdef); + }).finish(); + } + + protected SelSctlTypeDefinition finishGetTypeDef(SctlLookupTypeRequest req, + SctlPacket reply) { + SctlLookupTypeReply lookedup = SctlClient.checkReply(req, SctlLookupTypeReply.class, reply); + return lookedup.tdef; + } + + protected CompletableFuture> doGetAllTypeDefs() { + SctlEnumerateTypesRequest req = new SctlEnumerateTypesRequest(parent.nsid); + return sequence(TypeSpec.future(this::doGetAllTypeDefs)).then((seq) -> { + client.sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishGetAllTypeDefs(req, reply)); + }).finish(); + } + + protected CompletableFuture> processBusGetAllTypeDefs( + int tag, SctlEnumerateTypesRequest req) { + CompletableFuture> promise = + allDefs.provide(); + return sequence(TypeSpec.future(this::processBusGetAllTypeDefs)).then((seq) -> { + client.recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + Map replies = + finishGetAllTypeDefs(req, reply); + promise.complete(replies); + seq.exit(replies); + }).finish(); + } + + protected Map finishGetAllTypeDefs( + SctlEnumerateTypesRequest req, SctlPacket reply) { + SctlEnumerateTypesReply enumed = + SctlClient.checkReply(req, SctlEnumerateTypesReply.class, reply); + synchronized (lazyDefs) { + for (SelSctlTypeDefinition tdef : enumed.tdefs) { + final AbstractSctlTypeName tname = tdef.getTypeName(); + lazyDefs.put(tname, tdef); + } + return lazyDefs.getCompletedMap(); + } + } + + public CompletableFuture getType(AbstractSctlTypeName tname) { + return lazyTypes.get(tname); + } + + protected CompletableFuture doGetType(AbstractSctlTypeName tname) { + if (tname instanceof SctlArrayTypeName) { + SctlArrayTypeName nArray = (SctlArrayTypeName) tname; + return getType(nArray.tname.sel).thenApply(tElem -> { + return new DefaultTargetArrayDataType(tElem, (int) nArray.nelem); + }); + } + if (tname instanceof SctlBaseTypeName) { + SctlBaseTypeName tBase = (SctlBaseTypeName) tname; + if (tBase.base == Cbase.Vvoid) { + return COMPLETED_TYPE_VOID; + } + return getTypeDef(tname).thenApply(d -> { + SctlBaseTypeDefinition dBase = (SctlBaseTypeDefinition) d.sel; + return new DefaultTargetPrimitiveDataType(dBase.rep.getKind(), + dBase.rep.getByteLength()); + }); + } + if (tname instanceof SctlBitfieldTypeName) { + SctlBitfieldTypeName nBitfield = (SctlBitfieldTypeName) tname; + return getType(nBitfield.tname.sel).thenApply(tField -> { + return new DefaultTargetBitfieldDataType(tField, Byte.toUnsignedInt(nBitfield.pos), + Byte.toUnsignedInt(nBitfield.width)); + }); + } + if (tname instanceof SctlEnumConstTypeName) { + // Just let it take the enum type, perhaps with a "const" modifier, if Ghidra supports it + SctlEnumConstTypeName nEnumConst = (SctlEnumConstTypeName) tname; + return getType(nEnumConst.tname.sel).thenApply(tEnum -> { + return tEnum; // TODO: Some indicator of being constant? + }); + } + if (tname instanceof SctlEnumTypeName) { + SctlEnumTypeName nEnum = (SctlEnumTypeName) tname; + return CompletableFuture.completedFuture(new SctlTargetEnumDataType.Ref(this, nEnum)); + } + if (tname instanceof SctlFunctionTypeName) { + SctlFunctionTypeName nFunction = (SctlFunctionTypeName) tname; + SctlTargetFunctionDataType tFunction = new SctlTargetFunctionDataType(this, nFunction); + return tFunction.collectMembers().thenApply(__ -> { + changeElements(List.of(), List.of(tFunction), "Fetched"); + return tFunction; + }); + } + if (tname instanceof SctlPointerTypeName) { + SctlPointerTypeName nPointer = (SctlPointerTypeName) tname; + return getType(nPointer.tname.sel).thenApply(tReferent -> { + return new DefaultTargetPointerDataType(tReferent); + }); + } + if (tname instanceof SctlStructTypeName) { + SctlStructTypeName nStruct = (SctlStructTypeName) tname; + return CompletableFuture + .completedFuture(new SctlTargetStructDataType.Ref(this, nStruct)); + } + if (tname instanceof SctlTypedefTypeName) { + SctlTypedefTypeName nTypedef = (SctlTypedefTypeName) tname; + return CompletableFuture + .completedFuture(new SctlTargetTypedefDataType.Ref(this, nTypedef)); + } + if (tname instanceof SctlUndefinedTypeName) { + SctlUndefinedTypeName nUndefined = (SctlUndefinedTypeName) tname; + // Just pass it through, perhaps with some modifier + return getType(nUndefined.tname.sel).thenApply(tUndefined -> { + return tUndefined; // TODO: Some indicator of being undefined? + }); + } + if (tname instanceof SctlUnionTypeName) { + SctlUnionTypeName nUnion = (SctlUnionTypeName) tname; + return CompletableFuture.completedFuture(new SctlTargetUnionDataType.Ref(this, nUnion)); + } + throw new AssertionError("Unrecognized SCTL type name: " + tname); + } + + protected CompletableFuture> doGetAllTypes() { + return sequence(TypeSpec.future(this::doGetAllTypes)).then(seq -> { + allDefs.request().handle(seq::next); + }, TypeSpec.map(AbstractSctlTypeName.class, SelSctlTypeDefinition.class)).then((m, seq) -> { + AsyncFence fence = new AsyncFence(); + for (AbstractSctlTypeName tname : m.keySet()) { + fence.include(lazyTypes.get(tname)); + } + fence.ready().handle(seq::next); + }).then((seq) -> { + seq.exit(lazyTypes.getCompletedMap().values()); + }).finish(); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getAllTypes().thenCompose(all -> { + AsyncFence fence = new AsyncFence(); + // NOTE: Some derived data types get added as we process children of named ones + for (TargetDataType t : new ArrayList<>(all)) { + if (t instanceof TargetNamedDataTypeRef) { + TargetNamedDataTypeRef ref = (TargetNamedDataTypeRef) t; + fence.include(ref.fetch()); // Get should cause the element to get added + } + } + return fence.ready(); + }); + } + + public CompletableFuture> getAllTypes() { + return allTypes.request(); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetEnumConstant.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetEnumConstant.java new file mode 100644 index 0000000000..6cc9a9fcfc --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetEnumConstant.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import ghidra.dbg.util.PathUtils; + +public class SctlTargetEnumConstant extends SctlTargetDataTypeMember { + protected static String indexConst(long value) { + return PathUtils.makeIndex(value); + } + + protected static String keyConst(int position) { + return PathUtils.makeKey(indexConst(position)); + } + + public SctlTargetEnumConstant(SctlTargetNamedDataType type, int position, long value, + String memberName) { + super(type, keyConst(position), position, value, memberName, type, "EnumConstant"); + } + + @Override + public String toString() { + String[] parts = parent.module.getIndex().split("/"); + return String.format("", parts[parts.length - 1], + memberName, position); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetEnumDataType.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetEnumDataType.java new file mode 100644 index 0000000000..81b49f0295 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetEnumDataType.java @@ -0,0 +1,72 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.sctl.protocol.types.*; + +public class SctlTargetEnumDataType + extends SctlTargetNamedDataType { + + public static class Ref extends AbstractRef { + protected final SctlEnumTypeName nEnum; + + protected Ref(SctlTargetDataTypeNamespace types, SctlEnumTypeName nEnum) { + super(types, nEnum); + this.nEnum = nEnum; + } + + @Override + protected CompletableFuture doGet() { + return AsyncUtils.sequence(TypeSpec.cls(SctlTargetEnumDataType.class)).then(seq -> { + types.getTypeDef(nEnum).handle(seq::next); + }, TypeSpec.cls(SelSctlTypeDefinition.class)).then((def, seq) -> { + SctlEnumTypeDefinition dEnum = (SctlEnumTypeDefinition) def.sel; + SctlTargetEnumDataType tEnum = new SctlTargetEnumDataType(types, nEnum, dEnum); + types.changeElements(List.of(), List.of(tEnum), "Fetched"); + seq.exit(tEnum); + }).finish(); + } + } + + // TODO: Dispose of this once converted? + protected final SctlEnumTypeDefinition dEnum; + + protected final int byteLength; + + public SctlTargetEnumDataType(SctlTargetDataTypeNamespace types, SctlEnumTypeName tname, + SctlEnumTypeDefinition dEnum) { + super(types, tname, NamedDataTypeKind.ENUM, "EnumType"); + this.dEnum = dEnum; + + this.byteLength = dEnum.rep.getByteLength(); + + changeAttributes(List.of(), Map.of( // + ENUM_BYTE_LENGTH_ATTRIBUTE_NAME, byteLength // + ), "Initialized"); + + List elems = new ArrayList<>(dEnum.consts.size()); + for (int i = 0; i < dEnum.consts.size(); i++) { + SctlEnumConstant c = dEnum.consts.get(i); + elems.add(new SctlTargetEnumConstant(this, i, c.value, c.id.str)); + } + changeElements(List.of(), elems, "Initialized"); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetFunctionDataType.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetFunctionDataType.java new file mode 100644 index 0000000000..0460cf8805 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetFunctionDataType.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncFence; +import ghidra.dbg.sctl.protocol.types.SctlFunctionParameter; +import ghidra.dbg.sctl.protocol.types.SctlFunctionTypeName; + +public class SctlTargetFunctionDataType + extends SctlTargetNamedDataType { + + // TODO: Dispose of this once converted? + protected final SctlFunctionTypeName nFunction; + + public SctlTargetFunctionDataType(SctlTargetDataTypeNamespace types, + SctlFunctionTypeName nFunction) { + super(types, nFunction, NamedDataTypeKind.FUNCTION, "FunctionType"); + this.nFunction = nFunction; + } + + public CompletableFuture collectMembers() { + AsyncFence fence = new AsyncFence(); + List elems = new ArrayList<>(nFunction.params.size()); + fence.include(parent.getType(nFunction.tname.sel).thenAccept(t -> { + SctlTargetFunctionParameter r = + new SctlTargetFunctionParameter(this, -1, "", t); + synchronized (elems) { + elems.add(r); + } + })); + for (int i = 0; i < nFunction.params.size(); i++) { + int pos = i; + SctlFunctionParameter param = nFunction.params.get(i); + fence.include(parent.getType(param.tname.sel).thenAccept(t -> { + SctlTargetFunctionParameter p = + new SctlTargetFunctionParameter(this, pos, param.pname.str, t); + synchronized (elems) { + elems.add(p); + } + })); + } + return fence.ready().thenAccept(__ -> { + changeElements(List.of(), elems, "Initialized"); + }); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetFunctionParameter.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetFunctionParameter.java new file mode 100644 index 0000000000..9bedea3be2 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetFunctionParameter.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import ghidra.dbg.attributes.TargetDataType; +import ghidra.dbg.target.TargetNamedDataType; +import ghidra.dbg.util.PathUtils; + +public class SctlTargetFunctionParameter + extends SctlTargetDataTypeMember { + + protected static String keyParameter(long position) { + return PathUtils.makeKey(indexParameter(position)); + } + + protected static String indexParameter(long position) { + if (position == -1) { + return TargetNamedDataType.FUNCTION_RETURN_INDEX; + } + if (position >= 0) { + return TargetNamedDataType.FUNCTION_PARAMETER_DIM + "," + position; + } + throw new IllegalArgumentException("position = " + position); + } + + public SctlTargetFunctionParameter(SctlTargetFunctionDataType function, int position, + String paramName, TargetDataType dataType) { + super(function, keyParameter(position), position, -1, paramName, dataType, + position == -1 ? "Return" : "Parameter"); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetMemory.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetMemory.java new file mode 100644 index 0000000000..cc5d2e5d07 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetMemory.java @@ -0,0 +1,97 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.memory.*; +import ghidra.dbg.target.TargetMemory; +import ghidra.program.model.address.Address; + +public class SctlTargetMemory extends DefaultTargetObject + implements TargetMemory { + + protected final SctlClient client; + + private final CachedMemory memCache = + new CachedMemory(this::rawReadMemory, this::rawWriteMemory); + private final MemoryWriter memWriter = memCache; + private final MemoryReader memReader = memCache; + + public SctlTargetMemory(SctlTargetProcess process) { + super(process.client, process, "Memory", "Memory"); + this.client = process.client; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (refresh) { + parent.lazyStat.forget(); + } + return parent.lazyStat.request(); + } + + @Override + public CompletableFuture readMemory(Address address, int length) { + return memReader.readMemory(client.addrMapper.mapAddressToOffset(address), length); + } + + private CompletableFuture rawReadMemory(long offset, int length) { + return client.readMemory(parent.primaryCtlid, offset, length).thenApply(data -> { + notifyUpdate(offset, data); + return data; + }); + } + + @Override + public CompletableFuture writeMemory(Address address, byte[] data) { + return memWriter.writeMemory(client.addrMapper.mapAddressToOffset(address), data); + } + + private CompletableFuture rawWriteMemory(long offset, byte[] data) { + return client.writeMemory(parent.primaryCtlid, offset, data).thenAccept(__ -> { + notifyUpdate(offset, data); + }); + } + + protected void notifyUpdate(long offset, byte[] data) { + Address address = client.addrMapper.mapOffsetToAddress(offset); + listeners.fire(TargetMemoryListener.class).memoryUpdated(this, address, data); + } + + @Override + public CompletableFuture invalidateCaches() { + memCache.clear(); + return AsyncUtils.NIL; + } + + protected void clearRegions() { + setElements(List.of(), "Refreshing"); + } + + protected void addRegion(SctlTargetMemoryRegion region) { + // TODO: Rewrite this to use setElements after all regions are collected + changeElements(List.of(), List.of(region), "Refreshed"); + } + + protected void invalidateMemoryCaches() { + memCache.clear(); + listeners.fire.invalidateCacheRequested(this); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetMemoryRegion.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetMemoryRegion.java new file mode 100644 index 0000000000..18bc48e9a8 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetMemoryRegion.java @@ -0,0 +1,96 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.Map; + +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetMemoryRegion; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.*; + +public class SctlTargetMemoryRegion extends DefaultTargetObject + implements TargetMemoryRegion { + protected static final String FILE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "file"; + + protected static String keyRegion(Address address) { + return PathUtils.makeKey(indexRegion(address)); + } + + protected static String indexRegion(Address address) { + return address.toString(); + } + + protected final SctlClient client; + + protected final String file; + protected final AddressRange range; + protected final BitmaskSet protections; + + public SctlTargetMemoryRegion(SctlTargetMemory memory, String file, Address address, + long length, BitmaskSet protections) + throws AddressOverflowException { + super(memory.client, memory, keyRegion(address), "MemoryRegion"); + this.client = memory.client; + + this.file = file; + this.range = new AddressRangeImpl(address, address.addNoWrap(length - 1)); + this.protections = protections; + + changeAttributes(List.of(), Map.of( // + FILE_ATTRIBUTE_NAME, file, // + RANGE_ATTRIBUTE_NAME, range, // + READABLE_ATTRIBUTE_NAME, isReadable(), // + WRITABLE_ATTRIBUTE_NAME, isWritable(), // + EXECUTABLE_ATTRIBUTE_NAME, isExecutable() // + ), "Initialized"); + } + + @Override + public String getDisplay() { + if (file != null) { + return range.getMinAddress() + " (" + file + ")"; + } + return range.getMinAddress().toString(); + } + + @Override + public AddressRange getRange() { + return range; + } + + public String getFile() { + return file; + } + + @Override + public boolean isReadable() { + return protections.contains(SctlMemoryProtection.PROT_READ); + } + + @Override + public boolean isWritable() { + return protections.contains(SctlMemoryProtection.PROT_WRITE); + } + + @Override + public boolean isExecutable() { + return protections.contains(SctlMemoryProtection.PROT_EXEC); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetModule.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetModule.java new file mode 100644 index 0000000000..7cef1ed4eb --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetModule.java @@ -0,0 +1,116 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.*; + +import ghidra.async.AsyncLazyMap; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.sctl.protocol.consts.Mkind; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.*; + +/** + * A namespace on the SCTL server + * + * Typically, a namespace belongs to a single target, but it is possible, e.g., if two targets share + * a common library, for a single namespace to belong to multiple targets. SCTL also generates a + * namespace on a {@code Tnames} command, which is not associated with any particular CTLID. + * + * The symbols and types from a namespace are cached for the lifetime of this proxy object, because + * they should never change during the life of the CTLID or NSID. This is accomplished using an + * {@link AsyncLazyMap} + */ +public class SctlTargetModule + extends DefaultTargetObject + implements TargetModule { + + protected static String keyModule(String filepath) { + return PathUtils.makeKey(indexModule(filepath)); + } + + protected static String indexModule(String filepath) { + return filepath; + } + + protected final SctlClient client; + protected final long nsid; + protected final String filepath; + protected final Address base; + protected AddressRangeImpl range; + protected final boolean executable; + + protected final SctlTargetSectionContainer sections; + protected final SctlTargetSymbolNamespace symbols; + protected final SctlTargetDataTypeNamespace types; + + /** + * Construct a module proxy + * + * The given path is used as the name of the module. + * + * @see SctlClient#createModule(long, String, Address) + * @param process the SCTL process to which the namespace belongs + * @param nsid the SCTL-assigned nsid "namespace ID" + * @param path the path from the {@link Mkind#Tnames} or {@link Mkind#Rstat} message + * @param executable true if this is the executable image (not a library) + */ + public SctlTargetModule(SctlTargetModuleContainer modules, long nsid, String filepath, + Address base, boolean executable) { + super(modules.client, modules, keyModule(filepath), "Module"); + this.client = modules.client; + this.nsid = nsid; + this.filepath = filepath; + this.base = base; + range = new AddressRangeImpl(base, base); + this.executable = executable; + + this.sections = new SctlTargetSectionContainer(this); + this.symbols = new SctlTargetSymbolNamespace(this); + this.types = new SctlTargetDataTypeNamespace(this); + + changeAttributes(List.of(), Map.of( // + RANGE_ATTRIBUTE_NAME, range, // + sections.getName(), sections, // + symbols.getName(), symbols, // + types.getName(), types // + ), "Initialized"); + } + + @Override + public AddressRange getRange() { + return range; + } + + protected void addSection(String name, Address start, long length) { + sections.add(name, start, length); + } + + public void updateRange() { + Address max = sections.getCachedElements() + .values() + .stream() + .map(s -> s.getRange().getMaxAddress()) + .max(Comparator.naturalOrder()) + .orElse(null); + range = new AddressRangeImpl(base, max); + changeAttributes(List.of(), Map.of( + RANGE_ATTRIBUTE_NAME, range), + "Have sections"); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetModuleContainer.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetModuleContainer.java new file mode 100644 index 0000000000..4a387afc81 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetModuleContainer.java @@ -0,0 +1,83 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.lifecycle.Internal; +import ghidra.program.model.address.Address; + +public class SctlTargetModuleContainer + extends DefaultTargetObject { + + protected final SctlClient client; + + private final Map modulesByNsid = new LinkedHashMap<>(); + + public SctlTargetModuleContainer(SctlTargetProcess process) { + super(process.client, process, "Modules", "ModuleContainer"); + this.client = process.client; + } + + /** + * Create a module proxy + * + * This is preferred to calling {@link SctlTargetModule(SctlClient, long, String, boolean)} + * directly, since this will add the module to the client's container. + * + * @param nsid the SCTL-assigned NSID "namespace ID" + * @param filepath the path given in {@code Tnames} + * @param executable the module is the executable image defining this process + * @return the new module proxy + */ + @Internal + public SctlTargetModule create(long nsid, String filepath, Address base, boolean executable) { + SctlTargetModule ns = + new SctlTargetModule(this, nsid, filepath, base, executable); + client.session.processes.putModule(nsid, ns); + modulesByNsid.put(nsid, ns); + changeElements(List.of(), List.of(ns), "Refreshed"); + return ns; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (refresh) { + parent.lazyStat.forget(); + } + return parent.lazyStat.request(); + } + + protected void clear() { + // TODO: For refreshing, would rather collect and the use setElements + synchronized (this) { + client.session.processes.removeAllModules(modulesByNsid); + modulesByNsid.clear(); + } + setElements(List.of(), "Refreshing"); + } + + protected String getExecutablePath() { + for (SctlTargetModule module : modulesByNsid.values()) { + if (module.executable) { + return module.filepath; + } + } + return null; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetNamedDataType.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetNamedDataType.java new file mode 100644 index 0000000000..66d7584b6c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetNamedDataType.java @@ -0,0 +1,134 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncLazyValue; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.attributes.TargetNamedDataTypeRef; +import ghidra.dbg.sctl.protocol.types.*; +import ghidra.dbg.target.TargetNamedDataType; +import ghidra.dbg.util.PathUtils; + +public abstract class SctlTargetNamedDataType, M extends SctlTargetDataTypeMember> + extends DefaultTargetObject + implements TargetNamedDataType { + + protected static String indexStruct(SctlStructTypeName tStruct) { + return "struct " + tStruct.tag.str; + } + + protected static String indexUnion(SctlUnionTypeName tUnion) { + return "union " + tUnion.tag.str; + } + + protected static String indexEnum(SctlEnumTypeName tEnum) { + return "enum " + tEnum.tag.str; + } + + protected static String indexTypedef(SctlTypedefTypeName tTypedef) { + return "typedef " + tTypedef.tid.str; + } + + protected static String indexFunction(SctlFunctionTypeName tFunction) { + // SCTL does not name these. It'd be nice to have something more stable + return "function " + "sig" + System.identityHashCode(tFunction); + } + + protected static String indexType(AbstractSctlTypeName tname) { + if (tname instanceof SctlStructTypeName) { + return indexStruct((SctlStructTypeName) tname); + } + if (tname instanceof SctlUnionTypeName) { + return indexUnion((SctlUnionTypeName) tname); + } + if (tname instanceof SctlEnumTypeName) { + return indexEnum((SctlEnumTypeName) tname); + } + if (tname instanceof SctlTypedefTypeName) { + return indexTypedef((SctlTypedefTypeName) tname); + } + if (tname instanceof SctlFunctionTypeName) { + return indexFunction((SctlFunctionTypeName) tname); + } + throw new AssertionError("Unrecognized SCTL type kind: " + tname); + } + + protected static String keyType(AbstractSctlTypeName tname) { + return PathUtils.makeKey(indexType(tname)); + } + + public static abstract class AbstractRef> + implements TargetNamedDataTypeRef { + protected final SctlTargetDataTypeNamespace types; + protected final List path; + protected final AsyncLazyValue t = new AsyncLazyValue<>(this::doGet); + + protected AbstractRef(SctlTargetDataTypeNamespace types, AbstractSctlTypeName tname) { + this.types = types; + this.path = PathUtils.index(types.getPath(), SctlTargetNamedDataType.indexType(tname)); + } + + protected abstract CompletableFuture doGet(); + + @Override + public CompletableFuture fetch() { + return t.request(); + } + + @Override + public DebuggerObjectModel getModel() { + return types.client; + } + + @Override + public List getPath() { + return path; + } + } + + protected final SctlClient client; + protected final SctlTargetModule module; + + protected final NamedDataTypeKind kind; + + protected SctlTargetNamedDataType(SctlTargetDataTypeNamespace types, + AbstractSctlTypeName tname, NamedDataTypeKind kind, String typeHint) { + super(types.client, types, keyType(tname), typeHint); + this.client = types.client; + this.module = types.getImplParent(); + + this.kind = kind; + + changeAttributes(List.of(), Map.of( // + NAMED_DATA_TYPE_KIND_ATTRIBUTE_NAME, kind // + ), "Initialized"); + } + + @Override + public String toString() { + return "<" + getClass().getSimpleName() + " " + getIndex() + ">"; + } + + @Override + public NamedDataTypeKind getKind() { + return kind; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetObject.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetObject.java new file mode 100644 index 0000000000..4aa487d89b --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetObject.java @@ -0,0 +1,210 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; + +import ghidra.async.AsyncLazyValue; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.sctl.protocol.common.AbstractSctlObjectEntry; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.CollectionUtils.Delta; +import ghidra.dbg.util.PathUtils; +import ghidra.dbg.util.PathUtils.TargetObjectKeyComparator; +import ghidra.util.datastruct.ListenerSet; + +/** + * A target thread on the SCTL server + */ +public class SctlTargetObject implements TargetObject { + public static final String PATH_SEPARATOR_STRING = "|"; + public static final String PATH_SEPARATOR_REGEX = "\\|"; + + private final SctlClient client; + private final String joinedPath; + private final List path; + private final int hash; + boolean valid = true; + + final AsyncLazyValue> lazyAttributes = + new AsyncLazyValue<>(this::doGetAttributes); + final AsyncLazyValue> lazyElements = + new AsyncLazyValue<>(this::doGetChildren); + private String kind; + private String value; + private String type; + // NOTE: 'key' is last element of the path. The method getName() retrieves it. + + // TODO: Create one of these per object proxy, and ensure users act accordingly + // For now, aliasing to client.listenersObject + protected final ListenerSet listeners; + + public SctlTargetObject(SctlClient client, SctlTargetObjectsContainer objects, + List path, String kind, String value, String type) { + this.client = client; + this.joinedPath = StringUtils.join(path, PATH_SEPARATOR_STRING); + this.path = PathUtils.extend(objects.getPath(), path); + this.hash = computeHashCode(); + this.kind = kind; + this.value = value; + this.type = type; + + this.listeners = client.listenersObject; + } + + public SctlTargetObject(SctlClient client, AbstractSctlObjectEntry entry) { + this.client = client; + this.joinedPath = entry.getPath().str; + this.path = List.of(joinedPath.split(PATH_SEPARATOR_REGEX)); + this.hash = computeHashCode(); + this.kind = entry.getKind().str; + this.value = entry.getValue().str; + this.type = entry.getType().str; + + this.listeners = client.listenersObject; + } + + @Override + public boolean equals(Object obj) { + return doEquals(obj); + } + + @Override + public int hashCode() { + return hash; + } + + protected void checkValid() { + if (!valid) { + throw new IllegalStateException( + "This thread handle is no longer valid, i.e., the thread has been destroyed."); + } + } + + protected CompletableFuture> doGetAttributes() { + return lazyAttributes.request(); + } + + protected CompletableFuture> doGetChildren() { + return lazyElements.request(); + } + + @Override + public SctlClient getModel() { + return client; + } + + @Override + public Object getProtocolID() { + return path; + } + + @Override + public List getPath() { + return path; + } + + @Override + public String getDisplay() { + if (value != null && !value.equals("")) { + if (type != null && !type.equals("")) { + return value + ":" + type; + } + return value; + } + return ""; + //return getName() + " " + kind + " " + value + " " + type; + } + + @Override + public String getTypeHint() { + return kind; + } + + @Override + public boolean isValid() { + return valid; + } + + @Override + public CompletableFuture> fetchElements() { + return getModel().getElements(path); + } + + @Override + public CompletableFuture> fetchAttributes() { + return getModel().getAttributes(path); + } + + @Override + public void addListener(TargetObjectListener l) { + listeners.add(l); + } + + @Override + public void removeListener(TargetObjectListener l) { + listeners.remove(l); + } + + public String getJoinedPath() { + return joinedPath; + } + + @Override + public synchronized Map getCachedElements() { + if (lazyElements.isDone()) { + return lazyElements.request().getNow(null); + } + return Map.of(); + } + + @Override + public synchronized Map getCachedAttributes() { + if (lazyAttributes.isDone()) { + return lazyAttributes.request().getNow(null); + } + return Map.of(); + } + + protected void notifyAttributes(Map map) { + Delta delta; + synchronized (this) { + CompletableFuture> future = lazyAttributes.provide(); + if (!future.isDone()) { + future.complete(new TreeMap<>(TargetObjectKeyComparator.ATTRIBUTE)); + } + Map attrs = future.getNow(null); + delta = Delta.computeAndSet(attrs, map, Delta.EQUAL); + } + listeners.fire.attributesChanged(this, delta.getKeysRemoved(), delta.added); + } + + protected void notifyElements(Map map) { + Delta delta; + synchronized (this) { + CompletableFuture> future = lazyElements.provide(); + if (!future.isDone()) { + future.complete(new TreeMap<>(TargetObjectKeyComparator.ELEMENT)); + } + Map elems = future.getNow(null); + delta = Delta.computeAndSet(elems, map, Delta.SAME); + } + listeners.fire.elementsChanged(this, delta.getKeysRemoved(), delta.added); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetObjectsContainer.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetObjectsContainer.java new file mode 100644 index 0000000000..d91a27a84e --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetObjectsContainer.java @@ -0,0 +1,118 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.io.InvalidObjectException; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; + +public class SctlTargetObjectsContainer + extends DefaultTargetObject { + + protected final Map objectsByJoined = new LinkedHashMap<>(); + protected final SctlClient client; + + public SctlTargetObjectsContainer(SctlTargetSession session) { + super(session.client, session, "Objects", "ObjectsContainer"); + this.client = session.client; + } + + /** + * Create an object + * + * This is preferred to calling + * {@link SctlTargetObject#SctlTargetObject(SctlClient, String, String, String, String, String)} + * directly, since this will add the object to the client container. + * + * Note this does not check if another object already exists at the given path + * + * @param key the id for the object + * @param sub the path used to identify the object terminating in the key + * @param kind the generic container type + * @params value the value of the object if it exists + * @params type the type of the object if it exists + * @return the new object proxy + * @throws InvalidObjectException + */ + protected SctlTargetObject create(String key, List sub, String kind, + String value, String type) throws InvalidObjectException { + assert key.equals(PathUtils.getKey(sub)); + if (sub == null || sub.isEmpty()) { + throw new IllegalArgumentException("sub cannot be null or empty"); + } + List fullPath = PathUtils.extend(getPath(), sub); + List parentPath = PathUtils.parent(fullPath); + String joinedParent = StringUtils.join(parentPath, SctlTargetObject.PATH_SEPARATOR_STRING); + TargetObject newParent = + parentPath.equals(getPath()) ? this : objectsByJoined.get(joinedParent); + if (newParent == null) { + throw new IllegalArgumentException( + "parent of newPath=" + newParent + ", which does not exist"); + } + + SctlTargetObject object = new SctlTargetObject(client, this, sub, kind, value, type); + if (object.getTypeHint().equals("OBJECT_ERROR")) { + throw new InvalidObjectException(object.getDisplay()); + } + objectsByJoined.put(StringUtils.join(sub, SctlTargetObject.PATH_SEPARATOR_STRING), object); + return object; + } + + protected List findObjectByKey(String key) { + return objectsByJoined.values() + .stream() + .filter(o -> key.equals(o.getName())) + .collect(Collectors.toList()); + } + + @Override + public CompletableFuture fetchSuccessor(List sub) { + if (sub.isEmpty()) { + return CompletableFuture.completedFuture(this); + } + String joinedSub = StringUtils.join(sub, SctlTargetObject.PATH_SEPARATOR_STRING); + return CompletableFuture.completedFuture(getObject(joinedSub)); + } + + protected SctlTargetObject getObject(String sub) { + return objectsByJoined.get(sub); + } + + protected SctlTargetObject require(String sub) { + SctlTargetObject object = objectsByJoined.get(sub); + if (object == null) { + throw new IllegalArgumentException("No such object: path=" + path); + } + return object; + } + + protected void notifyAttributes(String sub, Map map) { + SctlTargetObject obj = require(sub); + obj.notifyAttributes(map); + } + + protected void notifyElements(String sub, Map map) { + SctlTargetObject obj = require(sub); + obj.notifyElements(map); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetProcess.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetProcess.java new file mode 100644 index 0000000000..f98ca547d9 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetProcess.java @@ -0,0 +1,278 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncLazyValue; +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.*; +import ghidra.dbg.util.PathUtils; +import ghidra.lifecycle.Internal; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressOverflowException; +import ghidra.util.Msg; + +/** + * A target process on the SCTL server + */ +public class SctlTargetProcess extends DefaultTargetObject + implements TargetProcess, TargetAggregate, + TargetExecutionStateful { + //protected static int READ_COALESCE_DELAY_MS = 100; + //protected static int READ_PAGE_SIZE = 4096; // TODO: Make this configurable + + protected static final String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid"; + protected static final String PLATFORM_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "platform"; + + protected static String keyProcess(long ctlid) { + return PathUtils.makeKey(indexProcess(ctlid)); + } + + protected static String indexProcess(long ctlid) { + return PathUtils.makeIndex(ctlid); + } + + protected final SctlClient client; + + protected final long primaryCtlid; // CTLID of "primary" thread + + protected final AsyncLazyValue lazyStat = new AsyncLazyValue<>(this::doStat); + protected Long pid; + protected final String platform; + + protected final SctlTargetThreadContainer threads; + protected final SctlTargetModuleContainer modules; + protected final SctlTargetMemory memory; + + /** + * Construct a process + * + * SCTL does not have a distinct process object, so this is not technically a proxy object. It + * is simply a container to help track threads and properly implement Ghidra's debugging model. + * + * @see SctlClient#createProcess(long, long) + * @param client the client controlling the process + * @param primaryCtlid any CTLID, preferably the first observed, belonging to the process + * @param pid the PID, if known, of the process, or -1 + */ + protected SctlTargetProcess(SctlTargetProcessContainer processes, long primaryCtlid, Long pid, + String platform) { + super(processes.client, processes, keyProcess(primaryCtlid), "ProcessCTL"); + this.client = processes.client; + this.primaryCtlid = primaryCtlid; + this.pid = pid; + this.platform = platform; + + this.memory = new SctlTargetMemory(this); + this.modules = new SctlTargetModuleContainer(this); + this.threads = new SctlTargetThreadContainer(this); + + changeAttributes(List.of(), Map.of( // + PLATFORM_ATTRIBUTE_NAME, platform, // TODO: Use an environment object? + memory.getName(), memory, // + modules.getName(), modules, // + threads.getName(), threads // + ), "Initialized"); + if (pid != null) { + changeAttributes(List.of(), Map.of( // + PID_ATTRIBUTE_NAME, pid // + ), "Fetched"); + } + invalidateStat(); + } + + @Override + public String getName() { + return "Process CTL " + primaryCtlid; + } + + @Override + public SctlClient getModel() { + return client; + } + + /** + * Create a thread proxy + * + * This is preferred to calling + * {@link SctlTargetThread#SctlTargetThread(SctlTargetProcess, long, TargetThreadDisposition)} + * directly, since this will add the thread to the process's and client's containers. + * + * @param ctlid the SCTL-assigned CTLID "control ID" + * @param reason an explanation of the thread's existence + * @return the new thread + */ + protected SctlTargetThread createThread(long ctlid, String reason) { + SctlTargetThread newThread = new SctlTargetThread(threads, this, ctlid); + threads.put(ctlid, newThread, reason); + return newThread; + } + + protected void checkValid() { + if (!valid) { + throw new IllegalStateException( + "This process handle is no longer valid, i.e., the process has been destroyed."); + } + } + + /** + * Create a memory region + * + * This is preferred to calling + * {@link SctlMemoryRegion#SctlMemoryRegion(SctlTargetProcess, String, Address, long, BitmaskSet)} + * directly, since this will add the region to the process's container. + * + * @param file the name of the file defining the region + * @param addr the starting address (VMA) of the region + * @param len the length, in bytes, of the region + * @param flags the permission flags of the region from the protocol message + * @return the new region + * @throws AddressOverflowException if the region exceeds the bounds of the memory space + */ + @Internal + public SctlTargetMemoryRegion createMemoryRegion(String file, long addr, long len, + BitmaskSet flags) throws AddressOverflowException { + Address start = client.addrMapper.mapOffsetToAddress(addr); + SctlTargetMemoryRegion region = new SctlTargetMemoryRegion(memory, file, start, len, flags); + memory.addRegion(region); + return region; + } + + protected void setThreadState(SctlTargetThread thread, TargetExecutionState state) { + if (state == TargetExecutionState.RUNNING) { + memory.invalidateMemoryCaches(); + } + if (thread.getExecutionState() == TargetExecutionState.RUNNING) { + if (state != TargetExecutionState.RUNNING) { + // Forget what to do here.... + } + } + thread.setExecutionStateInternal(state); + TargetExecutionState procState = + isAnyThreadRunning() ? TargetExecutionState.RUNNING : TargetExecutionState.STOPPED; + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, procState // + ), "State changed"); + listeners.fire(TargetExecutionStateListener.class).executionStateChanged(this, procState); + } + + protected boolean isAnyThreadRunning() { + for (SctlTargetThread thread : threads.getCachedElements().values()) { + if (thread.isRunning()) { + return true; + } + } + return false; + } + + /** + * Destroy a thread, invoking listeners + * + * @param ctlid the CTLID of the thread + */ + protected void destroyThread(long ctlid, String reason) { + SctlTargetThread removed = threads.removeByCtlid(ctlid, reason); + if (removed == null) { + return; + } + if (threads.getCachedElements().isEmpty()) { + parent.destroy(primaryCtlid, reason); + } + } + + /** + * Clear and re-request stat + */ + protected void invalidateStat() { + lazyStat.forget(); + modules.clear(); + memory.clearRegions(); + lazyStat.request().exceptionally(e -> { + Msg.error(this, "Could not stat", e); + return null; + }); + } + + /** + * Set the PID of this process + * + * Unfortunately, SCTL's {@code Rlaunch} does not provide the PID of the newly-launched process. + * However, it can be retrieved via a {@code Tstat}. Thus overall, the PID an come from multiple + * places. The client does not actively seek a PID, but will store it opportunistically. When a + * process is created via {@code Tattach}, the PID is immediately known from the command and + * stored. In other circumstances, the PID is stored if/when {@code Rstat} is observed. A call + * to {@link #getPid()} will send a {@code Tstat} if the PID is not already known. + * + * Due to code organization, this method must be public, which is not ideal. Ghidra's + * {@link TargetProcess} does not include this method, so it cannot be accessed without a cast, + * but it can be accessed without reflection. + * + * Implementation note: The official sctl process control server tends to use the PID as the + * CTLID; however, that appears to be an implementation decision on their part as nothing in the + * specification requires it. Also, a second thread in the same process has a CTLID different + * than the PID. Granted, its PID would be the same as its spawning thread. + * + * @param pid the PID + */ + @Internal + public void setPid(long pid) { + this.pid = pid; + parent.notifyProcPid(this, pid); + changeAttributes(List.of(), Map.of( // + PID_ATTRIBUTE_NAME, pid, // + DISPLAY_ATTRIBUTE_NAME, getDisplay() // + ), "Fetched"); + } + + protected CompletableFuture doStat() { + // NOTE: Client populates fields on completion + return client.stat(primaryCtlid); + } + + public Long getPid() { + return pid; + } + + public String getPlatform() { + return platform; + } + + // TODO: Ensure someone calls stat + + @Override + public String toString() { + if (!valid) { + return ""; + } + return ""; + } + + @Override + public synchronized String getDisplay() { + if (!valid) { + return "Process INVALID"; + } + String exec = modules.getExecutablePath(); + if (exec == null) { + return "Process " + pid; + } + return "Process " + pid + " " + exec; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetProcessContainer.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetProcessContainer.java new file mode 100644 index 0000000000..3a5ad5f6a6 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetProcessContainer.java @@ -0,0 +1,156 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.*; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; + +public class SctlTargetProcessContainer + extends DefaultTargetObject { + + protected final SctlClient client; + + // ID proc by CTLID of its main thread, because PID is not always known + private final Map procsByCtlid = new LinkedHashMap<>(); + private final Map procsByPid = new LinkedHashMap<>(); + + // Manage the global lists here, but dole them out by proc + private final Map threadsByCtlid = new LinkedHashMap<>(); + private final Map modulesByNsid = new LinkedHashMap<>(); + + public SctlTargetProcessContainer(SctlTargetSession session) { + super(session.client, session, "Processes", "ProcessContainer"); + this.client = session.client; + } + + /** + * Create a process + * + * This is preferred to calling + * {@link SctlTargetProcess#SctlTargetProcess(SctlClient, long, long)} directly, since this will + * add the process to the client container. + * + * @param ctlid the CTLID of the primary thread + * @param pid the PID + * @return the new process proxy + */ + protected SctlTargetProcess create(long ctlid, Long pid, String platform) { + SctlTargetProcess proc = new SctlTargetProcess(this, ctlid, pid, platform); + procsByCtlid.put(ctlid, proc); + changeElements(List.of(), List.of(proc), "Created"); + if (pid == null) { + return proc; + } + notifyProcPid(proc, pid); + return proc; + } + + protected synchronized SctlTargetProcess getByCtlid(long ctlid) { + return procsByCtlid.get(ctlid); + } + + protected synchronized SctlTargetProcess require(long ctlid) { + SctlTargetProcess proc = procsByCtlid.get(ctlid); + if (proc == null) { + throw new NoSuchElementException("No such process: primary ctlid=" + ctlid); + } + return proc; + } + + protected synchronized void notifyProcPid(SctlTargetProcess proc, long pid) { + SctlTargetProcess collision = procsByPid.put(pid, proc); + if (collision != null && collision != proc) { + throw new AssertionError("Process " + pid + " is already known"); + } + } + + protected synchronized SctlTargetProcess getByPid(long pid) { + return procsByPid.get(pid); + } + + /** + * Destroy a process + * + * This does not generate any SCTL command. + * + * @param ctlid the CTLID of the primary thread + */ + protected SctlTargetProcess destroy(long ctlid, String reason) { + SctlTargetProcess proc; + synchronized (this) { + proc = procsByCtlid.remove(ctlid); + } + if (proc == null) { + throw new AssertionError("No such process: ctlid=" + ctlid); + } + Long pid = proc.getPid(); + if (pid != null) { + synchronized (this) { + proc = procsByPid.remove(pid); + } + if (proc == null) { + throw new AssertionError("procByPid out of sync with procsByCtlid"); + } + } + changeElements(List.of(proc.getIndex()), List.of(), reason); + return proc; + } + + protected synchronized void putThread(long ctlid, SctlTargetThread thread) { + threadsByCtlid.put(ctlid, thread); + } + + protected synchronized SctlTargetThread requireThread(long ctlid) { + SctlTargetThread thread = threadsByCtlid.get(ctlid); + if (thread == null) { + throw new NoSuchElementException("No such thread: ctlid=" + ctlid); + } + return thread; + } + + protected void setThreadState(long ctlid, TargetExecutionState state) { + SctlTargetThread thread = requireThread(ctlid); + thread.setState(state); + } + + protected synchronized SctlTargetThread removeThread(long ctlid) { + return threadsByCtlid.remove(ctlid); + } + + /** + * Destroy a thread, invoking listeners + * + * @param ctlid the CTLID of the thread + */ + protected void destroyThread(long ctlid, String reason) { + SctlTargetThread thread = requireThread(ctlid); + thread.process.destroyThread(ctlid, reason); + } + + protected synchronized void putModule(long nsid, SctlTargetModule module) { + modulesByNsid.put(nsid, module); + } + + protected synchronized SctlTargetModule getModule(long nsid) { + return modulesByNsid.get(nsid); + } + + protected synchronized void removeAllModules(Map modules) { + modulesByNsid.keySet().removeAll(modules.keySet()); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetRegisterDescription.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetRegisterDescription.java new file mode 100644 index 0000000000..2e65d6449f --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetRegisterDescription.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.Map; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetRegister; +import ghidra.dbg.util.PathUtils; + +public class SctlTargetRegisterDescription + extends DefaultTargetObject + implements TargetRegister { + + protected static String indexRegister(String name) { + return name; + } + + protected static String keyRegister(String name) { + return PathUtils.makeKey(indexRegister(name)); + } + + protected final String name; + protected final int bitLength; + + public SctlTargetRegisterDescription(SctlTargetRegisters registers, String name, + int bitLength) { + super(registers.client, registers, keyRegister(name), "Register"); + this.name = name; + this.bitLength = bitLength; + + changeAttributes(List.of(), Map.of( // + LENGTH_ATTRIBUTE_NAME, bitLength // + ), "Initialized"); + } + + @Override + public int getBitLength() { + return bitLength; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetRegisters.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetRegisters.java new file mode 100644 index 0000000000..ee72bf62f6 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetRegisters.java @@ -0,0 +1,187 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncLazyValue; +import ghidra.async.AsyncUtils; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; +import ghidra.dbg.sctl.protocol.common.SctlRegisterDefinition; +import ghidra.dbg.target.*; +import ghidra.dbg.util.CollectionUtils.Delta; + +public class SctlTargetRegisters + extends DefaultTargetObject implements + TargetRegisterBank, TargetRegisterContainer { + + protected final SctlClient client; + + protected final AsyncLazyValue> lazyRegDefs = + new AsyncLazyValue<>(this::doGetRegDefs); + protected final AsyncLazyValue> lazyRegDefsById = + new AsyncLazyValue<>(this::doGetRegDefsById); + protected final AsyncLazyValue> lazyRegDescs = + new AsyncLazyValue<>(this::doGetRegDescs); + + private final List selectedRegisters = new ArrayList<>(); + + private Map ctx = new LinkedHashMap<>(); + private boolean hasCtx = false; + + public SctlTargetRegisters(SctlTargetThread thread) { + super(thread.client, thread, "Registers", "RegisterBank"); + this.client = thread.client; + + changeAttributes(List.of(), Map.of( // + // TODO: Probably have one container for the whole client + DESCRIPTIONS_ATTRIBUTE_NAME, this // + ), "Initialized"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (refresh) { + lazyRegDescs.forget(); + lazyRegDefsById.forget(); + lazyRegDefs.forget(); + } + return lazyRegDescs.request().thenApply(__ -> null); + } + + @Override + public CompletableFuture> readRegistersNamed( + Collection names) { + parent.checkValid(); + return client.readRegisters(parent.ctlid); + } + + @Override + public CompletableFuture writeRegistersNamed(Map values) { + parent.checkValid(); + return client.writeRegisters(parent.ctlid, values); + } + + protected CompletableFuture selectRegisters(Set descs) { + // TODO: What if one of these is already executing? + Set curNames = new HashSet<>(); + Set newNames = new HashSet<>(); + for (SctlRegisterDefinition def : selectedRegisters) { + curNames.add(def.name.str); + } + for (TargetRegister reg : descs) { + newNames.add(reg.getName()); + } + if (curNames.equals(newNames)) { + return AsyncUtils.NIL; + } + return parent.process.client.chooseContext(parent.ctlid, descs); + } + + protected CompletableFuture> doGetRegDefs() { + return client.enumerateContext(parent.ctlid); + } + + protected CompletableFuture> doGetRegDefsById() { + return lazyRegDefs.request().thenApply(this::reKeyDefsById); + } + + protected Map reKeyDefsById( + Map defsByName) { + Map result = new HashMap<>(); + for (SctlRegisterDefinition def : defsByName.values()) { + result.put(def.regid, def); + } + return result; + } + + protected CompletableFuture> doGetRegDescs() { + return lazyRegDefs.request().thenApply(this::convertDefsToDescs); + } + + protected Map convertDefsToDescs( + Map defs) { + Map result = new LinkedHashMap<>(); + for (SctlRegisterDefinition def : defs.values()) { + result.put(def.name.str, + new SctlTargetRegisterDescription(this, def.name.str, (int) def.nbits)); + } + changeElements(List.of(), result.values(), "Fetched"); + return result; + } + + protected void updateContextIfPresent(AbstractSctlContext newCtx) { + if (newCtx == null) { + return; + } + newCtx.setSelectedRegisters(selectedRegisters); + Map newAsMap = newCtx.toMap(); + Delta delta; + synchronized (ctx) { + delta = Delta.computeAndSet(ctx, newAsMap, Arrays::equals); + } + if (delta.added.isEmpty()) { + return; + } + listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, delta.added); + } + + protected boolean hasContextSinceStop() { + return hasCtx; + } + + protected Map getContext() { + synchronized (ctx) { + return Map.copyOf(ctx); + } + } + + protected List getSelectedRegisters() { + return selectedRegisters; + } + + protected void setSelectedRegisters(Collection selectedRegisters) { + if (!this.selectedRegisters.containsAll(selectedRegisters)) { + this.hasCtx = false; + } + this.selectedRegisters.clear(); + this.selectedRegisters.addAll(selectedRegisters); + } + + @Override + public Map getCachedRegisters() { + return getContext(); + } + + @Override + public CompletableFuture invalidateCaches() { + synchronized (ctx) { + hasCtx = false; + ctx.clear(); + } + return super.invalidateCaches(); + } + + protected void invalidateCtx() { + synchronized (ctx) { + hasCtx = false; + ctx.clear(); + } + listeners.fire.invalidateCacheRequested(this); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSection.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSection.java new file mode 100644 index 0000000000..065add5957 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSection.java @@ -0,0 +1,66 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.Map; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.target.*; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.AddressRange; + +public class SctlTargetSection extends DefaultTargetObject + implements TargetSection { + + protected static String keySection(String name) { + return PathUtils.makeKey(indexSection(name)); + } + + protected static String indexSection(String name) { + return name; + } + + protected final SctlClient client; + + protected final SctlTargetModule module; + protected final AddressRange range; + + public SctlTargetSection(SctlTargetSectionContainer sections, SctlTargetModule module, + String name, AddressRange range) { + super(sections.client, sections, keySection(name), "Section"); + this.client = sections.client; + + this.module = module; + this.range = range; + + changeAttributes(List.of(), Map.of( // + MODULE_ATTRIBUTE_NAME, module, // + RANGE_ATTRIBUTE_NAME, range // + ), "Initialized"); + } + + @Override + public TypedTargetObjectRef> getModule() { + return module; + } + + @Override + public AddressRange getRange() { + return range; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSectionContainer.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSectionContainer.java new file mode 100644 index 0000000000..0f131efda2 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSectionContainer.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.program.model.address.*; +import ghidra.util.Msg; + +public class SctlTargetSectionContainer + extends DefaultTargetObject { + + protected final SctlClient client; + + public SctlTargetSectionContainer(SctlTargetModule module) { + super(module.client, module, "Sections", "SectionContainer"); + this.client = module.client; + } + + protected void add(String name, Address start, long length) { + if (length == 0) { + Msg.trace(this, "Ignoring 0-length section: " + name); + return; + } + + AddressRange range; + try { + range = new AddressRangeImpl(start, length); + } + catch (AddressOverflowException e) { + throw new IllegalArgumentException(e); + } + SctlTargetSection section = new SctlTargetSection(this, parent, name, range); + changeElements(List.of(), List.of(section), "Fetched"); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSession.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSession.java new file mode 100644 index 0000000000..5445c2e488 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSession.java @@ -0,0 +1,133 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.agent.DefaultTargetModelRoot; +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.error.DebuggerModelNoSuchPathException; +import ghidra.dbg.error.DebuggerModelTypeException; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetConsole.Channel; +import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.util.PathUtils; + +public class SctlTargetSession extends DefaultTargetModelRoot implements + TargetAttacher, + TargetFocusScope, + TargetInterpreter, + TargetCmdLineLauncher { + + protected final SctlClient client; + + protected final SctlTargetAttachableContainer attachable; + protected final SctlTargetProcessContainer processes; + protected final SctlTargetObjectsContainer objects; + + public SctlTargetSession(SctlClient client) { + super(client, "Session"); + this.client = client; + + attachable = new SctlTargetAttachableContainer(this); + processes = new SctlTargetProcessContainer(this); + objects = new SctlTargetObjectsContainer(this); + + changeAttributes(List.of(), Map.of( // + attachable.getName(), attachable, // + processes.getName(), processes, // + objects.getName(), objects // + ), "Initialized"); + } + + @Override + public CompletableFuture attach(long pid) { + return client.attach(pid).thenApply(__ -> null); + } + + @Override + public CompletableFuture attach(TypedTargetObjectRef> ref) { + client.assertMine(TargetObjectRef.class, ref); + // NOTE: These can change at any time. Just use the path to derive the target PID + if (!Objects.equals(PathUtils.parent(ref.getPath()), attachable.getPath())) { + throw new DebuggerModelTypeException( + "Target of attach must be a child of " + attachable.getPath()); + } + long pid; + try { + pid = Long.parseLong(ref.getIndex()); + } + catch (IllegalArgumentException e) { + throw new DebuggerModelNoSuchPathException("Badly-formatted PID", e); + } + return client.attach(pid).thenApply(__ -> null); + } + + @Override + public CompletableFuture execute(String cmd) { + return executeCapture(cmd).thenAccept((out) -> { + // TODO/HACK: This (re)direction should be done by the agent, not the client + // TODO: Need a flag in EXEC command indicating capture + listeners.fire(TargetInterpreterListener.class) + .consoleOutput(this, Channel.STDOUT, out); + }).exceptionally(e -> { + // TODO: Again, non-captured error output should be sent via Aevent by agent + listeners.fire(TargetInterpreterListener.class) + .consoleOutput(this, Channel.STDERR, e.getMessage() + "\n"); + return ExceptionUtils.rethrow(e); + }); + } + + @Override + public CompletableFuture executeCapture(String cmd) { + return client.executeCapture(cmd); + } + + @Override + public CompletableFuture launch(List args) { + return client.launch(args).thenApply(__ -> null); + } + + @Override + public CompletableFuture requestFocus(TargetObjectRef ref) { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + getModel().fetchModelObject(ref.getPath()).handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + if (!(obj instanceof SctlTargetThread)) { + throw new DebuggerModelTypeException("Can only focus threads"); + } + SctlTargetThread thread = (SctlTargetThread) obj; + thread.process.client.focusThread(thread.ctlid).handle(seq::exit); + }).finish(); + } + + protected void fireConsoleOutput(Channel channel, String out) { + listeners.fire(TargetInterpreterListener.class).consoleOutput(this, channel, out); + } + + protected void fireFocused(TargetObject focused) { + changeAttributes(List.of(), Map.of( // + FOCUS_ATTRIBUTE_NAME, focused // + ), "Focus changed"); + listeners.fire(TargetFocusScopeListener.class).focusChanged(this, focused); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetStructDataType.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetStructDataType.java new file mode 100644 index 0000000000..bd010992ce --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetStructDataType.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.sctl.protocol.types.*; + +public class SctlTargetStructDataType + extends SctlTargetCompositeDataType { + + public static class Ref extends AbstractRef { + protected final SctlStructTypeName nStruct; + + public Ref(SctlTargetDataTypeNamespace types, SctlStructTypeName nStruct) { + super(types, nStruct); + this.nStruct = nStruct; + } + + @Override + protected CompletableFuture doGet() { + return AsyncUtils.sequence(TypeSpec.cls(SctlTargetStructDataType.class)).then(seq -> { + types.getTypeDef(nStruct).handle(seq::next); + }, TypeSpec.cls(SelSctlTypeDefinition.class)).then((def, seq) -> { + SctlStructTypeDefinition dStruct = (SctlStructTypeDefinition) def.sel; + SctlTargetStructDataType tStruct = + new SctlTargetStructDataType(types, nStruct, dStruct); + tStruct.collectFields().thenApply(__ -> tStruct).handle(seq::next); + }, TypeSpec.cls(SctlTargetStructDataType.class)).then((tStruct, seq) -> { + types.changeElements(List.of(), List.of(tStruct), "Fetched"); + seq.exit(tStruct); + }).finish(); + } + } + + protected SctlTargetStructDataType(SctlTargetDataTypeNamespace types, + SctlStructTypeName nStruct, SctlStructTypeDefinition dStruct) { + super(types, nStruct, dStruct, NamedDataTypeKind.STRUCT, "StructType"); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSymbol.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSymbol.java new file mode 100644 index 0000000000..bd02b65158 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSymbol.java @@ -0,0 +1,118 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.attributes.TargetDataType; +import ghidra.dbg.sctl.client.depr.DebuggerAddressMapper; +import ghidra.dbg.sctl.protocol.common.SctlSymbol; +import ghidra.dbg.sctl.protocol.consts.Stype; +import ghidra.dbg.sctl.protocol.types.SelSctlTypeName; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetSymbol; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; + +/** + * A symbol retrieved by the SCTL client + * + * Implementation note: The type of the symbol is lazily requested. + */ +public class SctlTargetSymbol extends DefaultTargetObject + implements TargetSymbol { + + protected static String keySymbol(SctlSymbol sym) { + return PathUtils.makeKey(indexSymbol(sym)); + } + + protected static String indexSymbol(SctlSymbol sym) { + return sym.name.str; + } + + protected final SctlClient client; + + protected final SctlTargetModule module; + protected final BitmaskSet flags; + protected final SelSctlTypeName tname; + protected TargetDataType dataType; + protected final Address value; + protected final long size; + + /** + * Construct a new symbol description + * + * @param module the namespace containing the symbol + * @param sym the symbol description from the protocol message + * @param mapper the client's address mapper + */ + public SctlTargetSymbol(SctlTargetSymbolNamespace symbols, SctlTargetModule module, + SctlSymbol sym, DebuggerAddressMapper mapper) { + super(symbols.client, symbols, keySymbol(sym), "Symbol"); + this.client = symbols.client; + + this.module = module; + this.flags = sym.flags; + this.tname = sym.tname; + if (isConstant()) { + this.value = mapper.getAddressFactory().getConstantAddress(sym.val); + } + else { + this.value = mapper.mapOffsetToAddress(sym.val); + } + this.size = sym.size; + + changeAttributes(List.of(), Map.of( + VALUE_ATTRIBUTE_NAME, value, + DATA_TYPE_ATTRIBUTE_NAME, dataType, + SIZE_ATTRIBUTE_NAME, size // + ), "Initialized"); + } + + protected CompletableFuture init() { + return module.types.getType(tname.sel).thenApply(t -> { + dataType = t; + changeAttributes(List.of(), Map.of( // + DATA_TYPE_ATTRIBUTE_NAME, t // + ), "Initialized"); + return this; + }); + } + + @Override + public TargetDataType getDataType() { + return dataType; + } + + @Override + public boolean isConstant() { + return flags.contains(Stype.Senum); + } + + @Override + public Address getValue() { + return value; + } + + @Override + public long getSize() { + return size; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSymbolNamespace.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSymbolNamespace.java new file mode 100644 index 0000000000..f9e0ab61b5 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetSymbolNamespace.java @@ -0,0 +1,139 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import static ghidra.async.AsyncUtils.sequence; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.*; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.sctl.protocol.SctlPacket; +import ghidra.dbg.sctl.protocol.common.SctlSymbol; +import ghidra.dbg.sctl.protocol.common.reply.SctlEnumerateSymbolsReply; +import ghidra.dbg.sctl.protocol.common.reply.SctlLookupSymbolReply; +import ghidra.dbg.sctl.protocol.common.request.SctlEnumerateSymbolsRequest; +import ghidra.dbg.sctl.protocol.common.request.SctlLookupSymbolRequest; +import ghidra.dbg.target.TargetSymbolNamespace; + +public class SctlTargetSymbolNamespace + extends DefaultTargetObject + implements TargetSymbolNamespace { + + protected final SctlClient client; + + private final AsyncLazyMap lazySymbols = + new AsyncLazyMap<>(new HashMap<>(), this::doGetSymbol); + private final AsyncLazyValue> allSymbols = + new AsyncLazyValue<>(this::doGetAllSymbols); + + public SctlTargetSymbolNamespace(SctlTargetModule module) { + super(module.client, module, "Symbols", "SymbolNamespace"); + this.client = module.client; + } + + public CompletableFuture getSymbol(String name) { + return lazySymbols.get(name); + } + + protected CompletableFuture doGetSymbol(String name) { + SctlLookupSymbolRequest req = new SctlLookupSymbolRequest(parent.nsid, name); + return sequence(TypeSpec.cls(SctlTargetSymbol.class)).then((seq) -> { + client.sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + SctlTargetSymbol sym = finishGetSymbol(req, reply); + sym.init().handle(seq::exit); + }).finish(); + } + + protected CompletableFuture processBusGetSymbol(int tag, + SctlLookupSymbolRequest req) { + CompletableFuture promise = lazySymbols.put(req.name.str); + return sequence(TypeSpec.cls(SctlTargetSymbol.class)).then((seq) -> { + client.recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + SctlTargetSymbol sym = finishGetSymbol(req, reply); + sym.init().handle(seq::next); + }, TypeSpec.cls(SctlTargetSymbol.class)).then((sym, seq) -> { + promise.complete(sym); + seq.exit(sym); + }).finish(); + } + + protected SctlTargetSymbol finishGetSymbol(SctlLookupSymbolRequest req, SctlPacket reply) { + SctlLookupSymbolReply lookedup = + SctlClient.checkReply(req, SctlLookupSymbolReply.class, reply); + SctlTargetSymbol sym = new SctlTargetSymbol(this, parent, lookedup.sym, client.addrMapper); + changeElements(List.of(), List.of(sym), "Fetched"); + return sym; + } + + /* + * Methods for Tenumsym + */ + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (refresh) { + allSymbols.forget(); + lazySymbols.clear(); + } + return allSymbols.request().thenApply(__ -> null); + } + + @Override + public CompletableFuture fetchElement(String index) { + return lazySymbols.get(index); + } + + private CompletableFuture> doGetAllSymbols() { + SctlEnumerateSymbolsRequest req = new SctlEnumerateSymbolsRequest(parent.nsid); + return sequence(TypeSpec.map(String.class, SctlTargetSymbol.class)).then((seq) -> { + client.sendCommand(req).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + seq.exit(finishGetAllSymbols(req, reply)); + }).finish(); + } + + CompletableFuture> processBusGetAllSymbols(int tag, + SctlEnumerateSymbolsRequest req) { + CompletableFuture> promise = allSymbols.provide(); + return sequence(TypeSpec.map(String.class, SctlTargetSymbol.class)).then((seq) -> { + client.recvTag(tag).handle(seq::next); + }, TypeSpec.cls(SctlPacket.class)).then((reply, seq) -> { + Map syms = finishGetAllSymbols(req, reply); + promise.complete(syms); + seq.exit(syms); + }).finish(); + } + + Map finishGetAllSymbols(SctlEnumerateSymbolsRequest req, + SctlPacket reply) { + SctlEnumerateSymbolsReply enumed = + SctlClient.checkReply(req, SctlEnumerateSymbolsReply.class, reply); + synchronized (lazySymbols) { + for (SctlSymbol sym : enumed.syms) { + SctlTargetSymbol symbol = + new SctlTargetSymbol(this, parent, sym, client.addrMapper); + lazySymbols.put(sym.name.str, symbol); + } + } + Map result = lazySymbols.getCompletedMap(); + changeElements(List.of(), result.values(), "Fetched"); + return result; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetThread.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetThread.java new file mode 100644 index 0000000000..ce426e3530 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetThread.java @@ -0,0 +1,278 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.sctl.protocol.consts.Mkind; +import ghidra.dbg.target.*; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; +import ghidra.util.Msg; + +/** + * A target thread on the SCTL server + */ +public class SctlTargetThread extends DefaultTargetObject + implements TargetThread, TargetDetachable, + TargetExecutionStateful, TargetInterruptible, + TargetKillable, TargetResumable, + TargetSteppable { + private static final String EXIT_STATUS_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "exit_status"; + + protected static String keyThread(long ctlid) { + return PathUtils.makeKey(indexThread(ctlid)); + } + + protected static String indexThread(long ctlid) { + return PathUtils.makeIndex(ctlid); + } + + protected final SctlClient client; + + protected final SctlTargetProcess process; + protected final long ctlid; + + private TargetExecutionState state = TargetExecutionState.STOPPED; + + protected final SctlTargetBreakpointContainer breakpoints; + protected final SctlTargetRegisters registers; + + /** + * Construct a thread proxy + * + * @see SctlTargetProcess#createThread(long, TargetThreadDisposition) + * @param process the process owning the thread + * @param ctlid the SCTL-assigned CTLID "control ID" + * @param disposition an explanation of the thread's existence + */ + SctlTargetThread(SctlTargetThreadContainer threads, SctlTargetProcess process, long ctlid) { + super(threads.client, threads, keyThread(ctlid), "Thread"); + this.client = threads.client; + this.process = process; + this.ctlid = ctlid; + + this.breakpoints = new SctlTargetBreakpointContainer(this); + this.registers = new SctlTargetRegisters(this); + + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, state, // + breakpoints.getName(), breakpoints, // + registers.getName(), registers // + ), "Initialized"); + } + + protected void checkValid() { + if (!valid) { + throw new IllegalStateException( + "This thread handle is no longer valid, i.e., the thread has been destroyed."); + } + } + + @Override + public TargetExecutionState getExecutionState() { + return state; + } + + /** + * Change the thread's state, invoking thread and/or process listeners + * + * @param state the new state + */ + protected void setState(TargetExecutionState state) { + process.setThreadState(this, state); + } + + protected boolean isRunning() { + return state == TargetExecutionState.RUNNING; + } + + /** + * Create a breakpoint proxy + * + * This does not generate a {@link Mkind#Tsettrap} command. This only creates the proxy and + * stores it in the thread's container. + * + * This is preferred to calling + * {@link SctlBreakpoint#SctlBreakpoint(SctlTargetThread, long, long)} directly, since this will + * add the breakpoint to the thread's container. + * + * @param trpid the SCTL-assigned TRPID "trap ID" + * @param addr the address of the trap + * @return the new breakpoint + */ + protected SctlTargetBreakpoint createBreakpoint(long trpid, long addr) { + SctlTargetBreakpoint bpt = new SctlTargetBreakpoint(breakpoints, this, trpid, addr); + breakpoints.put(trpid, bpt); + return bpt; + } + + /** + * Destroy a breakpoint proxy + * + * This simply removes the specified proxy from the thread's container and sets the breakpoint's + * state to cleared. This does not generate a {@code Tclrtrap} command. It is merely a tracking + * mechanism. + * + * @param trpid the SCTL-assigned TRPID "trap ID" + * @return the removed breakpoint + */ + protected SctlTargetBreakpoint destroyBreakpoint(long trpid) { + SctlTargetBreakpoint bkpt = breakpoints.removeByTrpid(trpid); + if (bkpt == null) { + Msg.warn(this, "No such SCTL trap: " + trpid); + return null; + } + bkpt.cleared = true; + return bkpt; + } + + /** + * Copy all breakpoints from a given thread's container into this one's container + * + * @param src the thread whose breakpoints to copy + */ + protected void copyBreakpointsFrom(SctlTargetThread src) { + Map copied = new LinkedHashMap<>(); + synchronized (src.breakpoints) { + for (SctlTargetBreakpoint b : src.breakpoints.getAll()) { + SctlTargetBreakpoint c = new SctlTargetBreakpoint(b, breakpoints); + copied.put(c.trpid, c); + } + } + breakpoints.putAll(copied); + } + + protected void setExecutionStateInternal(TargetExecutionState state) { + this.state = state; + if (state == TargetExecutionState.STOPPED) { + registers.invalidateCtx(); + } + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, state // + ), "State changed"); + listeners.fire(TargetExecutionStateListener.class).executionStateChanged(this, state); + } + + @Override + public CompletableFuture resume() { + checkValid(); + return process.client.resume(ctlid); + } + + @Override + public CompletableFuture step(TargetStepKind kind) { + if (kind != TargetStepKind.INTO) { + throw new UnsupportedOperationException("step kind = " + kind); + } + checkValid(); + return process.client.step(ctlid); + } + + // SCTL-only + /** + * Snapshot a thread + * + * Creates an exact copy of this thread in a new process. It is unclear what happens if there + * are other threads in the source process. See {@code Tsnap} in the SCTL documentation. + * + * @return a proxy to the new copy + */ + public CompletableFuture snap() { + checkValid(); + return process.client.snap(ctlid); + } + + @Override + public CompletableFuture interrupt() { + checkValid(); + return process.client.interrupt(ctlid); + } + + protected CompletableFuture setBreakpoint(Address address) { + checkValid(); + return process.client.setTrap(ctlid, address, 1, false, false, false); + } + + @Override + public CompletableFuture detach() { + checkValid(); + return process.client.detachThread(ctlid); + } + + @Override + public CompletableFuture kill() { + checkValid(); + return process.client.killThread(ctlid); + } + + // SCTL-only + /** + * Select which events cause a thread to stop + * + * This allows trapping on events other than breakpoints. See {@code Ttrace} in the SCTL + * documentation. + * + * @param mode indicates whether to enable or disable the given events + * @param events the events to enable or disable + * @return a future which completes when the request is confirmed + */ + public CompletableFuture traceEvents(SctlTrace.Mode mode, Set events) { + checkValid(); + return process.client.traceEvents(ctlid, mode, events); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(""); + return sb.toString(); + } + + @Override + public String getDisplay() { + if (!valid) { + return "Thread INVALID CTL"; + } + return "Thread CTL " + ctlid + " (" + state + ")"; + } + + /** + * Destroy this thread + */ + protected void destroy(String reason) { + process.destroyThread(ctlid, reason); + } + + protected void setExitStatusCode(long status) { + // NOTE: I hope the user has a listener. Otherwise, it's gone in an instant. + changeAttributes(List.of(), Map.of( // + EXIT_STATUS_ATTRIBUTE_NAME, status // + ), "Exited"); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetThreadContainer.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetThreadContainer.java new file mode 100644 index 0000000000..8ee4993803 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetThreadContainer.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.*; +import java.util.stream.Collectors; + +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.util.PathUtils; + +public class SctlTargetThreadContainer + extends DefaultTargetObject { + + protected final SctlClient client; + + private final Map threadsByCtlid = new LinkedHashMap<>(); + + public SctlTargetThreadContainer(SctlTargetProcess process) { + super(process.client, process, "Threads", "ThreadContainer"); + this.client = process.client; + } + + protected void put(long ctlid, SctlTargetThread newThread, String reason) { + synchronized (this) { + threadsByCtlid.put(ctlid, newThread); + client.session.processes.putThread(ctlid, newThread); + } + changeElements(List.of(), List.of(newThread), reason); + } + + protected synchronized SctlTargetThread getByCtlid(long ctlid) { + return threadsByCtlid.get(ctlid); + } + + protected SctlTargetThread removeByCtlid(long ctlid, String reason) { + SctlTargetThread removed; + synchronized (this) { + removed = threadsByCtlid.remove(ctlid); + client.session.processes.removeThread(ctlid); + } + if (removed != null) { + changeElements(List.of(PathUtils.makeIndex(ctlid)), List.of(), reason); + } + return removed; + } + + protected void removeOthers(SctlTargetThread but, String reason) { + Set others = threadsByCtlid.keySet() + .stream() + .map(PathUtils::makeIndex) + .filter(i -> i != but.getIndex()) + .collect(Collectors.toSet()); + changeElements(others, List.of(), reason); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetTypedefDataType.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetTypedefDataType.java new file mode 100644 index 0000000000..6b6011af33 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetTypedefDataType.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.sctl.protocol.types.*; + +public class SctlTargetTypedefDataType + extends SctlTargetNamedDataType { + + public static class Ref extends AbstractRef { + protected final SctlTypedefTypeName nTypedef; + + public Ref(SctlTargetDataTypeNamespace types, SctlTypedefTypeName nTypedef) { + super(types, nTypedef); + this.nTypedef = nTypedef; + } + + @Override + protected CompletableFuture doGet() { + return AsyncUtils.sequence(TypeSpec.cls(SctlTargetTypedefDataType.class)).then(seq -> { + types.getTypeDef(nTypedef).handle(seq::next); + }, TypeSpec.cls(SelSctlTypeDefinition.class)).then((def, seq) -> { + SctlTypedefTypeDefinition dTypedef = (SctlTypedefTypeDefinition) def.sel; + SctlTargetTypedefDataType tTypedef = + new SctlTargetTypedefDataType(types, nTypedef, dTypedef); + tTypedef.collectDef().thenApply(__ -> tTypedef).handle(seq::next); + }, TypeSpec.cls(SctlTargetTypedefDataType.class)).then((tTypedef, seq) -> { + types.changeElements(List.of(), List.of(tTypedef), "Fetched"); + seq.exit(tTypedef); + }).finish(); + } + } + + // TODO: Dispose of this once converted? + protected final AbstractSctlTypeName nDef; + + public SctlTargetTypedefDataType(SctlTargetDataTypeNamespace types, SctlTypedefTypeName tname, + SctlTypedefTypeDefinition dTypedef) { + super(types, tname, NamedDataTypeKind.TYPEDEF, "TypedefType"); + this.nDef = dTypedef.tname.sel; + } + + protected CompletableFuture collectDef() { + return parent.getType(nDef).thenAccept(tDef -> { + SctlTargetTypedefDef def = new SctlTargetTypedefDef(this, tDef); + changeElements(List.of(), List.of(def), "Initialized"); + }); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetTypedefDef.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetTypedefDef.java new file mode 100644 index 0000000000..de6bec578e --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetTypedefDef.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import ghidra.dbg.attributes.TargetDataType; + +public class SctlTargetTypedefDef extends SctlTargetDataTypeMember { + public SctlTargetTypedefDef(SctlTargetTypedefDataType typedef, TargetDataType dataType) { + super(typedef, "[def]", -1, -1, "def", dataType, "TypedefDef"); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetUnionDataType.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetUnionDataType.java new file mode 100644 index 0000000000..f6fa9f50d8 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTargetUnionDataType.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.sctl.protocol.types.*; + +public class SctlTargetUnionDataType extends SctlTargetCompositeDataType { + + public static class Ref extends AbstractRef { + protected final SctlUnionTypeName nUnion; + + public Ref(SctlTargetDataTypeNamespace types, SctlUnionTypeName nUnion) { + super(types, nUnion); + this.nUnion = nUnion; + } + + @Override + protected CompletableFuture doGet() { + return AsyncUtils.sequence(TypeSpec.cls(SctlTargetUnionDataType.class)).then(seq -> { + types.getTypeDef(nUnion).handle(seq::next); + }, TypeSpec.cls(SelSctlTypeDefinition.class)).then((def, seq) -> { + SctlUnionTypeDefinition dUnion = (SctlUnionTypeDefinition) def.sel; + SctlTargetUnionDataType tStruct = + new SctlTargetUnionDataType(types, nUnion, dUnion); + tStruct.collectFields().thenApply(__ -> tStruct).handle(seq::next); + }, TypeSpec.cls(SctlTargetUnionDataType.class)).then((tUnion, seq) -> { + types.changeElements(List.of(), List.of(tUnion), "Fetched"); + seq.exit(tUnion); + }).finish(); + } + } + + protected SctlTargetUnionDataType(SctlTargetDataTypeNamespace types, SctlUnionTypeName nUnion, + SctlUnionTypeDefinition dUnion) { + super(types, nUnion, dUnion, NamedDataTypeKind.UNION, "UnionType"); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTcpDebuggerModelFactory.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTcpDebuggerModelFactory.java new file mode 100644 index 0000000000..95eda870d0 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTcpDebuggerModelFactory.java @@ -0,0 +1,76 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.DebuggerModelFactory; +import ghidra.dbg.util.ConfigurableFactory.FactoryDescription; + +@FactoryDescription( // + brief = "SCTL connection over TCP", // + htmlDetails = "Connect to an optionally remote agent via SCTL/TCP." // +) +public class SctlTcpDebuggerModelFactory implements DebuggerModelFactory { + + private String host = "localhost"; + @FactoryOption("Agent network address") + public final Property agentAddressOption = + Property.fromAccessors(String.class, this::getAgentAddress, this::setAgentAddress); + + private int port = 12345; + @FactoryOption("Agent TCP port") + public final Property agentPortOption = + Property.fromAccessors(Integer.class, this::getAgentPort, this::setAgentPort); + + @Override + public CompletableFuture build() { + try { + AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(); + return AsyncUtils.sequence(TypeSpec.cls(SctlClient.class)).then(seq -> { + AsyncUtils.completable(TypeSpec.VOID, channel::connect, + new InetSocketAddress(host, port)).handle(seq::next); + }).then(seq -> { + SctlClient client = new SctlClient(host + ":" + port, channel); + client.connect().thenApply(__ -> client).handle(seq::exit); + }).finish(); + } + catch (IOException e) { + return CompletableFuture.failedFuture(e); + } + } + + public String getAgentAddress() { + return host; + } + + public void setAgentAddress(String host) { + this.host = host; + } + + public int getAgentPort() { + return port; + } + + public void setAgentPort(int port) { + this.port = port; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTrace.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTrace.java new file mode 100644 index 0000000000..0d35a02bdc --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/SctlTrace.java @@ -0,0 +1,118 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import java.util.Set; + +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.sctl.protocol.consts.Evkind; + +/** + * Utilities for calling {@link SctlTargetThread#traceEvents(Mode, Set)} + */ +public interface SctlTrace { + /** + * Constants specifying "enable" or "disable" + */ + public enum Mode { + /** + * Disable events + */ + CLEAR(Evkind.Eclear), + /** + * Enable events + */ + SET(Evkind.Eset); + + public final Evkind kind; + + Mode(Evkind kind) { + this.kind = kind; + } + } + + /** + * Events to select from + */ + public enum Event { + /** + * Immediately preceding and following system calls + */ + SYSCALL(Evkind.Esyscall), + /** + * Immediately following POSIX {@code exec()} + */ + EXEC(Evkind.Eexec), + /** + * Immediately following POSIX {@code fork()} + */ + FORK(Evkind.Efork), + /** + * Immediately following Linux (@code clone()} + */ + CLONE(Evkind.Eclone), + /** + * Pending a signal + */ + SIGNAL(Evkind.Esignal), + /** + * Upon exit + */ + EXIT(Evkind.Eexit), + /** + * Immediately upon a trap point + */ + TRAP(Evkind.Etrap), + /** + * Immediately upon a snap point + */ + SNAP(Evkind.Esnap), + /** + * Enable context transfer after a step + */ + STEPCTX(Evkind.Estepctx), + /** + * Immediately following a module load + */ + LOAD(Evkind.Eload), + /** + * Immediately following a module unload + */ + UNLOAD(Evkind.Eunload); + + public final Evkind kind; + + Event(Evkind kind) { + this.kind = kind; + } + } + + /** + * Convert mode and event selections to protocol flags + * + * @param mode whether to enable or disable the events + * @param events the events to enable to disable + * @return the set of flags + */ + public static BitmaskSet toFlags(Mode mode, Set events) { + BitmaskSet flags = BitmaskSet.of(); + flags.add(mode.kind); + for (Event ev : events) { + flags.add(ev.kind); + } + return flags; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/depr/DebuggerAddressMapper.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/depr/DebuggerAddressMapper.java new file mode 100644 index 0000000000..d5ae6355c0 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/depr/DebuggerAddressMapper.java @@ -0,0 +1,54 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client.depr; + +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressFactory; + +/** + * A bijection of debugger addresses to offsets + * + *

+ * Some debuggers and/or debugging protocols map multiple address spaces into a single offset space. + * This interface provides the conversion functions between the two. The offset is typically used to + * communicate to the debugger. The address objects are used by Ghidra identify a location within + * the target's memory model. + */ +public interface DebuggerAddressMapper { + + /** + * Convert an offset to an address + * + * @param offset the offset + * @return the address + */ + public Address mapOffsetToAddress(long offset); + + /** + * Convert an address to an offset + * + * @param address the address + * @return the offset + */ + public long mapAddressToOffset(Address address); + + /** + * Get the factory to create addresses in the target's memory model + * + * @return the factory + */ + public AddressFactory getAddressFactory(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/depr/DefaultDebuggerAddressMapper.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/depr/DefaultDebuggerAddressMapper.java new file mode 100644 index 0000000000..2ce7d7e738 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/depr/DefaultDebuggerAddressMapper.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client.depr; + +import ghidra.program.model.address.*; + +/** + * A singleton default implementation of {@link DebuggerAddressMapper} + * + * This implements the minimum required to provide an address factory, which simply maps the entire + * offset space into single address space ("RAM") on the target. + */ +public class DefaultDebuggerAddressMapper implements DebuggerAddressMapper { + /** + * The singleton instance + */ + public static final DefaultDebuggerAddressMapper INSTANCE = new DefaultDebuggerAddressMapper(); + + protected final AddressSpace constant = + new GenericAddressSpace("const", 64, AddressSpace.TYPE_CONSTANT, 0); + protected final AddressSpace ram = + new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 1); + protected final AddressFactory factory = + new DefaultAddressFactory(new AddressSpace[] { ram, constant }); + + protected DefaultDebuggerAddressMapper() { + } + + @Override + public Address mapOffsetToAddress(long offset) { + return ram.getAddress(offset); + } + + @Override + public long mapAddressToOffset(Address address) { + return address.getOffset(); + } + + @Override + public AddressFactory getAddressFactory() { + return factory; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectPingResponse.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectPingResponse.java new file mode 100644 index 0000000000..7cecc03ee0 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectPingResponse.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client.err; + +import ghidra.util.NumericUtilities; + +/** + * Thrown when a ping response does not follow the spec + * + * It should respond with a count of the bytes sent in the request. + */ +public class SctlIncorrectPingResponse extends SctlProtocolSequenceException { + public SctlIncorrectPingResponse(byte[] content, int expected, int got) { + super("SCTL server gave incorrect ping reply for '" + + NumericUtilities.convertBytesToString(content) + "'. Expected " + expected + ". Got " + + got); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectReply.java new file mode 100644 index 0000000000..c2945b654c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectReply.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client.err; + +import ghidra.dbg.sctl.protocol.SctlPacket; + +/** + * Thrown when the server responds with an unexpected type + * + * A {@code Tx} request should have an {@code Rx} or {@code Rerror} response, as correlated by the + * tag. + */ +public class SctlIncorrectReply extends SctlProtocolSequenceException { + public SctlIncorrectReply(SctlPacket cmd, SctlPacket reply) { + super("Unexpected reply for " + cmd + ": " + reply); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectVersionRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectVersionRequest.java new file mode 100644 index 0000000000..fc0b50f5fb --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectVersionRequest.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client.err; + +/** + * Throw when the client offers an invalid version + */ +public class SctlIncorrectVersionRequest extends SctlProtocolSequenceException { + public SctlIncorrectVersionRequest(String message) { + super(message); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectVersionResponse.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectVersionResponse.java new file mode 100644 index 0000000000..c8ae957a1e --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlIncorrectVersionResponse.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client.err; + +import ghidra.dbg.sctl.protocol.consts.Mkind; + +/** + * Thrown when the server selects an invalid version + * + * The {@link Mkind#Rversion} reply must select a version from those listed in the + * {@link Mkind#Tversion} request. + */ +public class SctlIncorrectVersionResponse extends SctlProtocolSequenceException { + public SctlIncorrectVersionResponse(String message) { + super(message); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlPartialWriteException.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlPartialWriteException.java new file mode 100644 index 0000000000..9f55fc83b5 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlPartialWriteException.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client.err; + +import ghidra.dbg.sctl.err.SctlRuntimeException; +import ghidra.util.NumericUtilities; + +/** + * Thrown when fewer bytes than those given are written by the server + * + * The {@code Rwrite} reply contains a field for the server to report how many bytes of data from a + * {@code Twrite} request were actually written. Ideally, all bytes are written, so this is thrown + * if fewer are reported. + */ +public class SctlPartialWriteException extends SctlRuntimeException { + public SctlPartialWriteException(byte[] data, int len) { + super("Only wrote " + len + " bytes of " + NumericUtilities.convertBytesToString(data)); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlProtocolSequenceException.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlProtocolSequenceException.java new file mode 100644 index 0000000000..b0f6344478 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/client/err/SctlProtocolSequenceException.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client.err; + +import ghidra.dbg.sctl.err.SctlRuntimeException; + +/** + * A super class for exceptions resulting from unexpected or incorrect messages + */ +public class SctlProtocolSequenceException extends SctlRuntimeException { + public SctlProtocolSequenceException(String message) { + super(message); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/HasOptionalPlatform.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/HasOptionalPlatform.java new file mode 100644 index 0000000000..36e4d45afc --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/HasOptionalPlatform.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.dialect; + +public interface HasOptionalPlatform { + /** + * Check if the format contains a platform field + * + * @return true if supported, false otherwise + */ + boolean supportsPlatform(); + + /** + * Set the platform + * + * @param platform the platform + */ + void setPlatform(String platform); + + /** + * Get the platform + * + * @return the platform + */ + String getPlatform(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/HasOptionalProcessID.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/HasOptionalProcessID.java new file mode 100644 index 0000000000..3590a9faa2 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/HasOptionalProcessID.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.dialect; + +public interface HasOptionalProcessID { + /** + * Check if the format contains a process ID field + * + * @return true if supported, false otherwise + */ + boolean supportsProcessID(); + + /** + * Set the process ID + * + * @param pid the process ID + */ + void setProcessID(long pid); + + /** + * Get the process ID + * + * @return the process ID + */ + long getProcessID(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SctlDialect.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SctlDialect.java new file mode 100644 index 0000000000..d329241aba --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SctlDialect.java @@ -0,0 +1,119 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.dialect; + +import java.lang.reflect.InvocationTargetException; +import java.util.Set; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.PacketFactory; +import ghidra.dbg.sctl.protocol.*; +import ghidra.dbg.sctl.protocol.consts.Evkind; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; + +/** + * An interface for a SCTL dialect + * + * To handle different versions and variants, SCTL supports the notion dialects. When negotiating a + * connection, the client lists its version and dialects, and the server selects from those offered. + * The client supports multiple dialects, each implementing this interface. + */ +public interface SctlDialect { + public SctlDialect NULL_DIALECT = SctlNullDialect.INSTANCE; + + /** + * Get the system version string to offer in version negotiation + * + * @return the system version string + */ + public String getSysVersion(); + + /** + * Get the full version string to offer in version negotiation + * + * @return the full version string + */ + public default String getFullVersion() { + return SctlVersionInfo.SCTL_VERSION + ":" + getSysVersion(); + } + + /** + * Get the packet factory that provides dialect-specific packet formats + * + * @return the packet factory for this dialect + */ + public PacketFactory getPacketFactory(); + + /** + * Check whether this dialect is aware of other clients using the same server + * + * @return true if supported, false otherwise + */ + public boolean isBusSupported(); + + /** + * Check whether this dialect supports register selection + * + * @return true if supported, false otherwise + */ + public boolean isRegisterSelectionSupported(); + + /** + * Get the state of a CTL (thread) after an event is reported + * + * @param events the flags describing the event + * @return the state of the CTL + */ + public TargetExecutionState stateAfterEvent(Set events); + + /** + * If the dialect supports only one platform, get that platform + * + * @return the platform + * @throws UnsupportedOperationException if the dialect supports multiple platforms + */ + public String getSolePlatform(); + + /** + * Create the root packet type for this dialect + * + * @param tag the tag + * @param cmd the command + * @return the packet + */ + default public AbstractSelSctlPacket createSel(int tag, SctlPacket cmd) { + AbstractSelSctlPacket pkt = create(AbstractSelSctlPacket.class); + pkt.tag = tag; + pkt.sel = cmd; + return pkt; + } + + /** + * A convenience for invoking the dialect's packet factory + * + * @param cls the packet class to instantiate + * @return the new packet + */ + default public T create(Class cls) { + try { + return getPacketFactory().newPacket(cls); + } + catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | NoSuchMethodException | SecurityException e) { + throw new AssertionError(e); + } + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SctlNullDialect.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SctlNullDialect.java new file mode 100644 index 0000000000..87338da9c4 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SctlNullDialect.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.dialect; + +import java.util.Set; + +import ghidra.comm.packet.AbstractPacketFactory; +import ghidra.comm.packet.PacketFactory; +import ghidra.dbg.sctl.protocol.AbstractSelSctlPacket; +import ghidra.dbg.sctl.protocol.consts.Evkind; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; + +public enum SctlNullDialect implements SctlDialect { + INSTANCE; + + public static class SctlNullPacketFactory extends AbstractPacketFactory { + { + useFor(AbstractSelSctlPacket.class, SelSctlNullDialectPacket.class); + } + } + + public static final String SYS_VERSION = "(negotiating)"; + private static final PacketFactory FACTORY = new SctlNullPacketFactory(); + + @Override + public String getSysVersion() { + return SYS_VERSION; + } + + @Override + public PacketFactory getPacketFactory() { + return FACTORY; + } + + @Override + public boolean isBusSupported() { + return false; + } + + @Override + public boolean isRegisterSelectionSupported() { + throw new IllegalStateException("Dialect has not been negotiated"); + } + + @Override + public TargetExecutionState stateAfterEvent(Set events) { + throw new IllegalStateException("Dialect has not been negotiated"); + } + + @Override + public String getSolePlatform() { + throw new IllegalStateException("Dialect has not been negotiated"); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SelSctlNullDialectPacket.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SelSctlNullDialectPacket.java new file mode 100644 index 0000000000..3e248dce7a --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/dialect/SelSctlNullDialectPacket.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.dialect; + +import java.util.Map; + +import ghidra.dbg.sctl.protocol.AbstractSelSctlPacket; +import ghidra.dbg.sctl.protocol.SctlPacket; +import ghidra.dbg.sctl.protocol.common.reply.*; +import ghidra.dbg.sctl.protocol.common.request.SctlPingRequest; +import ghidra.dbg.sctl.protocol.common.request.SctlVersionRequest; +import ghidra.dbg.sctl.protocol.consts.Mkind; + +public class SelSctlNullDialectPacket extends AbstractSelSctlPacket { + public static final Map> METHOD_MAP = + typeMap(Mkind.class, SctlPacket.class) // + .put(Mkind.Rerror, SctlErrorReply.class) // + .put(Mkind.Tversion, SctlVersionRequest.class) // + .put(Mkind.Rversion, SctlVersionReply.class) // + .put(Mkind.Tping, SctlPingRequest.class) // + .put(Mkind.Rping, SctlPingReply.class) // + .build(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/err/SctlError.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/err/SctlError.java new file mode 100644 index 0000000000..1804eaafb3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/err/SctlError.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.err; + +import ghidra.dbg.sctl.protocol.common.reply.SctlErrorReply; + +/** + * An exception to describe errors detected by the SCTL server + * + * On the client side, this exception is thrown whenever an {@code Rerror} message is received. + * Usually, this results in a future completing exceptionally. + * + * For server implementations using this module, this exception can be thrown within request + * handlers. This will cause the client handler to generate an {@code Rerror} for the request that + * was being handled. + */ +public class SctlError extends SctlRuntimeException { + + /** + * Server side: Construct an exception to generate {@code Rerror} + * + * @param message a human-readable description of the error + */ + public SctlError(String message) { + super(message); + } + + /** + * Client side: Construct an exception for a received {@code Rerror} + * + * @param reply the received {@code Rerror} + */ + public SctlError(SctlErrorReply reply) { + super("Server reported error: " + reply.msg); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/err/SctlRuntimeException.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/err/SctlRuntimeException.java new file mode 100644 index 0000000000..32d8d24a94 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/err/SctlRuntimeException.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.err; + +/** + * A base exception for SCTL-related exceptions + */ +public class SctlRuntimeException extends RuntimeException { + public SctlRuntimeException(String message) { + super(message); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlNotify.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlNotify.java new file mode 100644 index 0000000000..b6c885b2c3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlNotify.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol; + +/** + * A base class for all {@code A}-type SCTL messages + * + * There is only one {@code A}-type message, so this class exists to follow the same convention as + * the {@code T}- and {@code R}-type messages. + */ +public abstract class AbstractSctlNotify extends SctlPacket { + // Empty +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlReply.java new file mode 100644 index 0000000000..2d7dc3081c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlReply.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol; + +/** + * A base class for all {@code R}-type SCTL messages + */ +public abstract class AbstractSctlReply extends SctlPacket { + // Empty +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlRequest.java new file mode 100644 index 0000000000..7778467e16 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSctlRequest.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol; + +/** + * A base class for all {@code T}-type SCTL messages + */ +public abstract class AbstractSctlRequest extends SctlPacket { + // Empty +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSelSctlPacket.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSelSctlPacket.java new file mode 100644 index 0000000000..5dcbd1d102 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/AbstractSelSctlPacket.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.annot.TypedByField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.consts.Mkind; + +public abstract class AbstractSelSctlPacket extends Packet { + /** + * For unmarshalling + */ + public AbstractSelSctlPacket() { + } + + /** + * Construct a SCTL message with the given tag and message + * + * @param tag the tag + * @param sel the message + */ + public AbstractSelSctlPacket(int tag, SctlPacket sel) { + this.tag = tag; + this.sel = sel; + } + + @PacketField + public Mkind op; + + @PacketField + public int tag; + + @PacketField + @TypedByField(by = "op", map = "METHOD_MAP") + public SctlPacket sel; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlMarshaller.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlMarshaller.java new file mode 100644 index 0000000000..1a7f5f6db2 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlMarshaller.java @@ -0,0 +1,103 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol; + +import java.nio.*; + +import ghidra.comm.packet.AbstractPacketMarshaller; +import ghidra.comm.packet.PacketCodec; +import ghidra.comm.packet.binary.ByteBufferPacketCodec; +import ghidra.comm.packet.err.PacketDecodeException; +import ghidra.comm.packet.err.PacketEncodeException; +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; + +/** + * The marshaller for SCTL messages + * + * This implements the {@code size[8]} portion of the messages, because that is used to delineate + * each message from the following message. The remaining packet fields are implemented in + * {@link SelSctlPacket} and its constituents. + * + * This only marshalls and unmarshalls {@link SelSctlPacket}s to and from {@link ByteBuffer}s. It is + * powered by the {@link ByteBufferPacketCodec}. + */ +public class SctlMarshaller + extends AbstractPacketMarshaller { + final static boolean DEBUG = false; + final static PacketCodec BB_CODEC = ByteBufferPacketCodec.getInstance(); + + /** + * Construct a SCTL packet marshaller + */ + public SctlMarshaller() { + super(BB_CODEC, AbstractSelSctlPacket.class); + } + + protected void printBuf(ByteBuffer buf) { + if (DEBUG) { + byte[] b = new byte[buf.remaining()]; + int p = buf.position(); + buf.get(b); + buf.position(p); + Msg.debug(this, NumericUtilities.convertBytesToString(b, ":")); + } + } + + @Override + public void marshall(ByteBuffer outbuf, AbstractSelSctlPacket pkt) + throws PacketEncodeException { + int pos = outbuf.position(); + try { + outbuf.order(ByteOrder.LITTLE_ENDIAN); + outbuf.putLong(0); // Placeholder + BB_CODEC.encodePacket(outbuf, pkt); + + // Backfill the length + int end = outbuf.position(); + int len = end - pos - Long.BYTES; + outbuf.putLong(pos, len); + } + catch (Exception e) { + outbuf.position(pos); + throw e; + } + } + + @Override + public R unmarshall(Class pktType, ByteBuffer inbuf) + throws PacketDecodeException { + int origPos = inbuf.position(); + int origLimit = inbuf.limit(); + try { + inbuf.order(ByteOrder.LITTLE_ENDIAN); + int len = (int) inbuf.getLong() + Long.BYTES; + if (inbuf.limit() < len) { + throw new BufferUnderflowException(); + } + inbuf.limit(len); + R rcvd = BB_CODEC.decodePacket(pktType, inbuf, factory); + return rcvd; + } + catch (Exception e) { + inbuf.position(origPos); + throw e; + } + finally { + inbuf.limit(origLimit); + } + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlPacket.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlPacket.java new file mode 100644 index 0000000000..60bb269303 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlPacket.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol; + +import ghidra.comm.packet.Packet; + +/** + * A base type for all SCTL requests, replies, and notifications + */ +public abstract class SctlPacket extends Packet { + // Empty +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlVersionInfo.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlVersionInfo.java new file mode 100644 index 0000000000..d5cd0ea2eb --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/SctlVersionInfo.java @@ -0,0 +1,149 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol; + +import java.util.*; + +import ghidra.dbg.sctl.client.err.SctlIncorrectVersionRequest; +import ghidra.dbg.sctl.client.err.SctlIncorrectVersionResponse; +import ghidra.dbg.sctl.dialect.SctlDialect; +import ghidra.dbg.sctl.protocol.common.request.SctlVersionRequest; +import ghidra.dbg.sctl.protocol.consts.Mkind; +import ghidra.dbg.sctl.protocol.v2012base.x86.linux.Sctl2012LinuxX86Dialect; +import ghidra.dbg.sctl.protocol.v2012base.x86.win.Sctl2012WindowsX86Dialect; +import ghidra.dbg.sctl.protocol.v2012ext.x86.linux.Sctl2012ExtLinuxX86Dialect; +import ghidra.dbg.sctl.protocol.v2012ext.x86.win.Sctl2012ExtWindowsX86Dialect; +import ghidra.dbg.sctl.protocol.v2018base.any.Sctl2018AnyAnyDialect; + +/** + * A static-only class for listing the versions and dialects of SCTL understood by this client + * + * This class cannot be instantiated. It only has static utility methods. + */ +public final class SctlVersionInfo { + public static final List KNOWN_DIALECTS = Arrays.asList(new SctlDialect[] { // + Sctl2018AnyAnyDialect.INSTANCE, // + Sctl2012ExtLinuxX86Dialect.INSTANCE, // + Sctl2012ExtWindowsX86Dialect.INSTANCE, // + Sctl2012LinuxX86Dialect.INSTANCE, // + Sctl2012WindowsX86Dialect.INSTANCE, // + }); + public static final Map DIALECTS_BY_NAME = makeDialectMap(); + public static final String SCTL_VERSION = "sctl-2012"; + + // Static only + private SctlVersionInfo() { + } + + /** + * Obtain the dialect to which a server agreed + * + * This method presumes that the server has replied to a {@code Tversion} request generated by + * {@link #makeRequest()}. The request lists all known dialects from which the server must + * choose one. This method checks that the response is one of the known dialects and returns an + * instance of that dialect. + * + * @param reply the reply from the server + * @return the agreed-upon dialect + * @throws SctlIncorrectVersionResponse if the selected dialect is not known + */ + public static SctlDialect agreeDialect(String selected) throws SctlIncorrectVersionResponse { + // TODO: Spec says multiple dialects may be chosen.... I only take the highest in my list + + String[] parts = selected.split(":"); + if (parts.length != 2) { + throw new SctlIncorrectVersionResponse( + "Server responded with malformed version: " + selected); + } + if (!SCTL_VERSION.equals(parts[0])) { + throw new SctlIncorrectVersionResponse( + "Server ctl version does not match offer: " + parts[0]); + } + Set versions = new HashSet<>(Arrays.asList(parts[1].split(","))); + // Take the "best", and check that it was in the offer + // Everything in KNOWN_DIALECT ought to have been in the offer + for (SctlDialect dialect : KNOWN_DIALECTS) { + if (versions.contains(dialect.getSysVersion())) { + return dialect; + } + } + throw new SctlIncorrectVersionResponse( + "Server requested unknown system dialects: " + parts[1]); + } + + private static Map makeDialectMap() { + Map map = new LinkedHashMap<>(); + for (SctlDialect dialect : KNOWN_DIALECTS) { + map.put(dialect.getSysVersion(), dialect); + } + return Collections.unmodifiableMap(map); + } + + /** + * Generate a {@link Mkind#Tversion} request listing the given dialects + * + * @param dialects the supported dialects + * @return the request + */ + public static SctlVersionRequest makeRequest(Collection dialects) { + StringBuilder sb = new StringBuilder(); + sb.append(SCTL_VERSION); + sb.append(':'); + boolean first = true; + for (SctlDialect dialect : dialects) { + if (!first) { + sb.append(','); + } + else { + first = false; + } + sb.append(dialect.getSysVersion()); + } + return new SctlVersionRequest(sb.toString()); + } + + /** + * Generate a {@link Mkind#Tversion} request listing all dialects known to the client + * + * @return the request + */ + public static SctlVersionRequest makeRequest() { + return makeRequest(KNOWN_DIALECTS); + } + + public static Set collectSupported(String offered) { + String parts[] = offered.split(":"); + if (parts.length != 2) { + throw new SctlIncorrectVersionRequest("Client offered a malformed version: " + offered); + } + if (!SCTL_VERSION.equals(parts[0])) { + throw new SctlIncorrectVersionRequest( + "Client ctl version is not supported: " + parts[0]); + } + Set supported = new LinkedHashSet<>(); + for (String version : parts[1].split(",")) { + SctlDialect dialect = DIALECTS_BY_NAME.get(version); + if (dialect != null) { + supported.add(dialect); + } + } + if (supported.isEmpty()) { + throw new SctlIncorrectVersionRequest( + "No versions offered by the client are supported: " + offered); + } + return supported; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractByLongFieldsSctlContext.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractByLongFieldsSctlContext.java new file mode 100644 index 0000000000..e7378e7fb9 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractByLongFieldsSctlContext.java @@ -0,0 +1,86 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.*; + +public abstract class AbstractByLongFieldsSctlContext extends AbstractSctlContext { + + @Override + public Map toMap() { + Map result = new LinkedHashMap<>(); + ByteBuffer buf = ByteBuffer.allocate(Long.BYTES); + buf.order(ByteOrder.BIG_ENDIAN); + for (Field fld : getClass().getFields()) { + try { + buf.putLong(0, fld.getLong(this)); + } + catch (IllegalArgumentException | IllegalAccessException e) { + throw new AssertionError(e); + } + result.put(fld.getName(), Arrays.copyOf(buf.array(), Long.BYTES)); + } + return result; + } + + @Override + public Set getRegisterNames() { + Set result = new LinkedHashSet<>(); + for (Field fld : getClass().getFields()) { + result.add(fld.getName()); + } + return result; + } + + @Override + public void updateFromMap(Map values) { + try { + Class cls = getClass(); + ByteBuffer buf = ByteBuffer.allocate(Long.BYTES); + buf.order(ByteOrder.BIG_ENDIAN); + for (Map.Entry ent : values.entrySet()) { + buf.putLong(0, 0); + buf.position(0); + byte[] val = ent.getValue(); + buf.put(val, Long.BYTES - val.length, val.length); + Field fld = cls.getField(ent.getKey()); + fld.setLong(this, buf.getLong(0)); + } + } + catch (NoSuchFieldException | SecurityException | IllegalArgumentException + | IllegalAccessException e) { + throw new AssertionError(e); + } + } + + @Override + public void update(String name, byte[] value) { + try { + Class cls = getClass(); + ByteBuffer buf = ByteBuffer.allocate(Long.BYTES); + buf.order(ByteOrder.BIG_ENDIAN); + buf.put(value, Long.BYTES - value.length, value.length); + Field fld = cls.getField(name); + fld.setLong(this, buf.getLong(0)); + } + catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { + throw new AssertionError(e); + } + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlBinary.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlBinary.java new file mode 100644 index 0000000000..a4a7e19eae --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlBinary.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import java.util.List; + +import ghidra.comm.packet.Packet; + +public abstract class AbstractSctlBinary extends Packet { + public abstract void setNamespaceID(long nsid); + + public abstract long getNamespaceID(); + + public abstract void setPath(String path); + + public abstract String getPath(); + + public abstract void setExecutable(boolean isexe); + + public abstract boolean isExecutable(); + + public abstract boolean supportsBase(); + + public abstract void setBase(long base); + + public abstract long getBase(); + + public abstract boolean supportsSections(); + + public abstract AbstractSctlSection addSection(); + + public abstract List getSections(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlContext.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlContext.java new file mode 100644 index 0000000000..d849f78aaa --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlContext.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import java.util.*; + +import ghidra.comm.packet.Packet; + +/** + * A base type for contexts + * + * This is a dialect-defined format. + * + * The default implementation assumes each register is stored in a field named after that register. + */ +public abstract class AbstractSctlContext extends Packet { + /** + * Convert this context to a register-value map as used in the Debugging API. + * + * @return the map + */ + public abstract Map toMap(); + + /** + * List the registers represented within this context + * + * @return the set of register names + */ + public abstract Set getRegisterNames(); + + /** + * Set the selected registers + * + * If the context has already been loaded with data, i.e., parsed from the channel, then this + * may re-parse the individual registers. + * + * @param regdefs the list of selected register definitions + */ + public abstract void setSelectedRegisters(List regdefs); + + /** + * Update the fields from a register-value map as used in the Debugging API. + * + * @param values the map + */ + public abstract void updateFromMap(Map values); + + /** + * Update a field by name using bytes in big-endian order + * + * @param name the register name + * @param value the register value + */ + public abstract void update(String name, byte[] value); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlObjectEntry.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlObjectEntry.java new file mode 100644 index 0000000000..e62e241d8e --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlObjectEntry.java @@ -0,0 +1,96 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import ghidra.comm.packet.Packet; + +/** + * An attachable target process entry + * + * This is a dialect-defined format. + */ +public abstract class AbstractSctlObjectEntry extends Packet { + /** + * Get the object path + * + * @return path the object path + */ + public abstract SctlString getPath(); + + /** + * Set the object path + * + * @param path the object path + */ + public abstract void setPath(SctlString path); + + /** + * Get the object path + * + * @return path the object path + */ + public abstract SctlString getKey(); + + /** + * Set the object path + * + * @param path the object path + */ + public abstract void setKey(SctlString key); + + /** + * Get the object kind + * + * @return path the object kind + */ + public abstract SctlString getKind(); + + /** + * Set the object kind + * + * @param path the object kind + */ + public abstract void setKind(SctlString kind); + + /** + * Get the object value + * + * @return path the object value + */ + public abstract SctlString getValue(); + + /** + * Set the object value if it exists + * + * @param path the object value + */ + public abstract void setValue(SctlString value); + + /** + * Get the object type + * + * @return path the object type + */ + public abstract SctlString getType(); + + /** + * Set the object type if it exists + * + * @param path the object type + */ + public abstract void setType(SctlString sctlString); + +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlProcessEntry.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlProcessEntry.java new file mode 100644 index 0000000000..9904f7101c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlProcessEntry.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import java.util.List; + +import ghidra.comm.packet.Packet; + +/** + * An attachable target process entry + * + * This is a dialect-defined format. + */ +public abstract class AbstractSctlProcessEntry extends Packet { + /** + * Get the process ID + * + * @return the process ID + */ + public abstract long getProcessID(); + + /** + * Set the process ID + * + * @param pid the process ID + */ + public abstract void setProcessID(long pid); + + /** + * Get the command that launched the process + * + * Alternatively, this may be any textual description of the process + * + * @return the command + */ + public abstract String getCommand(); + + /** + * Set the command that launched the process + * + * Alternatively, this may be any textual description of the process + * + * @param cmd the command + */ + public abstract void setCommand(String cmd); + + /** + * Get the threads in the process + * + * @return a list of thread entries + */ + public abstract List getThreads(); + + /** + * Add a thread to the thread list + * + * @return the new, empty thread entry + */ + public abstract AbstractSctlThreadEntry addThread(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlProcessList.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlProcessList.java new file mode 100644 index 0000000000..bb10cc7f67 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlProcessList.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import java.util.List; + +import ghidra.comm.packet.Packet; + +/** + * A list of attachable target processes + * + * This is a dialect-defined format. + */ +public abstract class AbstractSctlProcessList extends Packet { + /** + * Get the list of attachable target processes + * + * @return the list of process entries + */ + public abstract List getProcesses(); + + /** + * Add a new process to the list + * + * @return the new, empty process entry + */ + public abstract AbstractSctlProcessEntry addProcess(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlRegion.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlRegion.java new file mode 100644 index 0000000000..7044d17773 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlRegion.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import ghidra.comm.packet.Packet; +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.sctl.client.SctlMemoryProtection; + +public abstract class AbstractSctlRegion extends Packet { + /** + * Set the name of the region + * + * This may be a file path describing the module loaded here or any textual description of the + * region. + * + * @param name the name + */ + public abstract void setName(String name); + + /** + * Get the name of the region + * + * This may be a file path describing the module loaded here or any textual description of the + * region. + * + * @return the name + */ + public abstract String getName(); + + /** + * Set the start address of the region + * + * @param address the address + */ + public abstract void setAddress(long address); + + /** + * Get the start address of the region + * + * @return + */ + public abstract long getAddress(); + + /** + * Set the length, in bytes, of the region + * + * @param length the length + */ + public abstract void setLength(long length); + + /** + * Get the length, in bytes, of the region + * + * @return the length + */ + public abstract long getLength(); + + /** + * Set the region's protections (permissions) + * + * @param flags the flags enumerating the protections + */ + public abstract void setProtections(BitmaskSet flags); + + /** + * Get the region's protections (permissions) + * + * @return the flags enumerating the protections + */ + public abstract BitmaskSet getProtections(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlSection.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlSection.java new file mode 100644 index 0000000000..609fa096c7 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlSection.java @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import ghidra.comm.packet.Packet; + +public abstract class AbstractSctlSection extends Packet { + /** + * Set the starting address of the section as loaded + * + * @param address the address + */ + public abstract void setAddress(long address); + + /** + * Get the starting address of the section as loaded + * + * @return the address + */ + public abstract long getAddress(); + + /** + * Set the length, in bytes, of the section + * + * @param length the length + */ + public abstract void setLength(long length); + + /** + * Get the length, in bytes, of the section + * + * @return + */ + public abstract long getLength(); + + /** + * Set the name of the section + * + * @param name the name + */ + public abstract void setName(String name); + + /** + * Get the name of the section + * + * @return the name + */ + public abstract String getName(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlStatus.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlStatus.java new file mode 100644 index 0000000000..b39c2f5e03 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlStatus.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import java.util.List; + +import ghidra.comm.packet.Packet; +import ghidra.dbg.sctl.dialect.HasOptionalProcessID; + +/** + * The status of a CTL + * + * This is a dialect-defined format. + */ +public abstract class AbstractSctlStatus extends Packet implements HasOptionalProcessID { + /** + * Add a memory region + * + * @return the new, empty memory region entry + */ + public abstract AbstractSctlRegion addRegion(); + + /** + * Get the list of memory regions + * + * @return the regions + */ + public abstract List getRegions(); + + /** + * Add a binary module + * + * @return the new, empty binary module entry + */ + public abstract AbstractSctlBinary addBinary(); + + /** + * Get the list of binary modules + * + * @return the binaries + */ + public abstract List getBinaries(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlThreadEntry.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlThreadEntry.java new file mode 100644 index 0000000000..2d84955765 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlThreadEntry.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import ghidra.comm.packet.Packet; + +/** + * A thread entry of an attachable target process + */ +public abstract class AbstractSctlThreadEntry extends Packet { + /** + * Get the thread ID + * + * @return the thread ID + */ + public abstract long getThreadID(); + + /** + * Set the thread ID + * + * @param tid the thread ID + */ + public abstract void setThreadID(long tid); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlTrapSpec.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlTrapSpec.java new file mode 100644 index 0000000000..13cd0bb6ce --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/AbstractSctlTrapSpec.java @@ -0,0 +1,58 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import ghidra.comm.packet.Packet; + +/** + * A trap specification + * + * This is a dialect-defined format. This is effectively an interface defining the union of options + * for all supported dialects. If a dialect does not support an option, it must throw an + * {@link UnsupportedOperationException}. For {@code get} operations, the dialect should return what + * is implied by the restrictions of the dialect. + */ +public abstract class AbstractSctlTrapSpec extends Packet { + public abstract void setAddress(long address); + + public abstract long getAddress(); + + public abstract void setLength(long length); + + public abstract long getLength(); + + public abstract void setActionStop(); + + public abstract boolean isActionStop(); + + public abstract void setActionSnap(); + + public abstract boolean isActionSnap(); + + public abstract void setSoftwareExecute(); + + public abstract boolean isSoftwareExecute(); + + public abstract void setHardware(boolean read, boolean write, boolean execute); + + public abstract boolean isHardware(); + + public abstract boolean isHardwareRead(); + + public abstract boolean isHardwareWrite(); + + public abstract boolean isHardwareExecute(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlRegisterDefinition.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlRegisterDefinition.java new file mode 100644 index 0000000000..c2d6336e2d --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlRegisterDefinition.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.fields.PacketField; + +public class SctlRegisterDefinition extends Packet { + public SctlRegisterDefinition() { + } + + public SctlRegisterDefinition(long regid, String name, long nbits) { + this.regid = regid; + this.name = new SctlString(name); + this.nbits = nbits; + } + + @PacketField + public long regid; + + @PacketField + public SctlString name; + + // TODO: Consider a type name instead/in addition? + @PacketField + public long nbits; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlString.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlString.java new file mode 100644 index 0000000000..ec976d571b --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlString.java @@ -0,0 +1,52 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.annot.SizedByField; +import ghidra.comm.packet.fields.PacketField; + +/** + * A string in {@code size[8]str[size]} format + */ +public class SctlString extends Packet { + /** + * For unmarshalling + */ + public SctlString() { + } + + /** + * Wrap a string for SCTL packets + * + * @param str the string + */ + public SctlString(String str) { + this.str = str; + } + + @PacketField + public long len; + + @PacketField + @SizedByField("len") + public String str; + + @Override + public String toString() { + return "\"" + str + "\""; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlSymbol.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlSymbol.java new file mode 100644 index 0000000000..0830c63d1c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/SctlSymbol.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.annot.BitmaskEncoded; +import ghidra.comm.packet.fields.PacketField; +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.sctl.protocol.consts.Stype; +import ghidra.dbg.sctl.protocol.types.SctlAttributes; +import ghidra.dbg.sctl.protocol.types.SelSctlTypeName; + +/** + * The format of {@code sym} + */ +public class SctlSymbol extends Packet { + /** + * For unmarshalling + */ + public SctlSymbol() { + } + + /** + * Construct the symbol portion of a message + * + * @param name the name + * @param flags the Stype flags + * @param val the value (address or constant) + * @param size the size, in bytes + * @param tname the type name + */ + public SctlSymbol(String name, BitmaskSet flags, long val, long size, + SelSctlTypeName tname) { + this.name = new SctlString(name); + this.flags = flags; + this.val = val; + this.attrs = SctlAttributes.empty(); + this.size = size; + this.tname = tname; + } + + @PacketField + public SctlString name; + + @PacketField + @BitmaskEncoded(type = Byte.class) + public BitmaskSet flags; + + @PacketField + public long val; + + @PacketField + public SctlAttributes attrs; + + @PacketField + public long size; + + @PacketField + public SelSctlTypeName tname; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlEventNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlEventNotification.java new file mode 100644 index 0000000000..b427b785a4 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlEventNotification.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import ghidra.comm.packet.Packet; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * A base class for all {@code Aevent} (event notification) SCTL messages + */ +public abstract class AbstractSctlEventNotification extends Packet { + public abstract AbstractSctlContext getCtx(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlForkCloneNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlForkCloneNotification.java new file mode 100644 index 0000000000..6d7fcf283a --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlForkCloneNotification.java @@ -0,0 +1,51 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import ghidra.comm.packet.annot.OptionalField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Factored common parts for {@code Efork} and {@code Eclone} SCTL events + */ +public abstract class AbstractSctlForkCloneNotification extends AbstractSctlEventNotification { + public AbstractSctlForkCloneNotification() { + } + + public AbstractSctlForkCloneNotification(long spwnid, AbstractSctlContext ctx, + AbstractSctlContext spwnctx) { + this.spwnid = spwnid; + this.ctx = ctx; + this.spwnctx = spwnctx; + } + + @Override + public AbstractSctlContext getCtx() { + return ctx; + } + + @PacketField + public long spwnid; + + @PacketField + @OptionalField + public AbstractSctlContext ctx; + + @PacketField + @OptionalField + public AbstractSctlContext spwnctx; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlForkNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlForkNotification.java new file mode 100644 index 0000000000..f8ebb6bf70 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlForkNotification.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import ghidra.dbg.sctl.dialect.HasOptionalProcessID; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +public abstract class AbstractSctlForkNotification extends AbstractSctlForkCloneNotification + implements HasOptionalProcessID { + public AbstractSctlForkNotification() { + super(); + } + + public AbstractSctlForkNotification(long spwnid, AbstractSctlContext ctx, + AbstractSctlContext spwnctx) { + super(spwnid, ctx, spwnctx); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlListsLibrariesEventNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlListsLibrariesEventNotification.java new file mode 100644 index 0000000000..9a0f9c671f --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlListsLibrariesEventNotification.java @@ -0,0 +1,74 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import java.util.List; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.annot.*; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * Factored common parts for {@code Eload} and {@code Eunload} SCTL events + */ +public abstract class AbstractSctlListsLibrariesEventNotification + extends AbstractSctlEventNotification { + + public AbstractSctlListsLibrariesEventNotification() { + } + + public AbstractSctlListsLibrariesEventNotification(List libs, + AbstractSctlContext ctx) { + this.libs = libs; + this.ctx = ctx; + } + + @PacketField + public long nl; + + @PacketField + @RepeatedField + @CountedByField("nl") + public List libs; + + // TODO: Modify this for any-any-2018. May just remove base.... + public static class PathBase extends Packet { + public PathBase() { + } + + public PathBase(String path, long base) { + this.path = new SctlString(path); + this.base = base; + } + + @PacketField + public SctlString path; + + @PacketField + public long base; + } + + @PacketField + @OptionalField + public AbstractSctlContext ctx; + + @Override + public AbstractSctlContext getCtx() { + return ctx; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlOnlyCtxEventNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlOnlyCtxEventNotification.java new file mode 100644 index 0000000000..cbcd0ce7a0 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlOnlyCtxEventNotification.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import ghidra.comm.packet.annot.OptionalField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Factored common parts for SCTL events providing only the context registers + * + * Those are {@code Eexec}, {@code Esignal}, and {@code Esyscall}. + */ +public abstract class AbstractSctlOnlyCtxEventNotification extends AbstractSctlEventNotification { + public AbstractSctlOnlyCtxEventNotification() { + } + + public AbstractSctlOnlyCtxEventNotification(AbstractSctlContext ctx) { + this.ctx = ctx; + } + + @PacketField + @OptionalField + public AbstractSctlContext ctx; + + @Override + public AbstractSctlContext getCtx() { + return ctx; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlSnapNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlSnapNotification.java new file mode 100644 index 0000000000..fa708ebcda --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/AbstractSctlSnapNotification.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import ghidra.comm.packet.annot.OptionalField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.dialect.HasOptionalProcessID; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +public abstract class AbstractSctlSnapNotification extends AbstractSctlEventNotification + implements HasOptionalProcessID { + @PacketField + public long trpid; + + @PacketField + public long spwnid; + + @PacketField + @OptionalField + public AbstractSctlContext ctx; + + @Override + public AbstractSctlContext getCtx() { + return ctx; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlCloneNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlCloneNotification.java new file mode 100644 index 0000000000..63aaa9cdc1 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlCloneNotification.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Format for the {@code Eclone} SCTL event + */ +public class SctlCloneNotification extends AbstractSctlForkCloneNotification { + public SctlCloneNotification() { + } + + public SctlCloneNotification(long spwnid, AbstractSctlContext ctx, + AbstractSctlContext spwnctx) { + super(spwnid, ctx, spwnctx); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlEventNotify.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlEventNotify.java new file mode 100644 index 0000000000..e64f796ba2 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlEventNotify.java @@ -0,0 +1,190 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import java.util.LinkedHashSet; +import java.util.Set; + +import ghidra.comm.packet.annot.BitmaskEncoded; +import ghidra.comm.packet.annot.WithFlag; +import ghidra.comm.packet.fields.PacketField; +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.sctl.protocol.AbstractSctlNotify; +import ghidra.dbg.sctl.protocol.consts.Evkind; + +/** + * Format for the {@code Aevent} SCTL message + * + * This parses the flags and each applicable event. It is unclear how many events can be present in + * one message. Though, the documentation seems to imply exactly one must be present. + */ +public class SctlEventNotify extends AbstractSctlNotify { + /** + * For unmarshalling + */ + public SctlEventNotify() { + } + + /** + * Construct an {@code Aevent} message with the one given event details + * + * Additional events may be added using {@link #addOrSetDetails(AbstractSctlEventNotification)}. + * Note that more than one event may not be within the official specification. The proper flags + * will be set during message encoding. + * + * @param ctlid the CTLID of the affected thread + * @param details the event details + */ + public SctlEventNotify(long ctlid, AbstractSctlEventNotification details) { + this(ctlid, details, null); + } + + /** + * Construct an {@code Aevent} message with the one given event details and additional flags + * + * The event flags will be set during message encoding. The provided flags are set in addition + * to required event flag(s). Note: If an event flag is provided in {@code addFlags}, but that + * event is not actually present in the details, it will be cleared while encoding. + * + * @param ctlid the CTLID of the affected thread + * @param details the event details + * @param addFlags additional flags to set + */ + public SctlEventNotify(long ctlid, AbstractSctlEventNotification details, + BitmaskSet addFlags) { + this.ctlid = ctlid; + this.flags = addFlags; + addOrSetDetails(details); + } + + public void clearDetails() { + syscall = null; + trap = null; + snap = null; + fork = null; + clone = null; + exec = null; + exit = null; + signal = null; + load = null; + unload = null; + } + + public void addOrSetDetails(AbstractSctlEventNotification details) { + if (details == null) { + return; + } + else if (details instanceof SctlSyscallNotification) { + syscall = (SctlSyscallNotification) details; + } + else if (details instanceof SctlTrapNotification) { + trap = (SctlTrapNotification) details; + } + else if (details instanceof AbstractSctlSnapNotification) { + snap = (AbstractSctlSnapNotification) details; + } + else if (details instanceof AbstractSctlForkNotification) { + fork = (AbstractSctlForkNotification) details; + } + else if (details instanceof SctlCloneNotification) { + clone = (SctlCloneNotification) details; + } + else if (details instanceof SctlExecNotification) { + exec = (SctlExecNotification) details; + } + else if (details instanceof SctlExitNotification) { + exit = (SctlExitNotification) details; + } + else if (details instanceof SctlSignalNotification) { + signal = (SctlSignalNotification) details; + } + else if (details instanceof SctlLoadNotification) { + load = (SctlLoadNotification) details; + } + else if (details instanceof SctlUnloadNotification) { + unload = (SctlUnloadNotification) details; + } + } + + private static void addIfNotNull(Set set, T t) { + if (t != null) { + set.add(t); + } + } + + // There should really only be one anyway + public Set getAllEvents() { + Set all = new LinkedHashSet<>(); + addIfNotNull(all, syscall); + addIfNotNull(all, trap); + addIfNotNull(all, snap); + addIfNotNull(all, fork); + addIfNotNull(all, clone); + addIfNotNull(all, exec); + addIfNotNull(all, exit); + addIfNotNull(all, signal); + addIfNotNull(all, load); + addIfNotNull(all, unload); + return all; + } + + @PacketField + public long ctlid; + + @PacketField + @BitmaskEncoded(universe = Evkind.class) + public BitmaskSet flags; + + @PacketField + @WithFlag(by = "flags", flag = "Esyscall") + public SctlSyscallNotification syscall; + + @PacketField + @WithFlag(by = "flags", flag = "Etrap") + public SctlTrapNotification trap; + + @PacketField + @WithFlag(by = "flags", flag = "Esnap") + public AbstractSctlSnapNotification snap; + + @PacketField + @WithFlag(by = "flags", flag = "Efork") + public AbstractSctlForkNotification fork; + + @PacketField + @WithFlag(by = "flags", flag = "Eclone") + public SctlCloneNotification clone; + + @PacketField + @WithFlag(by = "flags", flag = "Eexec") + public SctlExecNotification exec; + + @PacketField + @WithFlag(by = "flags", flag = "Eexit") + public SctlExitNotification exit; + + @PacketField + @WithFlag(by = "flags", flag = "Esignal") + public SctlSignalNotification signal; + + @PacketField + @WithFlag(by = "flags", flag = "Eload") + public SctlLoadNotification load; + + @PacketField + @WithFlag(by = "flags", flag = "Eunload") + public SctlUnloadNotification unload; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlExecNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlExecNotification.java new file mode 100644 index 0000000000..353b6dba92 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlExecNotification.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +/** + * Format for the {@code Eexec} SCTL event + */ +public class SctlExecNotification extends AbstractSctlOnlyCtxEventNotification { + // No content +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlExitNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlExitNotification.java new file mode 100644 index 0000000000..2dc252a3bf --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlExitNotification.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import ghidra.comm.packet.annot.OptionalField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Format for the {@code Eexit} SCTL event + */ +public class SctlExitNotification extends AbstractSctlEventNotification { + public SctlExitNotification() { + } + + public SctlExitNotification(long status, AbstractSctlContext ctx) { + this.status = status; + this.ctx = ctx; + } + + @PacketField + public long status; + + @PacketField + @OptionalField + public AbstractSctlContext ctx; + + @Override + public AbstractSctlContext getCtx() { + return ctx; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlLoadNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlLoadNotification.java new file mode 100644 index 0000000000..c11cd5e257 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlLoadNotification.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import java.util.List; + +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Format for the {@code Eload} SCTL event + */ +public class SctlLoadNotification extends AbstractSctlListsLibrariesEventNotification { + public SctlLoadNotification() { + } + + public SctlLoadNotification(List libs, AbstractSctlContext ctx) { + super(libs, ctx); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlSignalNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlSignalNotification.java new file mode 100644 index 0000000000..e2178ea718 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlSignalNotification.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Format for the {@code Esignal} SCTL event + */ +public class SctlSignalNotification extends AbstractSctlOnlyCtxEventNotification { + public SctlSignalNotification() { + } + + public SctlSignalNotification(AbstractSctlContext ctx) { + super(ctx); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlSyscallNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlSyscallNotification.java new file mode 100644 index 0000000000..15c44a8c53 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlSyscallNotification.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +/** + * Format for the {@code Esyscall} SCTL event + */ +public class SctlSyscallNotification extends AbstractSctlOnlyCtxEventNotification { + // No content +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlTrapNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlTrapNotification.java new file mode 100644 index 0000000000..ad175d60ac --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlTrapNotification.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import ghidra.comm.packet.annot.OptionalField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Format for the {@code Etrap} SCTL event + */ +public class SctlTrapNotification extends AbstractSctlEventNotification { + public SctlTrapNotification() { + } + + public SctlTrapNotification(long trpid, AbstractSctlContext ctx) { + this.trpid = trpid; + this.ctx = ctx; + } + + @PacketField + public long trpid; + + @PacketField + @OptionalField + public AbstractSctlContext ctx; + + @Override + public AbstractSctlContext getCtx() { + return ctx; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlUnloadNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlUnloadNotification.java new file mode 100644 index 0000000000..12e6dbdc60 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/notify/SctlUnloadNotification.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.notify; + +import java.util.List; + +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Format for the {@code Eclone} SCTL event + */ +public class SctlUnloadNotification extends AbstractSctlListsLibrariesEventNotification { + public SctlUnloadNotification() { + } + + public SctlUnloadNotification(List libs, AbstractSctlContext ctx) { + super(libs, ctx); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlAttachReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlAttachReply.java new file mode 100644 index 0000000000..0775b47b3e --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlAttachReply.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.annot.OptionalField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.dialect.HasOptionalPlatform; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Format for the {@code Rattach} SCTL message + */ +public abstract class AbstractSctlAttachReply extends AbstractSctlReply + implements HasOptionalPlatform { + public AbstractSctlAttachReply() { + } + + public AbstractSctlAttachReply(long ctlid, AbstractSctlContext ctx) { + this.ctlid = ctlid; + this.ctx = ctx; + } + + @PacketField + public long ctlid; + + @PacketField + @OptionalField + public AbstractSctlContext ctx; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlLaunchReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlLaunchReply.java new file mode 100644 index 0000000000..a1eededeed --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlLaunchReply.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.annot.OptionalField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.dialect.HasOptionalPlatform; +import ghidra.dbg.sctl.dialect.HasOptionalProcessID; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +public abstract class AbstractSctlLaunchReply extends AbstractSctlReply + implements HasOptionalProcessID, HasOptionalPlatform { + @PacketField + public long ctlid; + + @PacketField + @OptionalField + public AbstractSctlContext ctx; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlSnapshotReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlSnapshotReply.java new file mode 100644 index 0000000000..07f3dc38ec --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/AbstractSctlSnapshotReply.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.annot.OptionalField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.dialect.HasOptionalProcessID; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Format for the {@code Rsnap} SCTL message + */ +public abstract class AbstractSctlSnapshotReply extends AbstractSctlReply + implements HasOptionalProcessID { + @PacketField + public long spwnid; + + @PacketField + @OptionalField + public AbstractSctlContext ctx; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlClearTrapReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlClearTrapReply.java new file mode 100644 index 0000000000..da4aa5d0ab --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlClearTrapReply.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rclrtrap} SCTL message + */ +public class SctlClearTrapReply extends AbstractSctlReply { + // Empty +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlContinueReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlContinueReply.java new file mode 100644 index 0000000000..dbc3070889 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlContinueReply.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rcont} SCTL message + */ +public class SctlContinueReply extends AbstractSctlReply { + // Empty +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlDetachReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlDetachReply.java new file mode 100644 index 0000000000..4e5482357c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlDetachReply.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rdetach} SCTL message + */ +public class SctlDetachReply extends AbstractSctlReply { + // Empty +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateLocalsReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateLocalsReply.java new file mode 100644 index 0000000000..da74c692ef --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateLocalsReply.java @@ -0,0 +1,119 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import java.util.List; +import java.util.Map; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.annot.*; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.SctlString; +import ghidra.dbg.sctl.protocol.consts.Lkind; +import ghidra.dbg.sctl.protocol.consts.Vkind; +import ghidra.dbg.sctl.protocol.types.SelSctlTypeName; + +/** + * Format for the {@code Renumloc} SCTL message + */ +public class SctlEnumerateLocalsReply extends AbstractSctlReply { + // This is currently untested, i.e., I do not know how reference sctl behaves. + + @PacketField + public long nl; + + @PacketField + @RepeatedField + @CountedByField("nl") + public List locs; + + public static class Local extends Packet { + @PacketField + public SctlString id; + + @PacketField + public SelSctlTypeName tname; + + @PacketField + public Vkind kind; + + @PacketField + public SelLocalExpr lexpr; + } + + public static abstract class AbstractLocalExpr extends Packet { + // Empty + } + + public static class SelLocalExpr extends Packet { + public static final Map> KIND_MAP = + typeMap(Lkind.class, AbstractLocalExpr.class) // + .put(Lkind.Lreg, LocalRegisterExpr.class) // + .put(Lkind.Lderef, LocalDereferenceExpr.class) // + .put(Lkind.Ladd, LocalAddExpr.class) // + .put(Lkind.Lsub, LocalSubExpr.class) // + .put(Lkind.Lulit, LocalUnsignedLiteralExpr.class) // + .put(Lkind.Lslit, LocalSignedLiteralExpr.class) // + .build(); + + @PacketField + public Lkind kind; + + @PacketField + @TypedByField(by = "kind", map = "KIND_MAP") + public AbstractLocalExpr expr; + } + + public static class LocalRegisterExpr extends AbstractLocalExpr { + @PacketField + public byte no; // register number, target-dependent + } + + public static class LocalDereferenceExpr extends AbstractLocalExpr { + @PacketField + public SelLocalExpr lexpr; + } + + public static class AbstractLocalBiExpr extends AbstractLocalExpr { + @PacketField + public SelLocalExpr lexpr1; + + @PacketField + public SelLocalExpr lexpr2; + } + + public static class LocalAddExpr extends AbstractLocalBiExpr { + // No content + } + + public static class LocalSubExpr extends AbstractLocalBiExpr { + // No content + } + + public static abstract class AbstractLocalLiteralExpr extends AbstractLocalExpr { + @PacketField + public long val; + } + + public static class LocalUnsignedLiteralExpr extends AbstractLocalLiteralExpr { + // No content + } + + public static class LocalSignedLiteralExpr extends AbstractLocalLiteralExpr { + // No content + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateSegmentsReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateSegmentsReply.java new file mode 100644 index 0000000000..62b7f48e00 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateSegmentsReply.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Renumseg} SCTL message + */ +public class SctlEnumerateSegmentsReply extends AbstractSctlReply { + // This seems to lack documentation online +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateSymbolsReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateSymbolsReply.java new file mode 100644 index 0000000000..3a9e44c67c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateSymbolsReply.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.SctlSymbol; + +/** + * Format for the {@code Renumsym} SCTL message + */ +public class SctlEnumerateSymbolsReply extends AbstractSctlReply { + @PacketField + public long ns; // NOTE: "number of symbols", not "namespace" + + @PacketField + @RepeatedField + @CountedByField("ns") + public List syms; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateTypesReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateTypesReply.java new file mode 100644 index 0000000000..c353928b99 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlEnumerateTypesReply.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.types.SelSctlTypeDefinition; + +/** + * Format for the {@code Renumtype} SCTL message + */ +public class SctlEnumerateTypesReply extends AbstractSctlReply { + @PacketField + public long nt; + + @PacketField + @RepeatedField + @CountedByField("nt") + public List tdefs; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlErrorReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlErrorReply.java new file mode 100644 index 0000000000..688d2a98fc --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlErrorReply.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rerror} SCTL message + */ +public class SctlErrorReply extends AbstractSctlReply { + public SctlErrorReply() { + } + + public SctlErrorReply(String msg) { + this.msg = msg; + } + + @PacketField + public String msg; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlGetContextReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlGetContextReply.java new file mode 100644 index 0000000000..fc9c623931 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlGetContextReply.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; +import ghidra.dbg.sctl.protocol.consts.Mkind; + +/** + * Format for the {@link Mkind#Rgetctx} SCTL message + */ +public class SctlGetContextReply extends AbstractSctlReply { + public SctlGetContextReply() { + } + + public SctlGetContextReply(AbstractSctlContext ctx) { + this.ctx = ctx; + } + + @PacketField + public AbstractSctlContext ctx; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlKillReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlKillReply.java new file mode 100644 index 0000000000..170281fa52 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlKillReply.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rkill} SCTL message + */ +public class SctlKillReply extends AbstractSctlReply { + // Empty +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupAddressReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupAddressReply.java new file mode 100644 index 0000000000..07cf672626 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupAddressReply.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.SctlSymbol; + +/** + * Format for the {@code Rlookaddr} SCTL message + */ +public class SctlLookupAddressReply extends AbstractSctlReply { + @PacketField + public SctlSymbol sym; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupProgramCounterReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupProgramCounterReply.java new file mode 100644 index 0000000000..322a5c91f7 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupProgramCounterReply.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rlookpc} SCTL message + */ +public class SctlLookupProgramCounterReply extends AbstractSctlReply { + @PacketField + public long addr; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupSourceReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupSourceReply.java new file mode 100644 index 0000000000..b4bdf141f0 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupSourceReply.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * Format for the {@code Rlooksrc} SCTL message + */ +public class SctlLookupSourceReply extends AbstractSctlReply { + @PacketField + public SctlString file; + + @PacketField + public int line; + + @PacketField + public int col; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupSymbolReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupSymbolReply.java new file mode 100644 index 0000000000..dd743bdfb3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupSymbolReply.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.SctlSymbol; + +/** + * Format for the {@code Rlooksym} SCTL message + */ +public class SctlLookupSymbolReply extends AbstractSctlReply { + public SctlLookupSymbolReply() { + } + + public SctlLookupSymbolReply(SctlSymbol sym) { + this.sym = sym; + } + + @PacketField + public SctlSymbol sym; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupTypeReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupTypeReply.java new file mode 100644 index 0000000000..184e623d7c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlLookupTypeReply.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.types.SelSctlTypeDefinition; + +/** + * Format for the {@code Rlooktype} SCTL message + */ +public class SctlLookupTypeReply extends AbstractSctlReply { + @PacketField + public SelSctlTypeDefinition tdef; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlNamesReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlNamesReply.java new file mode 100644 index 0000000000..7720b1c0d9 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlNamesReply.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rnames} SCTL message + */ +public class SctlNamesReply extends AbstractSctlReply { + public SctlNamesReply() { + } + + public SctlNamesReply(long nsid) { + this.nsid = nsid; + } + + @PacketField + public long nsid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlPingReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlPingReply.java new file mode 100644 index 0000000000..b52b3b52ab --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlPingReply.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rping} SCTL message + */ +public class SctlPingReply extends AbstractSctlReply { + public SctlPingReply() { + } + + public SctlPingReply(long cnt) { + this.cnt = cnt; + } + + @PacketField + public long cnt; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlProcessListReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlProcessListReply.java new file mode 100644 index 0000000000..c1741a7c27 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlProcessListReply.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.AbstractSctlProcessList; + +/** + * Format for the {@code Rps} SCTL message + */ +public class SctlProcessListReply extends AbstractSctlReply { + public SctlProcessListReply() { + } + + public SctlProcessListReply(AbstractSctlProcessList pslist) { + this.pslist = pslist; + } + + // TODO: If a dialect arises where this format differs, I will need to re-factor. + @PacketField + public AbstractSctlProcessList pslist; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlReadReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlReadReply.java new file mode 100644 index 0000000000..47e29d10ae --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlReadReply.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rread} SCTL message + */ +public class SctlReadReply extends AbstractSctlReply { + public SctlReadReply() { + } + + public SctlReadReply(byte[] bytes) { + this.bytes = bytes; + } + + @PacketField + public byte[] bytes; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlSetContextReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlSetContextReply.java new file mode 100644 index 0000000000..2188241e15 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlSetContextReply.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rsetctx} SCTL message + */ +public class SctlSetContextReply extends AbstractSctlReply { + // Empty +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlSetTrapReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlSetTrapReply.java new file mode 100644 index 0000000000..da68ea62a5 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlSetTrapReply.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rsettrap} SCTL message + */ +public class SctlSetTrapReply extends AbstractSctlReply { + + public SctlSetTrapReply() { + } + + public SctlSetTrapReply(long trpid) { + this.trpid = trpid; + } + + @PacketField + public long trpid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStatusReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStatusReply.java new file mode 100644 index 0000000000..4c856166e6 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStatusReply.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.AbstractSctlStatus; + +/** + * Format for the {@code Rstat} SCTL message + */ +public class SctlStatusReply extends AbstractSctlReply { + @PacketField + public AbstractSctlStatus status; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStepReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStepReply.java new file mode 100644 index 0000000000..a4258d7819 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStepReply.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.annot.OptionalField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Format for the {@code Rstep} SCTL message + */ +public class SctlStepReply extends AbstractSctlReply { + public SctlStepReply() { + } + + public SctlStepReply(AbstractSctlContext ctx) { + this.ctx = ctx; + } + + @PacketField + @OptionalField + public AbstractSctlContext ctx; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStopReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStopReply.java new file mode 100644 index 0000000000..533720ab3c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlStopReply.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.annot.OptionalField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Format for the {@code Rstop} SCTL message + */ +public class SctlStopReply extends AbstractSctlReply { + public SctlStopReply() { + } + + public SctlStopReply(AbstractSctlContext ctx) { + this.ctx = ctx; + } + + @PacketField + @OptionalField + public AbstractSctlContext ctx; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlTraceReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlTraceReply.java new file mode 100644 index 0000000000..14423545b3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlTraceReply.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rtrace} SCTL message + */ +public class SctlTraceReply extends AbstractSctlReply { + // Empty +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlUnwindOneFrameReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlUnwindOneFrameReply.java new file mode 100644 index 0000000000..5bc49f7247 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlUnwindOneFrameReply.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import java.util.List; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.consts.Rulekind; + +/** + * Format for the {@code Runwind1} SCTL message + */ +public class SctlUnwindOneFrameReply extends AbstractSctlReply { + @PacketField + public long nr; + + @PacketField + @RepeatedField + @CountedByField("nr") + public List rules; + + // The docs refer to DWARF 3.0 spec Section 6.4, but I can't tell which subset, precisely. + public static class Rule extends Packet { + @PacketField + public Rulekind kind; + + @PacketField + public long reg; + + @PacketField + public long no; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlVersionReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlVersionReply.java new file mode 100644 index 0000000000..1c156af85a --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlVersionReply.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rversion} SCTL message + */ +public class SctlVersionReply extends AbstractSctlReply { + public SctlVersionReply() { + } + + public SctlVersionReply(String version) { + this.version = version; + } + + @PacketField + public String version; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlWriteReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlWriteReply.java new file mode 100644 index 0000000000..22da6bfc90 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/reply/SctlWriteReply.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.reply; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +/** + * Format for the {@code Rwrite} SCTL message + */ +public class SctlWriteReply extends AbstractSctlReply { + public SctlWriteReply() { + } + + public SctlWriteReply(long cnt) { + this.cnt = cnt; + } + + @PacketField + public long cnt; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlAttachRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlAttachRequest.java new file mode 100644 index 0000000000..5964d12a16 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlAttachRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tattach} SCTL message + */ +public class SctlAttachRequest extends AbstractSctlRequest { + public SctlAttachRequest() { + } + + public SctlAttachRequest(long pid) { + this.pid = pid; + } + + @PacketField + public long pid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlClearTrapRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlClearTrapRequest.java new file mode 100644 index 0000000000..11b5b2b1a4 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlClearTrapRequest.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tclrtrap} SCTL message + */ +public class SctlClearTrapRequest extends AbstractSctlRequest { + public SctlClearTrapRequest() { + } + + public SctlClearTrapRequest(long ctlid, long trpid) { + this.ctlid = ctlid; + this.trpid = trpid; + } + + @PacketField + public long ctlid; + + @PacketField + public long trpid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlContinueRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlContinueRequest.java new file mode 100644 index 0000000000..e370cb1115 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlContinueRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tcont} SCTL message + */ +public class SctlContinueRequest extends AbstractSctlRequest { + public SctlContinueRequest() { + } + + public SctlContinueRequest(long ctlid) { + this.ctlid = ctlid; + } + + @PacketField + public long ctlid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlDetachRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlDetachRequest.java new file mode 100644 index 0000000000..b4be2c5687 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlDetachRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tdetach} SCTL message + */ +public class SctlDetachRequest extends AbstractSctlRequest { + public SctlDetachRequest() { + } + + public SctlDetachRequest(long ctlid) { + this.ctlid = ctlid; + } + + @PacketField + public long ctlid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateLocalsRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateLocalsRequest.java new file mode 100644 index 0000000000..df6b24cd44 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateLocalsRequest.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tenumloc} SCTL message + */ +public class SctlEnumerateLocalsRequest extends AbstractSctlRequest { + @PacketField + public long nsid; + + @PacketField + public long addr; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateSegmentsRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateSegmentsRequest.java new file mode 100644 index 0000000000..eb39eb3538 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateSegmentsRequest.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tenumseq} SCTL message + */ +public class SctlEnumerateSegmentsRequest extends AbstractSctlRequest { + // No content +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateSymbolsRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateSymbolsRequest.java new file mode 100644 index 0000000000..2af6df3c50 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateSymbolsRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tenumsym} SCTL message + */ +public class SctlEnumerateSymbolsRequest extends AbstractSctlRequest { + public SctlEnumerateSymbolsRequest() { + } + + public SctlEnumerateSymbolsRequest(long nsid) { + this.nsid = nsid; + } + + @PacketField + public long nsid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateTypesRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateTypesRequest.java new file mode 100644 index 0000000000..e35a637a27 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlEnumerateTypesRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tenumtype} SCTL message + */ +public class SctlEnumerateTypesRequest extends AbstractSctlRequest { + public SctlEnumerateTypesRequest() { + } + + public SctlEnumerateTypesRequest(long nsid) { + this.nsid = nsid; + } + + @PacketField + public long nsid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlGetContextRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlGetContextRequest.java new file mode 100644 index 0000000000..f374475cb7 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlGetContextRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tgetctx} SCTL message + */ +public class SctlGetContextRequest extends AbstractSctlRequest { + public SctlGetContextRequest() { + } + + public SctlGetContextRequest(long ctlid) { + this.ctlid = ctlid; + } + + @PacketField + public long ctlid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlKillRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlKillRequest.java new file mode 100644 index 0000000000..17e32878de --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlKillRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tkill} SCTL message + */ +public class SctlKillRequest extends AbstractSctlRequest { + public SctlKillRequest() { + } + + public SctlKillRequest(long ctlid) { + this.ctlid = ctlid; + } + + @PacketField + public long ctlid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLaunchRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLaunchRequest.java new file mode 100644 index 0000000000..54a9986121 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLaunchRequest.java @@ -0,0 +1,55 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * Format for the {@code Tlaunch} SCTL message + * + * Technically, this should be dialect dependent, but it appears both dialects in the reference + * implementations use this same format. + */ +// TODO: Use an abstract placeholder if this turns out to be a dialect-dependent extension point +public class SctlLaunchRequest extends AbstractSctlRequest { + public SctlLaunchRequest() { + } + + public SctlLaunchRequest(List args) { + this.args = new ArrayList<>(); + for (String arg : args) { + this.args.add(new SctlString(arg)); + } + } + + @PacketField + public long flags; + + @PacketField + public long narg; + + @PacketField + @RepeatedField + @CountedByField("narg") + public List args; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupAddressRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupAddressRequest.java new file mode 100644 index 0000000000..e1e0d18ba8 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupAddressRequest.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tlookaddr} SCTL message + */ +public class SctlLookupAddressRequest extends AbstractSctlRequest { + @PacketField + public long nsid; + + @PacketField + public long addr; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupProgramCounterRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupProgramCounterRequest.java new file mode 100644 index 0000000000..54d0802a65 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupProgramCounterRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * Format for the {@code Tlookpc} SCTL message + */ +public class SctlLookupProgramCounterRequest extends AbstractSctlRequest { + @PacketField + public long nsid; + + @PacketField + public SctlString file; + + @PacketField + public int line; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupSourceRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupSourceRequest.java new file mode 100644 index 0000000000..3aa5a9e6a9 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupSourceRequest.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tlooksrc} SCTL message + */ +public class SctlLookupSourceRequest extends AbstractSctlRequest { + @PacketField + public long nsid; + + @PacketField + public long addr; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupSymbolRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupSymbolRequest.java new file mode 100644 index 0000000000..f7c7015426 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupSymbolRequest.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * Format for the {@code Tlooksym} SCTL message + */ +public class SctlLookupSymbolRequest extends AbstractSctlRequest { + public SctlLookupSymbolRequest() { + } + + public SctlLookupSymbolRequest(long nsid, String name) { + this.nsid = nsid; + this.name = new SctlString(name); + } + + @PacketField + public long nsid; + + @PacketField + public SctlString name; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupTypeRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupTypeRequest.java new file mode 100644 index 0000000000..57af0d3f40 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlLookupTypeRequest.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; +import ghidra.dbg.sctl.protocol.types.AbstractSctlTypeName; +import ghidra.dbg.sctl.protocol.types.SelSctlTypeName; + +/** + * Format for the {@code Tlooktype} SCTL message + */ +public class SctlLookupTypeRequest extends AbstractSctlRequest { + public SctlLookupTypeRequest() { + } + + public SctlLookupTypeRequest(long nsid, AbstractSctlTypeName tname) { + this.nsid = nsid; + this.tname = new SelSctlTypeName(tname); + } + + @PacketField + public long nsid; + + @PacketField + public SelSctlTypeName tname; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlNamesRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlNamesRequest.java new file mode 100644 index 0000000000..bf0d6dfa42 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlNamesRequest.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * Format for the {@code Tnames} SCTL message + * + * Note this just binds a namespace. The {@code Tenum/look}x requests then retrieve the + * names. + */ +// "bind namespace" +public class SctlNamesRequest extends AbstractSctlRequest { + @PacketField + public SctlString path; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlPingRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlPingRequest.java new file mode 100644 index 0000000000..2c6cb1c16a --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlPingRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tping} SCTL message + */ +public class SctlPingRequest extends AbstractSctlRequest { + public SctlPingRequest() { + } + + public SctlPingRequest(String string) { + bytes = string.getBytes(); + } + + @PacketField + public byte[] bytes; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlProcessListRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlProcessListRequest.java new file mode 100644 index 0000000000..e36107ec40 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlProcessListRequest.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tps} SCTL message + */ +public class SctlProcessListRequest extends AbstractSctlRequest { + // No content +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlReadRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlReadRequest.java new file mode 100644 index 0000000000..fe4a009f41 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlReadRequest.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tread} SCTL message + */ +public class SctlReadRequest extends AbstractSctlRequest { + public SctlReadRequest() { + } + + public SctlReadRequest(long ctlid, long fd, long offset, long cnt) { + this.ctlid = ctlid; + this.fd = fd; + this.offset = offset; + this.cnt = cnt; + } + + @PacketField + public long ctlid; + + @PacketField + public long fd; + + @PacketField + public long offset; + + @PacketField + public long cnt; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSetContextRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSetContextRequest.java new file mode 100644 index 0000000000..73d58131c8 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSetContextRequest.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; + +/** + * Format for the {@code Tsetctx} SCTL message + */ +public class SctlSetContextRequest extends AbstractSctlRequest { + public SctlSetContextRequest() { + } + + public SctlSetContextRequest(long ctlid, AbstractSctlContext ctx) { + this.ctlid = ctlid; + this.ctx = ctx; + } + + @PacketField + public long ctlid; + + @PacketField + public AbstractSctlContext ctx; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSetTrapRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSetTrapRequest.java new file mode 100644 index 0000000000..14446ccb00 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSetTrapRequest.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; +import ghidra.dbg.sctl.protocol.common.AbstractSctlTrapSpec; + +/** + * Format for the {@code Tsettrap} SCTL message + */ +public class SctlSetTrapRequest extends AbstractSctlRequest { + public SctlSetTrapRequest() { + } + + public SctlSetTrapRequest(long ctlid, AbstractSctlTrapSpec spec) { + this.ctlid = ctlid; + this.spec = spec; + } + + @PacketField + public long ctlid; + + @PacketField + public AbstractSctlTrapSpec spec; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSnapshotRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSnapshotRequest.java new file mode 100644 index 0000000000..e3d92fca4c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlSnapshotRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tsnap} SCTL message + */ +public class SctlSnapshotRequest extends AbstractSctlRequest { + public SctlSnapshotRequest() { + } + + public SctlSnapshotRequest(long ctlid) { + this.ctlid = ctlid; + } + + @PacketField + public long ctlid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStatusRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStatusRequest.java new file mode 100644 index 0000000000..e0ab4293df --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStatusRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tstatus} SCTL message + */ +public class SctlStatusRequest extends AbstractSctlRequest { + public SctlStatusRequest() { + } + + public SctlStatusRequest(long ctlid) { + this.ctlid = ctlid; + } + + @PacketField + public long ctlid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStepRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStepRequest.java new file mode 100644 index 0000000000..17baea8c16 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStepRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tstep} SCTL message + */ +public class SctlStepRequest extends AbstractSctlRequest { + public SctlStepRequest() { + } + + public SctlStepRequest(long ctlid) { + this.ctlid = ctlid; + } + + @PacketField + public long ctlid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStopRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStopRequest.java new file mode 100644 index 0000000000..d1c25b5cb2 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlStopRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tstop} SCTL message + */ +public class SctlStopRequest extends AbstractSctlRequest { + public SctlStopRequest() { + } + + public SctlStopRequest(long ctlid) { + this.ctlid = ctlid; + } + + @PacketField + public long ctlid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlTraceRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlTraceRequest.java new file mode 100644 index 0000000000..50e5217404 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlTraceRequest.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.annot.BitmaskEncoded; +import ghidra.comm.packet.fields.PacketField; +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; +import ghidra.dbg.sctl.protocol.consts.Evkind; + +/** + * Format for the {@code Ttrace} SCTL message + */ +public class SctlTraceRequest extends AbstractSctlRequest { + public SctlTraceRequest() { + } + + public SctlTraceRequest(long ctlid, BitmaskSet flags) { + this.ctlid = ctlid; + this.flags = flags; + } + + @PacketField + public long ctlid; + + @PacketField + @BitmaskEncoded(universe = Evkind.class) + public BitmaskSet flags; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlUnwindOneFrameRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlUnwindOneFrameRequest.java new file mode 100644 index 0000000000..5a192f5fb4 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlUnwindOneFrameRequest.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tunwind1} SCTL message + */ +public class SctlUnwindOneFrameRequest extends AbstractSctlRequest { + @PacketField + public long nsid; + + @PacketField + public long addr; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlVersionRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlVersionRequest.java new file mode 100644 index 0000000000..6f04500902 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlVersionRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Tversion} SCTL message + */ +public class SctlVersionRequest extends AbstractSctlRequest { + public SctlVersionRequest() { + } + + public SctlVersionRequest(String version) { + this.version = version; + } + + @PacketField + public String version; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlWriteRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlWriteRequest.java new file mode 100644 index 0000000000..1a026c0d34 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/common/request/SctlWriteRequest.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.common.request; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +/** + * Format for the {@code Twrite} SCTL message + */ +public class SctlWriteRequest extends AbstractSctlRequest { + public SctlWriteRequest() { + } + + public SctlWriteRequest(long ctlid, long fd, long offset, byte[] bytes) { + this.ctlid = ctlid; + this.fd = fd; + this.offset = offset; + this.bytes = bytes; + } + + @PacketField + public long ctlid; + + @PacketField + public long fd; + + @PacketField + public long offset; + + @PacketField + public byte[] bytes; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Attrval.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Attrval.java new file mode 100644 index 0000000000..5fe01450db --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Attrval.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.consts; + +/** + * See the SCTL documentation + */ +public enum Attrval { + Astr, Acid, Auint; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Cbase.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Cbase.java new file mode 100644 index 0000000000..f6ca7994d7 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Cbase.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.consts; + +/** + * See the SCTL documentation + */ +public enum Cbase { + Vundef("undef"), + Vbool("_Bool"), + Vchar("char"), + Vshort("short"), + Vint("int"), + Vlong("long"), + Vvlong("long long"), + Vuchar("unsigned char"), + Vushort("unsigned short"), + Vuint("unsigned int"), + Vulong("unsigned long"), + Vuvlong("unsigned long long"), + Vfloat("float"), + Vdouble("double"), + Vlongdouble("long double"), + Vcomplex("float complex"), + Vdoublex("double complex"), + Vlongdoublex("long double complex"), + Vptr("ptr"), + Vvoid("void"); + + private String cname; + + Cbase(String cname) { + this.cname = cname; + } + + @Override + public String toString() { + return "Cbase:" + cname; + } + + public String getCName() { + return cname; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Evkind.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Evkind.java new file mode 100644 index 0000000000..66076c3537 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Evkind.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.consts; + +import ghidra.comm.util.BitmaskUniverse; +import ghidra.dbg.sctl.client.SctlExtension; + +/** + * Taken from Evkind in the SCTL documentation + * + * Two extra bits have been added to make the protocol more orthogonal, but remain backward + * compatible. The {@link #Estopped} bit indicates that the target has been stopped as part of this + * trace event. This is the usual case except for {@link #Efork} and {@link #Eclone} on Windows. The + * {@link #Erunning} bit indicates the opposite: the target is still running, despite issuing this + * trace event. Servers supporting this mechanism must set exactly one of these bits. If neither is + * present, the client must assume the server does not support the extension, and use the dialect + * default. To set both is an error. + * + * For now, this modification has been rolled into the SCTL-Bus extension. However, a server cannot + * use this extension with a non-bus client. + */ +public enum Evkind implements BitmaskUniverse { + Eclear(0 << 0), + Eset(1 << 0), + Esyscall(1 << 1), // Not valid on Windows + Eexec(1 << 2), // Not valid on Windows + Efork(1 << 3), + Eclone(1 << 4), + Esignal(1 << 5), + Eexit(1 << 6), + Etrap(1 << 7), + Esnap(1 << 8), + Estepctx(1 << 9), + Eload(1 << 10), + Eunload(1 << 11), + @SctlExtension("The target has stopped") + Estopped(1 << 12), + @SctlExtension("The target is still running") + Erunning(1 << 13); + + public final long mask; + + private Evkind(long mask) { + this.mask = mask; + } + + @Override + public long getMask() { + return mask; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Lkind.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Lkind.java new file mode 100644 index 0000000000..14695fed61 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Lkind.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.consts; + +/** + * See the SCTL documentation + */ +public enum Lkind { + Lreg, Lderef, Ladd, Lsub, Lulit, Lslit; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Mkind.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Mkind.java new file mode 100644 index 0000000000..a7cac238d2 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Mkind.java @@ -0,0 +1,102 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.consts; + +import ghidra.dbg.sctl.client.SctlExtension; + +/** + * See the SCTL documentation + */ +public enum Mkind { + Reserved0, + Rerror, + Reserved2, + Aevent, + Tversion, + Rversion, + Tping, + Rping, + Tps, + Rps, + Tlaunch, + Rlaunch, + Tattach, + Rattach, + Tstat, + Rstat, + Tcont, + Rcont, + Tstop, + Rstop, + Tstep, + Rstep, + Tsnap, + Rsnap, + Tkill, + Rkill, + Tdetach, + Rdetach, + Ttrace, + Rtrace, + Tsettrap, + Rsettrap, + Tclrtrap, + Rclrtrap, + Tgetctx, + Rgetctx, + Tsetctx, + Rsetctx, + Tread, + Rread, + Twrite, + Rwrite, + Tlooksym, + Rlooksym, + Tenumsym, + Renumsym, + Tlooktype, + Rlooktype, + Tenumtype, + Renumtype, + Tlookaddr, + Rlookaddr, + Tenumloc, + Renumloc, + Tenumseg, + Renumseg, + Tnames, + Rnames, + Tunwind1, + Runwind1, + Tlooksrc, + Rlooksrc, + Tlookpc, + Rlookpc, + @SctlExtension("Execute a CLI command") + Texec, + @SctlExtension("CLI Command output") + Rexec, + Tenumctx, + Renumctx, + Tchoosectx, + Rchoosectx, + Tfocus, + Rfocus, + Tgetchildren, + Rgetchildren, + Tgetattributes, + Rgetattributes, +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Rkind.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Rkind.java new file mode 100644 index 0000000000..891fba7abe --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Rkind.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.consts; + +import ghidra.dbg.attributes.TargetPrimitiveDataType.PrimitiveKind; + +/** + * See the SCTL documentation + */ +public enum Rkind { + Rundef(1, PrimitiveKind.UNDEFINED), + // Unsigned little-endian + Ru08le(1, PrimitiveKind.UINT), + Ru16le(2, PrimitiveKind.UINT), + Ru32le(4, PrimitiveKind.UINT), + Ru64le(8, PrimitiveKind.UINT), + // Signed little-endian + Rs08le(1, PrimitiveKind.SINT), + Rs16le(2, PrimitiveKind.SINT), + Rs32le(4, PrimitiveKind.SINT), + Rs64le(8, PrimitiveKind.SINT), + // Unsigned big-endian + Ru08be(1, PrimitiveKind.UINT), + Ru16be(2, PrimitiveKind.UINT), + Ru32be(4, PrimitiveKind.UINT), + Ru64be(8, PrimitiveKind.UINT), + // Signed big-endian + Rs08be(1, PrimitiveKind.SINT), + Rs16be(2, PrimitiveKind.SINT), + Rs32be(4, PrimitiveKind.SINT), + Rs64be(8, PrimitiveKind.SINT), + // Floating-point + Rf32(4, PrimitiveKind.FLOAT), + Rf64(8, PrimitiveKind.FLOAT), + Rf96(12, PrimitiveKind.FLOAT), + Rf128(16, PrimitiveKind.FLOAT), + // Complex floating-point + Rx64(8, PrimitiveKind.COMPLEX), + Rx128(16, PrimitiveKind.COMPLEX), + Rx192(24, PrimitiveKind.COMPLEX); + + private final int byteLength; + private final PrimitiveKind kind; + + Rkind(int byteLength, PrimitiveKind kind) { + this.byteLength = byteLength; + this.kind = kind; + } + + public int getByteLength() { + return byteLength; + } + + public PrimitiveKind getKind() { + return kind; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Rulekind.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Rulekind.java new file mode 100644 index 0000000000..6513edd226 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Rulekind.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.consts; + +/** + * See the SCTL documentation + */ +public enum Rulekind { + Rnone, Rcfaro, Rcfae, Rsame, Roff, Rvoff, Rreg, Rexp, Rvexp; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Stype.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Stype.java new file mode 100644 index 0000000000..8365eb0b11 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Stype.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.consts; + +import ghidra.comm.util.BitmaskUniverse; + +/** + * See the SCTL documentation + */ +public enum Stype implements BitmaskUniverse { + Sundef(1 << 0), Sdata(1 << 1), Stext(1 << 2), Sro(1 << 3), Senum(1 << 4), Sinline(1 << 5); + + public final long mask; + + private Stype(long mask) { + this.mask = mask; + } + + @Override + public long getMask() { + return mask; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Tkind.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Tkind.java new file mode 100644 index 0000000000..d913a93393 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Tkind.java @@ -0,0 +1,46 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.consts; + +/** + * See the SCTL documentation + */ +public enum Tkind { + Tvoid("void"), + Tbase("base"), + Tstruct("struct"), + Tunion("union"), + Tenum("enum"), + Tptr("pointer"), + Tarr("array"), + Tfun("function"), + Ttypedef("typedef"), + Tbitfield("bitfield"), + Tconst("constant"), + Txaccess("xaccess"), + Tundef("undef"); + + private String str; + + Tkind(String str) { + this.str = str; + } + + @Override + public String toString() { + return "Tkind:" + str; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Vkind.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Vkind.java new file mode 100644 index 0000000000..9a32bd5afd --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/consts/Vkind.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.consts; + +/** + * See the SCTL documentation + */ +public enum Vkind { + Vlocal, Vparam; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlAggregateTypeDefinition.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlAggregateTypeDefinition.java new file mode 100644 index 0000000000..1fda4cec4e --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlAggregateTypeDefinition.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for aggregate type definitions in SCTL + */ +public abstract class AbstractSctlAggregateTypeDefinition extends AbstractSctlTypeDefinition { + @PacketField + public SctlString tag; + + @PacketField + public long size; + + @PacketField + public SctlAttributes attrs; + + @PacketField + public long nfield; + + @PacketField + @RepeatedField + @CountedByField("nfield") + public List fields; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTaggedTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTaggedTypeName.java new file mode 100644 index 0000000000..b42547ee11 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTaggedTypeName.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for tagged type names in SCTL + */ +public abstract class AbstractSctlTaggedTypeName extends AbstractSctlTypeName { + public AbstractSctlTaggedTypeName() { + } + + public AbstractSctlTaggedTypeName(SctlString tag) { + this.tag = tag; + } + + @PacketField + public SctlString tag; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTypeDefinition.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTypeDefinition.java new file mode 100644 index 0000000000..dd491f7e5f --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTypeDefinition.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.Packet; + +/** + * A based type for each kind of {@code tdef[]} in SCTL + */ +public abstract class AbstractSctlTypeDefinition extends Packet { + public abstract AbstractSctlTypeName getTypeName(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTypeName.java new file mode 100644 index 0000000000..8951351472 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/AbstractSctlTypeName.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.Packet; + +/** + * A based type for each kind of {@code tname[]} in SCTL + */ +public class AbstractSctlTypeName extends Packet { + // No contents +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAggregateField.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAggregateField.java new file mode 100644 index 0000000000..297c2953d6 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAggregateField.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for each {@code field[]} in SCTL aggregate type definitions + */ +public class SctlAggregateField extends Packet { + @PacketField + public long off; + + @PacketField + public SctlAttributes attrs; + + @PacketField + public SctlString id; + + @PacketField + public SelSctlTypeName tname; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlArrayTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlArrayTypeName.java new file mode 100644 index 0000000000..7ccaa383fe --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlArrayTypeName.java @@ -0,0 +1,29 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; + +/** + * The format for array type names in SCTL + */ +public class SctlArrayTypeName extends AbstractSctlTypeName { + @PacketField + public long nelem; + + @PacketField + public SelSctlTypeName tname; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAtom.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAtom.java new file mode 100644 index 0000000000..22f782ebcf --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAtom.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.Packet; + +/** + * A base class for any key or value in a SCTL attribute + */ +public abstract class SctlAtom extends Packet { + // No content +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAttributeKeyVal.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAttributeKeyVal.java new file mode 100644 index 0000000000..1134b33f31 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAttributeKeyVal.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import java.util.Map; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.annot.TypedByField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.consts.Attrval; + +/** + * The format for SCTL key-value pairs in attributes + */ +public class SctlAttributeKeyVal extends Packet { + public static final Map> ATOM_MAP = + typeMap(Attrval.class, SctlAtom.class) // + .put(Attrval.Astr, SctlStringAtom.class) // + .put(Attrval.Acid, SctlSymbolAtom.class) // + .put(Attrval.Auint, SctlUIntAtom.class) // + .build(); + + @PacketField + public Attrval keyKind; + + @PacketField + @TypedByField(by = "keyKind", map = "ATOM_MAP") + public SctlAtom key; + + @PacketField + public Attrval valKind; + + @PacketField + @TypedByField(by = "valKind", map = "ATOM_MAP") + public SctlAtom val; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAttributes.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAttributes.java new file mode 100644 index 0000000000..29934711f6 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlAttributes.java @@ -0,0 +1,43 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; + +/** + * The format for {@code attrs} in SCTL + */ +public class SctlAttributes extends Packet { + public static SctlAttributes empty() { + SctlAttributes attrs = new SctlAttributes(); + attrs.pairs = new ArrayList<>(); + return attrs; + } + + @PacketField + public long na; + + @PacketField + @RepeatedField + @CountedByField("na") + public List pairs; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBaseTypeDefinition.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBaseTypeDefinition.java new file mode 100644 index 0000000000..a146929394 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBaseTypeDefinition.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.consts.Cbase; +import ghidra.dbg.sctl.protocol.consts.Rkind; + +/** + * The format for base type definitions in SCTL + */ +public class SctlBaseTypeDefinition extends AbstractSctlTypeDefinition { + @PacketField + public Cbase base; + + @PacketField + public Rkind rep; + + @Override + public AbstractSctlTypeName getTypeName() { + return new SctlBaseTypeName(base); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBaseTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBaseTypeName.java new file mode 100644 index 0000000000..23a208c171 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBaseTypeName.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.consts.Cbase; + +/** + * The format for base type names in SCTL + */ +public class SctlBaseTypeName extends AbstractSctlTypeName { + public SctlBaseTypeName() { + } + + public SctlBaseTypeName(Cbase base) { + this.base = base; + } + + @PacketField + public Cbase base; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBitfieldTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBitfieldTypeName.java new file mode 100644 index 0000000000..aeb508d4c1 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlBitfieldTypeName.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; + +/** + * The format for bitfield type names in SCTL + */ +public class SctlBitfieldTypeName extends AbstractSctlTypeName { + @PacketField + public byte width; + + @PacketField + public byte pos; + + @PacketField + public SelSctlTypeName tname; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumConstTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumConstTypeName.java new file mode 100644 index 0000000000..c63e96402a --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumConstTypeName.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; + +/** + * The format for enumeration constant type names in SCTL + */ +public class SctlEnumConstTypeName extends AbstractSctlTypeName { + @PacketField + public SelSctlTypeName tname; // name of enum type +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumConstant.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumConstant.java new file mode 100644 index 0000000000..6c4f341aca --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumConstant.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for {@code const[]} in SCTL enumeration type definitions in SCTL + */ +public class SctlEnumConstant extends Packet { + @PacketField + public SctlString id; + + @PacketField + public long value; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumTypeDefinition.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumTypeDefinition.java new file mode 100644 index 0000000000..5f3811c0f3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumTypeDefinition.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; +import ghidra.dbg.sctl.protocol.consts.Rkind; + +/** + * The format for enumerate type definitions in SCTL + */ +public class SctlEnumTypeDefinition extends AbstractSctlTypeDefinition { + @PacketField + public SctlString tag; + + @PacketField + public Rkind rep; + + @PacketField + public long nconst; + + @PacketField + @RepeatedField + @CountedByField("nconst") + public List consts; + + @Override + public AbstractSctlTypeName getTypeName() { + return new SctlEnumTypeName(tag); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumTypeName.java new file mode 100644 index 0000000000..7d059499ec --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlEnumTypeName.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for enumeration type names in SCTL + */ +public class SctlEnumTypeName extends AbstractSctlTaggedTypeName { + public SctlEnumTypeName() { + } + + public SctlEnumTypeName(SctlString tag) { + super(tag); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlFunctionParameter.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlFunctionParameter.java new file mode 100644 index 0000000000..19ff5fb9ff --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlFunctionParameter.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for {@code param[]} of function type names in SCTL + */ +public class SctlFunctionParameter extends Packet { + @PacketField + public SctlAttributes attrs; + + @PacketField + public SctlString pname; + + @PacketField + public SelSctlTypeName tname; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlFunctionTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlFunctionTypeName.java new file mode 100644 index 0000000000..bee5a0ea31 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlFunctionTypeName.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; + +/** + * The format for function type names in SCTL + */ +public class SctlFunctionTypeName extends AbstractSctlTypeName { + @PacketField + public SelSctlTypeName tname; // return type + + @PacketField + public long np; + + @PacketField + @RepeatedField + @CountedByField("np") + public List params; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlPointerTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlPointerTypeName.java new file mode 100644 index 0000000000..66e9103996 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlPointerTypeName.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; + +/** + * The format for pointer type names in SCTL + */ +public class SctlPointerTypeName extends AbstractSctlTypeName { + @PacketField + public SelSctlTypeName tname; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStringAtom.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStringAtom.java new file mode 100644 index 0000000000..649d7d8977 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStringAtom.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * A string key or value in SCTL attributes + */ +public class SctlStringAtom extends SctlAtom { + @PacketField + public SctlString str; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStructTypeDefinition.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStructTypeDefinition.java new file mode 100644 index 0000000000..be25ff31ec --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStructTypeDefinition.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +/** + * The format for a struct type definition in SCTL + */ +public class SctlStructTypeDefinition extends AbstractSctlAggregateTypeDefinition { + @Override + public AbstractSctlTypeName getTypeName() { + return new SctlStructTypeName(tag); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStructTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStructTypeName.java new file mode 100644 index 0000000000..9c8550cbce --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlStructTypeName.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for a struct type name in SCTL + */ +public class SctlStructTypeName extends AbstractSctlTaggedTypeName { + public SctlStructTypeName() { + } + + public SctlStructTypeName(SctlString tag) { + super(tag); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlSymbolAtom.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlSymbolAtom.java new file mode 100644 index 0000000000..1983178157 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlSymbolAtom.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * A symbol key or value in SCTL attributes + */ +public class SctlSymbolAtom extends SctlAtom { + @PacketField + public SctlString sym; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlTypedefTypeDefinition.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlTypedefTypeDefinition.java new file mode 100644 index 0000000000..87adda49e7 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlTypedefTypeDefinition.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for a {@code typedef} type definition in SCTL + */ +public class SctlTypedefTypeDefinition extends AbstractSctlTypeDefinition { + @PacketField + public SctlString tid; + + @PacketField + public SelSctlTypeName tname; + + @Override + public AbstractSctlTypeName getTypeName() { + return new SctlTypedefTypeName(tid); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlTypedefTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlTypedefTypeName.java new file mode 100644 index 0000000000..b89d6b0f9a --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlTypedefTypeName.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for a {@code typedef} type name in SCTL + */ +public class SctlTypedefTypeName extends AbstractSctlTypeName { + public SctlTypedefTypeName() { + } + + public SctlTypedefTypeName(SctlString tid) { + this.tid = tid; + } + + @PacketField + public SctlString tid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUIntAtom.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUIntAtom.java new file mode 100644 index 0000000000..35c37ae297 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUIntAtom.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; + +/** + * A 64-bit numerical key or value in SCTL attributes + */ +public class SctlUIntAtom extends SctlAtom { + @PacketField + public long uint; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUndefinedTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUndefinedTypeName.java new file mode 100644 index 0000000000..894bf7e9d5 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUndefinedTypeName.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.comm.packet.fields.PacketField; + +/** + * The format for an undefined type name in SCTL + */ +public class SctlUndefinedTypeName extends AbstractSctlTypeName { + @PacketField + public SelSctlTypeName tname; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUnionTypeDefinition.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUnionTypeDefinition.java new file mode 100644 index 0000000000..7dedba265e --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUnionTypeDefinition.java @@ -0,0 +1,26 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +/** + * The format for a union type definition in SCTL + */ +public class SctlUnionTypeDefinition extends AbstractSctlAggregateTypeDefinition { + @Override + public AbstractSctlTypeName getTypeName() { + return new SctlUnionTypeName(tag); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUnionTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUnionTypeName.java new file mode 100644 index 0000000000..f5223f0d7c --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SctlUnionTypeName.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for a union type name in SCTL + */ +public class SctlUnionTypeName extends AbstractSctlTaggedTypeName { + public SctlUnionTypeName() { + } + + public SctlUnionTypeName(SctlString tag) { + super(tag); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SelSctlTypeDefinition.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SelSctlTypeDefinition.java new file mode 100644 index 0000000000..5519977117 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SelSctlTypeDefinition.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import java.util.Map; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.annot.TypedByField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.consts.Tkind; + +/** + * The format for {@code tdef[]} in SCTL + * + * This parses {@code Tkind} and selects the appropriate kind of type definition + */ +public class SelSctlTypeDefinition extends Packet { + public static final Map> KIND_MAP = + typeMap(Tkind.class, AbstractSctlTypeDefinition.class) // + .put(Tkind.Tbase, SctlBaseTypeDefinition.class) // 1. base type + .put(Tkind.Tstruct, SctlStructTypeDefinition.class) // 2. aggregate types + .put(Tkind.Tunion, SctlUnionTypeDefinition.class) // 2. ... + .put(Tkind.Tenum, SctlEnumTypeDefinition.class) // 3. enum type + .put(Tkind.Ttypedef, SctlTypedefTypeDefinition.class) // 4. typedef type + .build(); + + @PacketField + public Tkind kind; + + @PacketField + @TypedByField(by = "kind", map = "KIND_MAP") + public AbstractSctlTypeDefinition sel; + + public AbstractSctlTypeName getTypeName() { + return sel.getTypeName(); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SelSctlTypeName.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SelSctlTypeName.java new file mode 100644 index 0000000000..8422dd5427 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/types/SelSctlTypeName.java @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.types; + +import java.util.Map; + +import ghidra.comm.packet.Packet; +import ghidra.comm.packet.annot.TypedByField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.consts.Tkind; + +/** + * The format for {@code tname} in SCTL + * + * This parses {@code Tkind} and selects the appropriate kind of type name + */ +public class SelSctlTypeName extends Packet { + public SelSctlTypeName() { + } + + public SelSctlTypeName(AbstractSctlTypeName sel) { + this.sel = sel; + } + + public static final Map> KIND_MAP = + typeMap(Tkind.class, AbstractSctlTypeName.class) // + .put(Tkind.Tbase, SctlBaseTypeName.class) // 1. base type + .put(Tkind.Tstruct, SctlStructTypeName.class) // 2. tagged type + .put(Tkind.Tunion, SctlUnionTypeName.class) // 2. ... + .put(Tkind.Tenum, SctlEnumTypeName.class) // 2. ... + .put(Tkind.Ttypedef, SctlTypedefTypeName.class) // 3. typedef type + .put(Tkind.Tptr, SctlPointerTypeName.class) // 4. pointer type + .put(Tkind.Tarr, SctlArrayTypeName.class) // 5. array type + .put(Tkind.Tfun, SctlFunctionTypeName.class) // 6. function type + .put(Tkind.Tbitfield, SctlBitfieldTypeName.class) // 7. bitfield type + .put(Tkind.Tconst, SctlEnumConstTypeName.class) // 8. enumeration constant type + .put(Tkind.Tundef, SctlUndefinedTypeName.class) // 9. undefined + .build(); + + @PacketField + public Tkind kind; + + @PacketField + @TypedByField(by = "kind", map = "KIND_MAP") + public AbstractSctlTypeName sel; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012AttachReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012AttachReply.java new file mode 100644 index 0000000000..cce85f3975 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012AttachReply.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base; + +import ghidra.dbg.sctl.protocol.common.reply.AbstractSctlAttachReply; + +public class Sctl2012AttachReply extends AbstractSctlAttachReply { + @Override + public boolean supportsPlatform() { + return false; + } + + @Override + public void setPlatform(String platform) { + throw new UnsupportedOperationException(); + } + + @Override + public String getPlatform() { + throw new UnsupportedOperationException(); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012ForkNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012ForkNotification.java new file mode 100644 index 0000000000..4923b24b26 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012ForkNotification.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base; + +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; +import ghidra.dbg.sctl.protocol.common.notify.AbstractSctlForkNotification; + +/** + * Format for the {@code Efork} SCTL event + */ +public class Sctl2012ForkNotification extends AbstractSctlForkNotification { + public Sctl2012ForkNotification() { + super(); + } + + public Sctl2012ForkNotification(long spwnid, AbstractSctlContext ctx, + AbstractSctlContext spwnctx) { + super(spwnid, ctx, spwnctx); + } + + @Override + public boolean supportsProcessID() { + return false; + } + + @Override + public void setProcessID(long pid) { + throw new UnsupportedOperationException(); + } + + @Override + public long getProcessID() { + throw new UnsupportedOperationException(); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012LaunchReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012LaunchReply.java new file mode 100644 index 0000000000..0602fd3b96 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012LaunchReply.java @@ -0,0 +1,53 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base; + +import ghidra.dbg.sctl.protocol.common.reply.AbstractSctlLaunchReply; + +/** + * Format for the {@code Rlaunch} SCTL message + */ +public class Sctl2012LaunchReply extends AbstractSctlLaunchReply { + @Override + public boolean supportsProcessID() { + return false; + } + + @Override + public void setProcessID(long pid) { + throw new UnsupportedOperationException(); + } + + @Override + public long getProcessID() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean supportsPlatform() { + return false; + } + + @Override + public void setPlatform(String platform) { + throw new UnsupportedOperationException(); + } + + @Override + public String getPlatform() { + throw new UnsupportedOperationException(); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012SnapNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012SnapNotification.java new file mode 100644 index 0000000000..ccb8a2ee00 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012SnapNotification.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base; + +import ghidra.dbg.sctl.protocol.common.notify.AbstractSctlSnapNotification; + +/** + * Format for the {@code Esnap} SCTL event + */ +public class Sctl2012SnapNotification extends AbstractSctlSnapNotification { + @Override + public boolean supportsProcessID() { + return false; + } + + @Override + public void setProcessID(long pid) { + throw new UnsupportedOperationException(); + } + + @Override + public long getProcessID() { + throw new UnsupportedOperationException(); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012SnapshotReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012SnapshotReply.java new file mode 100644 index 0000000000..0d4f18d11a --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/Sctl2012SnapshotReply.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base; + +import ghidra.dbg.sctl.protocol.common.reply.AbstractSctlSnapshotReply; + +public class Sctl2012SnapshotReply extends AbstractSctlSnapshotReply { + @Override + public boolean supportsProcessID() { + return false; + } + + @Override + public void setProcessID(long pid) { + throw new UnsupportedOperationException(); + } + + @Override + public long getProcessID() { + throw new UnsupportedOperationException(); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/SelSctl2012Packet.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/SelSctl2012Packet.java new file mode 100644 index 0000000000..3394fc98e6 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/SelSctl2012Packet.java @@ -0,0 +1,98 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base; + +import java.util.Map; + +import ghidra.dbg.sctl.dialect.SelSctlNullDialectPacket; +import ghidra.dbg.sctl.protocol.AbstractSelSctlPacket; +import ghidra.dbg.sctl.protocol.SctlPacket; +import ghidra.dbg.sctl.protocol.common.notify.SctlEventNotify; +import ghidra.dbg.sctl.protocol.common.reply.*; +import ghidra.dbg.sctl.protocol.common.request.*; +import ghidra.dbg.sctl.protocol.consts.Mkind; + +public class SelSctl2012Packet extends AbstractSelSctlPacket { + public SelSctl2012Packet() { + super(); + } + + public SelSctl2012Packet(int tag, SctlPacket sel) { + super(tag, sel); + } + + public static final Map> METHOD_MAP = + typeMap(Mkind.class, SctlPacket.class) // + .putAll(SelSctlNullDialectPacket.METHOD_MAP) // + .put(Mkind.Aevent, SctlEventNotify.class) // + .put(Mkind.Tps, SctlProcessListRequest.class) // + .put(Mkind.Rps, SctlProcessListReply.class) // + .put(Mkind.Tlaunch, SctlLaunchRequest.class) // + .put(Mkind.Rlaunch, AbstractSctlLaunchReply.class) // + .put(Mkind.Tattach, SctlAttachRequest.class) // + .put(Mkind.Rattach, AbstractSctlAttachReply.class) // + .put(Mkind.Tstat, SctlStatusRequest.class) // + .put(Mkind.Rstat, SctlStatusReply.class) // + .put(Mkind.Tcont, SctlContinueRequest.class) // + .put(Mkind.Rcont, SctlContinueReply.class) // + .put(Mkind.Tstop, SctlStopRequest.class) // + .put(Mkind.Rstop, SctlStopReply.class) // + .put(Mkind.Tstep, SctlStepRequest.class) // + .put(Mkind.Rstep, SctlStepReply.class) // + .put(Mkind.Tsnap, SctlSnapshotRequest.class) // + .put(Mkind.Rsnap, AbstractSctlSnapshotReply.class) // + .put(Mkind.Tkill, SctlKillRequest.class) // + .put(Mkind.Rkill, SctlKillReply.class) // + .put(Mkind.Tdetach, SctlDetachRequest.class) // + .put(Mkind.Rdetach, SctlDetachReply.class) // + .put(Mkind.Ttrace, SctlTraceRequest.class) // + .put(Mkind.Rtrace, SctlTraceReply.class) // + .put(Mkind.Tsettrap, SctlSetTrapRequest.class) // + .put(Mkind.Rsettrap, SctlSetTrapReply.class) // + .put(Mkind.Tclrtrap, SctlClearTrapRequest.class) // + .put(Mkind.Rclrtrap, SctlClearTrapReply.class) // + .put(Mkind.Tgetctx, SctlGetContextRequest.class) // + .put(Mkind.Rgetctx, SctlGetContextReply.class) // + .put(Mkind.Tsetctx, SctlSetContextRequest.class) // + .put(Mkind.Rsetctx, SctlSetContextReply.class) // + .put(Mkind.Tread, SctlReadRequest.class) // + .put(Mkind.Rread, SctlReadReply.class) // + .put(Mkind.Twrite, SctlWriteRequest.class) // + .put(Mkind.Rwrite, SctlWriteReply.class) // + .put(Mkind.Tlooksym, SctlLookupSymbolRequest.class) // + .put(Mkind.Rlooksym, SctlLookupSymbolReply.class) // + .put(Mkind.Tenumsym, SctlEnumerateSymbolsRequest.class) // + .put(Mkind.Renumsym, SctlEnumerateSymbolsReply.class) // + .put(Mkind.Tlooktype, SctlLookupTypeRequest.class) // + .put(Mkind.Rlooktype, SctlLookupTypeReply.class) // + .put(Mkind.Tenumtype, SctlEnumerateTypesRequest.class) // + .put(Mkind.Renumtype, SctlEnumerateTypesReply.class) // + .put(Mkind.Tlookaddr, SctlLookupAddressRequest.class) // + .put(Mkind.Rlookaddr, SctlLookupAddressReply.class) // + .put(Mkind.Tenumloc, SctlEnumerateLocalsRequest.class) // + .put(Mkind.Renumloc, SctlEnumerateLocalsReply.class) // + .put(Mkind.Tenumseg, SctlEnumerateSegmentsRequest.class) // + .put(Mkind.Renumseg, SctlEnumerateSegmentsReply.class) // + .put(Mkind.Tnames, SctlNamesRequest.class) // + .put(Mkind.Rnames, SctlNamesReply.class) // + .put(Mkind.Tunwind1, SctlUnwindOneFrameRequest.class) // + .put(Mkind.Runwind1, SctlUnwindOneFrameReply.class) // + .put(Mkind.Tlooksrc, SctlLookupSourceRequest.class) // + .put(Mkind.Rlooksrc, SctlLookupSourceReply.class) // + .put(Mkind.Tlookpc, SctlLookupProgramCounterRequest.class) // + .put(Mkind.Rlookpc, SctlLookupProgramCounterReply.class) // + .build(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/SctlX86Context.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/SctlX86Context.java new file mode 100644 index 0000000000..585020d169 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/SctlX86Context.java @@ -0,0 +1,87 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base.x86; + +import java.util.List; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractByLongFieldsSctlContext; +import ghidra.dbg.sctl.protocol.common.SctlRegisterDefinition; + +/** + * The context packet for SCTL's x86 dialect + */ +public class SctlX86Context extends AbstractByLongFieldsSctlContext { + @PacketField + public long r15; + @PacketField + public long r14; + @PacketField + public long r13; + @PacketField + public long r12; + @PacketField + public long rbp; + @PacketField + public long rbx; + @PacketField + public long r11; + @PacketField + public long r10; + @PacketField + public long r9; + @PacketField + public long r8; + @PacketField + public long rax; + @PacketField + public long rcx; + @PacketField + public long rdx; + @PacketField + public long rsi; + @PacketField + public long rdi; + @PacketField + public long orig_rax; + @PacketField + public long rip; + @PacketField + public long cs; + @PacketField + public long eflags; + @PacketField + public long rsp; + @PacketField + public long ss; + @PacketField + public long fs_base; + @PacketField + public long gs_base; + @PacketField + public long ds; + @PacketField + public long es; + @PacketField + public long fs; + @PacketField + public long gs; + + @Override + public void setSelectedRegisters(List regdefs) { + // Has no effect + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxBinary.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxBinary.java new file mode 100644 index 0000000000..8fc8b708c2 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxBinary.java @@ -0,0 +1,98 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base.x86.linux; + +import java.util.List; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.*; + +/** + * Format of SCTL's {@code bin} for Linux dialects + */ +public class Sctl2012LinuxBinary extends AbstractSctlBinary { + @Override + public void setNamespaceID(long nsid) { + this.nsid = nsid; + } + + @Override + public long getNamespaceID() { + return nsid; + } + + @Override + public void setPath(String path) { + this.path = new SctlString(path); + } + + @Override + public String getPath() { + return path.str; + } + + @Override + public void setExecutable(boolean isexe) { + this.isexe = isexe; + } + + @Override + public boolean isExecutable() { + return isexe; + } + + @Override + public boolean supportsBase() { + return true; + } + + @Override + public void setBase(long base) { + this.base = base; + } + + @Override + public long getBase() { + return base; + } + + @Override + public boolean supportsSections() { + return false; + } + + @Override + public AbstractSctlSection addSection() { + throw new UnsupportedOperationException(); + } + + @Override + public List getSections() { + throw new UnsupportedOperationException(); + } + + @PacketField + public long nsid; + + @PacketField + public long base; + + @PacketField + public boolean isexe; + + @PacketField + public SctlString path; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxProcessEntry.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxProcessEntry.java new file mode 100644 index 0000000000..cce00a0791 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxProcessEntry.java @@ -0,0 +1,76 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base.x86.linux; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlProcessEntry; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for each process of {@code bytes[]} of SCTL's {@code Rps} reply for Linux dialects + */ +public class Sctl2012LinuxProcessEntry extends AbstractSctlProcessEntry { + @PacketField + public long pid; + + @PacketField + public SctlString cmd; + + @PacketField + public long nt; + + @PacketField + @RepeatedField + @CountedByField("nt") + public List threads = new ArrayList<>(); + + @Override + public long getProcessID() { + return pid; + } + + @Override + public void setProcessID(long pid) { + this.pid = pid; + } + + @Override + public String getCommand() { + return cmd.str; + } + + @Override + public void setCommand(String cmd) { + this.cmd = new SctlString(cmd); + } + + @Override + public List getThreads() { + return threads; + } + + @Override + public Sctl2012LinuxThreadEntry addThread() { + Sctl2012LinuxThreadEntry t = new Sctl2012LinuxThreadEntry(); + threads.add(t); + return t; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxProcessList.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxProcessList.java new file mode 100644 index 0000000000..b2038028df --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxProcessList.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base.x86.linux; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlProcessEntry; +import ghidra.dbg.sctl.protocol.common.AbstractSctlProcessList; + +/** + * The format for {@code bytes[]} of SCTL's {@code Rps} reply for Linux dialects + */ +public class Sctl2012LinuxProcessList extends AbstractSctlProcessList { + @PacketField + public long ntarg; + + @PacketField + @RepeatedField + @CountedByField("ntarg") + public List processes = new ArrayList<>(); + + @Override + public List getProcesses() { + return processes; + } + + @Override + public Sctl2012LinuxProcessEntry addProcess() { + Sctl2012LinuxProcessEntry proc = new Sctl2012LinuxProcessEntry(); + processes.add(proc); + return proc; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxRegion.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxRegion.java new file mode 100644 index 0000000000..b25df2dd68 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxRegion.java @@ -0,0 +1,81 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base.x86.linux; + +import ghidra.comm.packet.annot.BitmaskEncoded; +import ghidra.comm.packet.fields.PacketField; +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.sctl.client.SctlMemoryProtection; +import ghidra.dbg.sctl.protocol.common.AbstractSctlRegion; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format of {@code region} in SCTL's Linux dialects + */ +public class Sctl2012LinuxRegion extends AbstractSctlRegion { + @Override + public void setName(String name) { + this.file = new SctlString(name); + } + + @Override + public String getName() { + return file.str; + } + + @Override + public void setAddress(long address) { + this.addr = address; + } + + @Override + public long getAddress() { + return addr; + } + + @Override + public void setLength(long length) { + this.len = length; + } + + @Override + public long getLength() { + return len; + } + + @Override + public void setProtections(BitmaskSet flags) { + this.flags = flags; + } + + @Override + public BitmaskSet getProtections() { + return flags; + } + + @PacketField + public SctlString file; + + @PacketField + public long addr; + + @PacketField + public long len; + + @PacketField + @BitmaskEncoded(universe = SctlMemoryProtection.class) + public BitmaskSet flags; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxStatus.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxStatus.java new file mode 100644 index 0000000000..3ba1b59825 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxStatus.java @@ -0,0 +1,87 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base.x86.linux; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlStatus; + +/** + * The format of {@code stat[]} in SCTL's Linux dialects + */ +public class Sctl2012LinuxStatus extends AbstractSctlStatus { + @Override + public boolean supportsProcessID() { + return true; + } + + @Override + public void setProcessID(long pid) { + this.pid = pid; + } + + @Override + public long getProcessID() { + return pid; + } + + @Override + public Sctl2012LinuxRegion addRegion() { + Sctl2012LinuxRegion r = new Sctl2012LinuxRegion(); + regions.add(r); + return r; + } + + @Override + public List getRegions() { + return regions; + } + + @Override + public Sctl2012LinuxBinary addBinary() { + Sctl2012LinuxBinary b = new Sctl2012LinuxBinary(); + bins.add(b); + return b; + } + + @Override + public List getBinaries() { + return bins; + } + + @PacketField + public long pid; + + @PacketField + public long nr; + + @PacketField + @RepeatedField + @CountedByField("nr") + public List regions = new ArrayList<>(); + + @PacketField + public long nb; + + @PacketField + @RepeatedField + @CountedByField("nb") + public List bins = new ArrayList<>(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxThreadEntry.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxThreadEntry.java new file mode 100644 index 0000000000..00c5991ca5 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxThreadEntry.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base.x86.linux; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlThreadEntry; + +/** + * The format for each thread in SCTL's {@code Rps} for Linux dialects + */ +public class Sctl2012LinuxThreadEntry extends AbstractSctlThreadEntry { + @PacketField + public long tid; + + @Override + public long getThreadID() { + return tid; + } + + @Override + public void setThreadID(long tid) { + this.tid = tid; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxTrapSpec.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxTrapSpec.java new file mode 100644 index 0000000000..5a6c83fa63 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxTrapSpec.java @@ -0,0 +1,141 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base.x86.linux; + +import ghidra.comm.packet.annot.BitmaskEncoded; +import ghidra.comm.packet.fields.PacketField; +import ghidra.comm.util.BitmaskSet; +import ghidra.comm.util.BitmaskUniverse; +import ghidra.dbg.sctl.protocol.common.AbstractSctlTrapSpec; + +/** + * The format of {@code bytes[]} in SCTL's {@code Tsettrap} for Linux dialects + */ +public class Sctl2012LinuxTrapSpec extends AbstractSctlTrapSpec { + public enum Flag implements BitmaskUniverse { + ACTION_STOP(0), ACTION_SNAP(1 << 0); + + private final long mask; + + Flag(long mask) { + this.mask = mask; + } + + @Override + public long getMask() { + return mask; + } + } + + public Sctl2012LinuxTrapSpec() { + this.mode = BitmaskSet.of(); + } + + public Sctl2012LinuxTrapSpec(BitmaskSet mode, long addr) { + this.mode = mode; + this.addr = addr; + } + + @Override + public void setAddress(long address) { + this.addr = address; + } + + @Override + public long getAddress() { + return addr; + } + + @Override + public void setLength(long length) { + if (length == 1) { + // Yeah + return; + } + throw new UnsupportedOperationException(); + } + + @Override + public long getLength() { + return 1; + } + + @Override + public void setActionStop() { + mode.remove(Flag.ACTION_SNAP); + } + + @Override + public boolean isActionStop() { + return !mode.contains(Flag.ACTION_SNAP); + } + + @Override + public void setActionSnap() { + mode.add(Flag.ACTION_SNAP); + } + + @Override + public boolean isActionSnap() { + return mode.contains(Flag.ACTION_SNAP); + } + + @Override + public void setSoftwareExecute() { + // Yeah + } + + @Override + public boolean isSoftwareExecute() { + return true; + } + + @Override + public void setHardware(boolean read, boolean write, boolean execute) { + if (!read && !write && !execute) { + // Yeah + return; + } + throw new UnsupportedOperationException(); + } + + @Override + public boolean isHardware() { + return false; + } + + @Override + public boolean isHardwareRead() { + return false; + } + + @Override + public boolean isHardwareWrite() { + return false; + } + + @Override + public boolean isHardwareExecute() { + return false; + } + + @PacketField + @BitmaskEncoded(universe = Flag.class) + public BitmaskSet mode; + + @PacketField + public long addr; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxX86Dialect.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxX86Dialect.java new file mode 100644 index 0000000000..f36e687583 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/linux/Sctl2012LinuxX86Dialect.java @@ -0,0 +1,93 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base.x86.linux; + +import java.util.Set; + +import ghidra.comm.packet.AbstractPacketFactory; +import ghidra.comm.packet.PacketFactory; +import ghidra.dbg.sctl.dialect.SctlDialect; +import ghidra.dbg.sctl.protocol.AbstractSelSctlPacket; +import ghidra.dbg.sctl.protocol.common.*; +import ghidra.dbg.sctl.protocol.common.notify.AbstractSctlForkNotification; +import ghidra.dbg.sctl.protocol.common.notify.AbstractSctlSnapNotification; +import ghidra.dbg.sctl.protocol.common.reply.*; +import ghidra.dbg.sctl.protocol.consts.Evkind; +import ghidra.dbg.sctl.protocol.v2012base.*; +import ghidra.dbg.sctl.protocol.v2012base.x86.SctlX86Context; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; + +/** + * The "x86-linux-2012" SCTL dialect + */ +public enum Sctl2012LinuxX86Dialect implements SctlDialect { + INSTANCE; + + public static class Sctl2012LinuxX86PacketFactory extends AbstractPacketFactory { + { + useFor(AbstractSelSctlPacket.class, SelSctl2012Packet.class); + + useFor(AbstractSctlAttachReply.class, Sctl2012AttachReply.class); + useFor(AbstractSctlLaunchReply.class, Sctl2012LaunchReply.class); + useFor(AbstractSctlSnapshotReply.class, Sctl2012SnapshotReply.class); + + useFor(AbstractSctlForkNotification.class, Sctl2012ForkNotification.class); + useFor(AbstractSctlSnapNotification.class, Sctl2012SnapNotification.class); + + useFor(AbstractSctlContext.class, SctlX86Context.class); + useFor(AbstractSctlProcessList.class, Sctl2012LinuxProcessList.class); + useFor(AbstractSctlStatus.class, Sctl2012LinuxStatus.class); + useFor(AbstractSctlTrapSpec.class, Sctl2012LinuxTrapSpec.class); + } + } + + public static final String SYS_VERSION = "x86-linux-2012"; + public static final String PLATFORM = "linux-x86_64"; + private static final PacketFactory FACTORY = new Sctl2012LinuxX86PacketFactory(); + + @Override + public String getSysVersion() { + return SYS_VERSION; + } + + @Override + public PacketFactory getPacketFactory() { + return FACTORY; + } + + @Override + public boolean isBusSupported() { + return false; + } + + @Override + public boolean isRegisterSelectionSupported() { + return false; + } + + @Override + public TargetExecutionState stateAfterEvent(Set events) { + if (events.contains(Evkind.Eexit)) { + return TargetExecutionState.TERMINATED; + } + return TargetExecutionState.STOPPED; + } + + @Override + public String getSolePlatform() { + return PLATFORM; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/win/Sctl2012WindowsX86Context.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/win/Sctl2012WindowsX86Context.java new file mode 100644 index 0000000000..fcdf8d90cd --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/win/Sctl2012WindowsX86Context.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base.x86.win; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.v2012base.x86.SctlX86Context; + +/** + * The context packet for SCTL's x86-win dialect + */ +public class Sctl2012WindowsX86Context extends SctlX86Context { + @PacketField(after = "gs") + public long sig; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/win/Sctl2012WindowsX86Dialect.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/win/Sctl2012WindowsX86Dialect.java new file mode 100644 index 0000000000..d3f7ebf0a4 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012base/x86/win/Sctl2012WindowsX86Dialect.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012base.x86.win; + +import java.util.Set; + +import ghidra.comm.packet.AbstractPacketFactory; +import ghidra.comm.packet.PacketFactory; +import ghidra.dbg.sctl.dialect.SctlDialect; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; +import ghidra.dbg.sctl.protocol.consts.Evkind; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; + +/** + * The "x86-win-2012" SCTL dialect + * + * Note that support for Windows was nascent when the specification was implemented. In many cases, + * the packet formats are the same as those from Linux. In other cases, the specs say things are + * undefined, but the reference stub behaves as it does on Linux, anyway. Reasonable guesses are + * implemented here, but this client has not been tested with the official SCTL process controller + * for Windows. + */ +public enum Sctl2012WindowsX86Dialect implements SctlDialect { + INSTANCE; + + public static class Sctl2012WindowsX86PacketFactory extends AbstractPacketFactory { + { + useFor(AbstractSctlContext.class, Sctl2012WindowsX86Context.class); + } + } + + public static final String SYS_VERSION = "x86-win-2012"; + public static final String PLATFORM = "win-x64"; + private static final PacketFactory FACTORY = new Sctl2012WindowsX86PacketFactory(); + + @Override + public String getSysVersion() { + return SYS_VERSION; + } + + @Override + public PacketFactory getPacketFactory() { + return FACTORY; + } + + @Override + public boolean isBusSupported() { + return false; + } + + @Override + public boolean isRegisterSelectionSupported() { + return false; + } + + @Override + public TargetExecutionState stateAfterEvent(Set events) { + if (events.contains(Evkind.Efork)) { + return TargetExecutionState.RUNNING; + } + if (events.contains(Evkind.Eclone)) { + return TargetExecutionState.RUNNING; + } + return TargetExecutionState.STOPPED; + } + + @Override + public String getSolePlatform() { + return PLATFORM; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SctlExecuteReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SctlExecuteReply.java new file mode 100644 index 0000000000..9e7493651a --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SctlExecuteReply.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012ext; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.client.SctlExtension; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * Format for the "{@code Rexec}" SCTL message + * + * There is no {@code Texec} or {@code Rexec} in the official SCTL protocol. It is an extension for + * Ghidra to issue CLI commands to a connected interactive debugger. + */ +@SctlExtension("Response to CLI command in remote debugger") +public class SctlExecuteReply extends AbstractSctlReply { + public SctlExecuteReply() { + } + + public SctlExecuteReply(String out) { + this.out = new SctlString(out); + } + + @PacketField + public SctlString out; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SctlExecuteRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SctlExecuteRequest.java new file mode 100644 index 0000000000..d00ad063b0 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SctlExecuteRequest.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012ext; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.client.SctlExtension; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * Format for the "{@code Texec}" SCTL message + * + * There is no {@code Texec} or {@code Rexec} in the official SCTL protocol. It is an extension for + * Ghidra to issue CLI commands to a connected interactive debugger. + */ +@SctlExtension("Execute a CLI command in the remote debugger") +public class SctlExecuteRequest extends AbstractSctlRequest { + public SctlExecuteRequest() { + } + + public SctlExecuteRequest(String cmd) { + this.cmd = new SctlString(cmd); + } + + @PacketField + public SctlString cmd; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SelSctl2012ExtPacket.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SelSctl2012ExtPacket.java new file mode 100644 index 0000000000..2117b843c3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/SelSctl2012ExtPacket.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012ext; + +import java.util.Map; + +import ghidra.dbg.sctl.protocol.AbstractSelSctlPacket; +import ghidra.dbg.sctl.protocol.SctlPacket; +import ghidra.dbg.sctl.protocol.consts.Mkind; +import ghidra.dbg.sctl.protocol.v2012base.SelSctl2012Packet; + +public class SelSctl2012ExtPacket extends AbstractSelSctlPacket { + public SelSctl2012ExtPacket() { + super(); + } + + public SelSctl2012ExtPacket(int tag, SctlPacket sel) { + super(tag, sel); + } + + public static final Map> METHOD_MAP = + typeMap(Mkind.class, SctlPacket.class) // + .putAll(SelSctl2012Packet.METHOD_MAP) // + .put(Mkind.Texec, SctlExecuteRequest.class) // + .put(Mkind.Rexec, SctlExecuteReply.class) // + .build(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/x86/linux/Sctl2012ExtLinuxX86Dialect.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/x86/linux/Sctl2012ExtLinuxX86Dialect.java new file mode 100644 index 0000000000..9f5e764f9a --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/x86/linux/Sctl2012ExtLinuxX86Dialect.java @@ -0,0 +1,165 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012ext.x86.linux; + +import java.util.Set; + +import ghidra.comm.packet.PacketFactory; +import ghidra.dbg.sctl.client.SctlExtension; +import ghidra.dbg.sctl.dialect.SctlDialect; +import ghidra.dbg.sctl.protocol.AbstractSelSctlPacket; +import ghidra.dbg.sctl.protocol.consts.Evkind; +import ghidra.dbg.sctl.protocol.v2012base.x86.linux.Sctl2012LinuxX86Dialect; +import ghidra.dbg.sctl.protocol.v2012base.x86.linux.Sctl2012LinuxX86Dialect.Sctl2012LinuxX86PacketFactory; +import ghidra.dbg.sctl.protocol.v2012ext.SelSctl2012ExtPacket; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; + +/** + * "Bus" extension to SCTL's Linux x86 2012 dialect. + * + * SCTL-Bus is an extension (read "hack") added by Ghidra's implementation of SCTL. Since SCTL was + * designed to give L1 complete and exclusive control of a remote process, asynchronous + * notifications were only required for execution events. The "Bus" variant allows multiple + * controllers to cooperate and stay in sync. The controller must now also listen for requests from + * other controllers as well as for the responses those requests generate. + * + * To distinguish each controller, the tag field is structured as follows: + * + *

+ *   31-24            23-0
+ * +--------+------------------------+
+ * |   ID   |          Tag           |
+ * +--------+------------------------+
+ * 
+ * + * Where ID (the highest-order byte) identifies the controller and the remaining bits uniquely + * identify the request. Each client should be assigned its ID statically. No method of dynamic + * selection has been explored, though random selection may be available in future versions. + * + * The extension provides more flexibility in debugger connections than is immediately apparent. + * First, the setup is backward compatible with non-bus SCTL. A client can offer both variants, and + * a non-bus SCTL server ought to select the non-bus variant. Furthermore, a bus-enabled stub can + * agree to a non-bus controller without any changes in protocol, except to give the controller + * exclusive access to the bus. + * + * Second, a non-bus stub need not be aware that multiple controllers are present. A proxy bus can + * be placed in front of the stub. It need only negotiate the versions. So long as all controllers + * speak the same version, it can just forward all packets on all connections -- as a bus should. + * + * Third, for stubs that operate by integrating a host debugger, the stub may pretend the debugger's + * UI is another controller on the bus. This enables it to keep the connected controller(s) in sync + * with commands issued directly in the debugger's UI. + * + * For terminology, consider the typical example setup for debugging on Linux with Ghidra: + * + *
+ * +--------+      +------+            +-----+        +-----------+
+ * | Ghidra |-SCTL-| stub |-pty:GDB/MI-| gdb |-ptrace-| /bin/echo |
+ * +--------+      +------+            +-----+        +-----------+
+ * 
+ * + * Ghidra, a.k.a, the Ghidra debugger, is acting as the SCTL client. gdb is the external or remote + * debugger. A custom SCTL stub controls gdb via a pty with GDB/MI. As an example, + * /bin/echo is the program image for the target process. + * + * In this setup, Ghidra does not have exclusive control of the target process. Ghidra cooperates + * with the external debugger, so there is a need to communicate to Ghidra control commands issued + * by the user to the external debugger. In this case, the stub should prefer to use SCTL's defined + * notification commands where applicable. For example, if a process forks, the stub ought to issue + * the fork notification rather than synthesizing a launch command. When the stub synthesizes a + * control command, it must also synthesize the successful reply. It would synthesize these commands + * using incrementing tags and a bus ID different than the client's ID. + * + * The requests that may be synthesized by the stub include: + *
    + *
  • launch - to notify the clients of a launched process, excluding those created by fork + * or clone, since those already have defined notifications.
  • + *
  • attach - to notify the clients of an attached process, excluding those created by fork + * or clone, since those already have defined notifications.
  • + *
  • step - to notify the clients that a step was issued externally.
  • + *
  • stop - to notify the clients that a stop was issued externally.
  • + *
  • cont - to notify the clients that a continue was issued externally.
  • + *
  • kill - to notify the clients that a kill was issued externally. This need only be sent + * if a process is explicitly terminated by the debugger. For processes that exit in the usual way, + * use the usual SCTL notify message.
  • + *
  • detach - to notify the clients that a detach was issued externally. This need only be + * sent if the user explicitly detached from a process. For processes that exit in the usual way, + * use the usual SCTL notify message.
  • + *
  • settrap - to notify the clients that the user created a breakpoint.
  • + *
  • clrtrap - to notify the clients that the user deleted a breakpoint.
  • + *
  • setctx - to notify the clients that the user modified one or more registers.
  • + *
+ * + * These rules are designed to prevent a stub from giving a client any false impressions. They need + * not be followed precisely. For example, if a stop is implemented by sending the target SIGINT, + * then it is acceptable for the stub to send a signal notification even if the stop was requested + * from the debugger's UI. + * + * If bus mode is available, the stub ought to sync the client to its current state. If a proxy is + * used, and it keeps a log, it could replay it on any new channel. If it is smart, it can cull that + * log to eliminate messages that are no longer relevant to the current state. A stub that is also a + * controller ought to synthesize the necessary control commands upon connection. + */ +@SctlExtension("Bus or bidirectional requests") +public enum Sctl2012ExtLinuxX86Dialect implements SctlDialect { + INSTANCE; + + public static class Sctl2012BusLinuxX86PacketFactory extends Sctl2012LinuxX86PacketFactory { + { + useFor(AbstractSelSctlPacket.class, SelSctl2012ExtPacket.class); + } + } + + public static final String SYS_VERSION = Sctl2012LinuxX86Dialect.SYS_VERSION + "-bus"; + public static final String PLATFORM = Sctl2012LinuxX86Dialect.PLATFORM; + private static final PacketFactory FACTORY = new Sctl2012BusLinuxX86PacketFactory(); + + @Override + public String getSysVersion() { + return SYS_VERSION; + } + + @Override + public PacketFactory getPacketFactory() { + return FACTORY; + } + + @Override + public boolean isBusSupported() { + return true; + } + + @Override + public boolean isRegisterSelectionSupported() { + return false; + } + + @Override + public TargetExecutionState stateAfterEvent(Set events) { + if (events.contains(Evkind.Erunning)) { + return TargetExecutionState.RUNNING; + } + if (events.contains(Evkind.Estopped)) { + return TargetExecutionState.STOPPED; + } + return Sctl2012LinuxX86Dialect.INSTANCE.stateAfterEvent(events); + } + + @Override + public String getSolePlatform() { + return PLATFORM; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/x86/win/Sctl2012ExtWindowsX86Dialect.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/x86/win/Sctl2012ExtWindowsX86Dialect.java new file mode 100644 index 0000000000..ae18264324 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2012ext/x86/win/Sctl2012ExtWindowsX86Dialect.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2012ext.x86.win; + +import java.util.Set; + +import ghidra.comm.packet.PacketFactory; +import ghidra.dbg.sctl.client.SctlExtension; +import ghidra.dbg.sctl.dialect.SctlDialect; +import ghidra.dbg.sctl.protocol.AbstractSelSctlPacket; +import ghidra.dbg.sctl.protocol.consts.Evkind; +import ghidra.dbg.sctl.protocol.v2012base.x86.win.Sctl2012WindowsX86Dialect; +import ghidra.dbg.sctl.protocol.v2012base.x86.win.Sctl2012WindowsX86Dialect.Sctl2012WindowsX86PacketFactory; +import ghidra.dbg.sctl.protocol.v2012ext.SelSctl2012ExtPacket; +import ghidra.dbg.sctl.protocol.v2012ext.x86.linux.Sctl2012ExtLinuxX86Dialect; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; + +/** + * "Bidirectional" extension to the Windows x86 2012 SCTL dialect. + * + * @see Sctl2012ExtLinuxX86Dialect + */ +@SctlExtension("Bus or bidirectional requests") +public enum Sctl2012ExtWindowsX86Dialect implements SctlDialect { + INSTANCE; + + public static class Sctl2012BusWindowsX86PacketFactory extends Sctl2012WindowsX86PacketFactory { + { + useFor(AbstractSelSctlPacket.class, SelSctl2012ExtPacket.class); + } + } + + public static final String SYS_VERSION = Sctl2012WindowsX86Dialect.SYS_VERSION + "-bus"; + public static final String PLATFORM = Sctl2012WindowsX86Dialect.PLATFORM; + private static final PacketFactory FACTORY = new Sctl2012BusWindowsX86PacketFactory(); + + @Override + public String getSysVersion() { + return SYS_VERSION; + } + + @Override + public PacketFactory getPacketFactory() { + return FACTORY; + } + + @Override + public boolean isBusSupported() { + return true; + } + + @Override + public boolean isRegisterSelectionSupported() { + return false; + } + + @Override + public TargetExecutionState stateAfterEvent(Set events) { + if (events.contains(Evkind.Erunning)) { + return TargetExecutionState.RUNNING; + } + if (events.contains(Evkind.Estopped)) { + return TargetExecutionState.STOPPED; + } + return Sctl2012WindowsX86Dialect.INSTANCE.stateAfterEvent(events); + } + + @Override + public String getSolePlatform() { + return PLATFORM; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018AttachReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018AttachReply.java new file mode 100644 index 0000000000..933a108bb4 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018AttachReply.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; +import ghidra.dbg.sctl.protocol.common.reply.AbstractSctlAttachReply; + +public class Sctl2018AttachReply extends AbstractSctlAttachReply { + @Override + public boolean supportsPlatform() { + return true; + } + + @Override + public void setPlatform(String platform) { + this.platform = new SctlString(platform); + } + + @Override + public String getPlatform() { + return platform.str; + } + + @PacketField(after = "ctlid", before = "ctx") + public SctlString platform; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018ForkNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018ForkNotification.java new file mode 100644 index 0000000000..30f6c9a40e --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018ForkNotification.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; +import ghidra.dbg.sctl.protocol.common.notify.AbstractSctlForkNotification; + +public class Sctl2018ForkNotification extends AbstractSctlForkNotification { + public Sctl2018ForkNotification() { + super(); + } + + public Sctl2018ForkNotification(long spwnid, AbstractSctlContext ctx, + AbstractSctlContext spwnctx) { + super(spwnid, ctx, spwnctx); + } + + @Override + public boolean supportsProcessID() { + return true; + } + + @Override + public long getProcessID() { + return pid; + } + + @Override + public void setProcessID(long pid) { + this.pid = pid; + } + + @PacketField(after = "spwnid", before = "ctx") + public long pid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018LaunchReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018LaunchReply.java new file mode 100644 index 0000000000..fe2b5e3e19 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018LaunchReply.java @@ -0,0 +1,58 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.SctlString; +import ghidra.dbg.sctl.protocol.common.reply.AbstractSctlLaunchReply; + +public class Sctl2018LaunchReply extends AbstractSctlLaunchReply { + @Override + public boolean supportsProcessID() { + return true; + } + + @Override + public void setProcessID(long pid) { + this.pid = pid; + } + + @Override + public long getProcessID() { + return pid; + } + + @Override + public boolean supportsPlatform() { + return true; + } + + @Override + public void setPlatform(String platform) { + this.platform = new SctlString(platform); + } + + @Override + public String getPlatform() { + return platform.str; + } + + @PacketField(after = "ctlid") + public long pid; + + @PacketField(before = "ctx") + public SctlString platform; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018SnapNotification.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018SnapNotification.java new file mode 100644 index 0000000000..896b00e6e3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018SnapNotification.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.notify.AbstractSctlSnapNotification; + +public class Sctl2018SnapNotification extends AbstractSctlSnapNotification { + + @Override + public boolean supportsProcessID() { + return true; + } + + @Override + public void setProcessID(long pid) { + this.pid = pid; + } + + @Override + public long getProcessID() { + return pid; + } + + @PacketField(after = "spwnid", before = "ctx") + public long pid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018SnapshotReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018SnapshotReply.java new file mode 100644 index 0000000000..113ee33b80 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/Sctl2018SnapshotReply.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.reply.AbstractSctlSnapshotReply; + +public class Sctl2018SnapshotReply extends AbstractSctlSnapshotReply { + @Override + public boolean supportsProcessID() { + return true; + } + + @Override + public void setProcessID(long pid) { + this.pid = pid; + } + + @Override + public long getProcessID() { + return pid; + } + + @PacketField(after = "spwnid", before = "ctx") + public long pid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlChooseContextReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlChooseContextReply.java new file mode 100644 index 0000000000..8867ac0bce --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlChooseContextReply.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; +import ghidra.dbg.sctl.protocol.common.reply.SctlGetContextReply; +import ghidra.dbg.sctl.protocol.consts.Mkind; + +/** + * Format for the {@link Mkind#Rchoosectx} SCTL message + */ +public class SctlChooseContextReply extends SctlGetContextReply { + public SctlChooseContextReply() { + } + + public SctlChooseContextReply(AbstractSctlContext ctx) { + super(ctx); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlChooseContextRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlChooseContextRequest.java new file mode 100644 index 0000000000..4eee90d838 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlChooseContextRequest.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +public class SctlChooseContextRequest extends AbstractSctlRequest { + public SctlChooseContextRequest() { + super(); + } + + public SctlChooseContextRequest(long ctlid) { + this.ctlid = ctlid; + } + + @PacketField + public long ctlid; + + @PacketField + public long nreg; + + @RepeatedField + @CountedByField("nreg") + @PacketField + public List regids = new ArrayList<>(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlEnumerateContextReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlEnumerateContextReply.java new file mode 100644 index 0000000000..a48a2eba14 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlEnumerateContextReply.java @@ -0,0 +1,35 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.SctlRegisterDefinition; + +public class SctlEnumerateContextReply extends AbstractSctlReply { + @PacketField + public long nreg; + + @RepeatedField + @CountedByField("nreg") + @PacketField + public List regdefs = new ArrayList<>(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlEnumerateContextRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlEnumerateContextRequest.java new file mode 100644 index 0000000000..0421c8188d --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlEnumerateContextRequest.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +public class SctlEnumerateContextRequest extends AbstractSctlRequest { + public SctlEnumerateContextRequest() { + } + + public SctlEnumerateContextRequest(long ctlid) { + this.ctlid = ctlid; + } + + @PacketField + public long ctlid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlFocusReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlFocusReply.java new file mode 100644 index 0000000000..60193e8f1f --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlFocusReply.java @@ -0,0 +1,21 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import ghidra.dbg.sctl.protocol.AbstractSctlReply; + +public class SctlFocusReply extends AbstractSctlReply { +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlFocusRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlFocusRequest.java new file mode 100644 index 0000000000..29fc46d4dd --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlFocusRequest.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; + +public class SctlFocusRequest extends AbstractSctlRequest { + public SctlFocusRequest() { + } + + public SctlFocusRequest(long ctlid) { + this.ctlid = ctlid; + } + + @PacketField + public long ctlid; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetAttributesReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetAttributesReply.java new file mode 100644 index 0000000000..87a7de9d51 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetAttributesReply.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.AbstractSctlObjectEntry; + +public class SctlGetAttributesReply extends AbstractSctlReply { + + @PacketField + public long nattributes; + + @PacketField + @RepeatedField + @CountedByField("nattributes") + public List attributes = new ArrayList<>(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetAttributesRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetAttributesRequest.java new file mode 100644 index 0000000000..4d4195498b --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetAttributesRequest.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; +import ghidra.dbg.sctl.protocol.common.SctlString; + +public class SctlGetAttributesRequest extends AbstractSctlRequest { + + public SctlGetAttributesRequest() { + } + + public SctlGetAttributesRequest(String path) { + this.path = new SctlString(path); + } + + @PacketField + public SctlString path; + +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetElementsReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetElementsReply.java new file mode 100644 index 0000000000..ab226aa997 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetElementsReply.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlReply; +import ghidra.dbg.sctl.protocol.common.AbstractSctlObjectEntry; + +public class SctlGetElementsReply extends AbstractSctlReply { + + @PacketField + public long nelements; + + @PacketField + @RepeatedField + @CountedByField("nelements") + public List elements = new ArrayList<>(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetElementsRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetElementsRequest.java new file mode 100644 index 0000000000..ac32f47ad3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlGetElementsRequest.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.AbstractSctlRequest; +import ghidra.dbg.sctl.protocol.common.SctlString; + +public class SctlGetElementsRequest extends AbstractSctlRequest { + + public SctlGetElementsRequest() { + } + + public SctlGetElementsRequest(String path) { + this.path = new SctlString(path); + } + + @PacketField + public SctlString path; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepIntoReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepIntoReply.java new file mode 100644 index 0000000000..66749711d1 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepIntoReply.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +public class SctlStepIntoReply { + +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepIntoRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepIntoRequest.java new file mode 100644 index 0000000000..3aa67cebc2 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepIntoRequest.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +public class SctlStepIntoRequest { + +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOutReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOutReply.java new file mode 100644 index 0000000000..3f71677209 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOutReply.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +public class SctlStepOutReply { + +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOutRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOutRequest.java new file mode 100644 index 0000000000..adcba61aff --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOutRequest.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +public class SctlStepOutRequest { + +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOverReply.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOverReply.java new file mode 100644 index 0000000000..9271cd6544 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOverReply.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +public class SctlStepOverReply { + +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOverRequest.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOverRequest.java new file mode 100644 index 0000000000..ed25bf1d40 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SctlStepOverRequest.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +public class SctlStepOverRequest { + +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SelSctl2018Packet.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SelSctl2018Packet.java new file mode 100644 index 0000000000..fd2cc87378 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/SelSctl2018Packet.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base; + +import java.util.Map; + +import ghidra.dbg.sctl.protocol.AbstractSelSctlPacket; +import ghidra.dbg.sctl.protocol.SctlPacket; +import ghidra.dbg.sctl.protocol.consts.Mkind; +import ghidra.dbg.sctl.protocol.v2012ext.SelSctl2012ExtPacket; + +public class SelSctl2018Packet extends AbstractSelSctlPacket { + public static final Map> METHOD_MAP = + typeMap(Mkind.class, SctlPacket.class) // + .putAll(SelSctl2012ExtPacket.METHOD_MAP) // + .put(Mkind.Tenumctx, SctlEnumerateContextRequest.class) // + .put(Mkind.Renumctx, SctlEnumerateContextReply.class) // + .put(Mkind.Tchoosectx, SctlChooseContextRequest.class) // + .put(Mkind.Rchoosectx, SctlChooseContextReply.class) // + .put(Mkind.Tfocus, SctlFocusRequest.class) // + .put(Mkind.Rfocus, SctlFocusReply.class) // + .put(Mkind.Tgetchildren, SctlGetElementsRequest.class) // + .put(Mkind.Rgetchildren, SctlGetElementsReply.class) // + .put(Mkind.Tgetattributes, SctlGetAttributesRequest.class) // + .put(Mkind.Rgetattributes, SctlGetAttributesReply.class) // + .build(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018AnyAnyDialect.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018AnyAnyDialect.java new file mode 100644 index 0000000000..5cb8c9e6e3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018AnyAnyDialect.java @@ -0,0 +1,90 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base.any; + +import java.util.Set; + +import ghidra.comm.packet.AbstractPacketFactory; +import ghidra.comm.packet.PacketFactory; +import ghidra.dbg.sctl.dialect.SctlDialect; +import ghidra.dbg.sctl.protocol.AbstractSelSctlPacket; +import ghidra.dbg.sctl.protocol.common.*; +import ghidra.dbg.sctl.protocol.common.notify.AbstractSctlForkNotification; +import ghidra.dbg.sctl.protocol.common.notify.AbstractSctlSnapNotification; +import ghidra.dbg.sctl.protocol.common.reply.*; +import ghidra.dbg.sctl.protocol.consts.Evkind; +import ghidra.dbg.sctl.protocol.v2018base.*; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; + +public enum Sctl2018AnyAnyDialect implements SctlDialect { + INSTANCE; + + protected static class Sctl2018BusAnyAnyPacketFactory extends AbstractPacketFactory { + { + useFor(AbstractSelSctlPacket.class, SelSctl2018Packet.class); + + useFor(AbstractSctlAttachReply.class, Sctl2018AttachReply.class); + useFor(AbstractSctlLaunchReply.class, Sctl2018LaunchReply.class); + useFor(AbstractSctlSnapshotReply.class, Sctl2018SnapshotReply.class); + + useFor(AbstractSctlForkNotification.class, Sctl2018ForkNotification.class); + useFor(AbstractSctlSnapNotification.class, Sctl2018SnapNotification.class); + + useFor(AbstractSctlContext.class, Sctl2018Context.class); + useFor(AbstractSctlProcessList.class, Sctl2018ProcessList.class); + useFor(AbstractSctlStatus.class, Sctl2018Status.class); + useFor(AbstractSctlTrapSpec.class, Sctl2018TrapSpec.class); + + useFor(AbstractSctlObjectEntry.class, Sctl2018ObjectEntry.class); + } + } + + public static final String SYS_VERSION = "any-any-2018"; + private static final PacketFactory FACTORY = new Sctl2018BusAnyAnyPacketFactory(); + + @Override + public String getSysVersion() { + return SYS_VERSION; + } + + @Override + public PacketFactory getPacketFactory() { + return FACTORY; + } + + @Override + public boolean isBusSupported() { + return true; + } + + @Override + public boolean isRegisterSelectionSupported() { + return true; + } + + @Override + public TargetExecutionState stateAfterEvent(Set events) { + if (events.contains(Evkind.Erunning)) { + return TargetExecutionState.RUNNING; + } + return TargetExecutionState.STOPPED; + } + + @Override + public String getSolePlatform() { + throw new UnsupportedOperationException(); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Binary.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Binary.java new file mode 100644 index 0000000000..06a317b9a7 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Binary.java @@ -0,0 +1,106 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base.any; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlBinary; +import ghidra.dbg.sctl.protocol.common.SctlString; + +public class Sctl2018Binary extends AbstractSctlBinary { + @Override + public void setNamespaceID(long nsid) { + this.nsid = nsid; + } + + @Override + public long getNamespaceID() { + return nsid; + } + + @Override + public void setPath(String path) { + this.path = new SctlString(path); + } + + @Override + public String getPath() { + return path.str; + } + + @Override + public void setExecutable(boolean isexe) { + this.isexe = isexe; + } + + @Override + public boolean isExecutable() { + return isexe; + } + + @Override + public boolean supportsBase() { + return false; + } + + @Override + public void setBase(long base) { + throw new UnsupportedOperationException(); + } + + @Override + public long getBase() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean supportsSections() { + return true; + } + + @Override + public Sctl2018Section addSection() { + Sctl2018Section s = new Sctl2018Section(); + sections.add(s); + return s; + } + + @Override + public List getSections() { + return sections; + } + + @PacketField + public long nsid; + + @PacketField + public boolean isexe; + + @PacketField + public SctlString path; + + @PacketField + public long ns; + + @PacketField + @RepeatedField + @CountedByField("ns") + public List sections = new ArrayList<>(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Context.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Context.java new file mode 100644 index 0000000000..5cc0296e38 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Context.java @@ -0,0 +1,141 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base.any; + +import java.util.*; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; +import ghidra.dbg.sctl.protocol.common.SctlRegisterDefinition; + +public class Sctl2018Context extends AbstractSctlContext { + protected class RegisterCodec { + final int start; + final int byteLen; + + public RegisterCodec(int start, int byteLen) { + this.start = start; + this.byteLen = byteLen; + } + + public void encode(byte[] val) { + System.arraycopy(val, 0, data, start, byteLen); + } + + public void decode(byte[] val) { + System.arraycopy(data, start, val, 0, byteLen); + } + + public byte[] decode() { + byte[] val = new byte[byteLen]; + decode(val); + return val; + } + } + + //private final List regdefs; + private final Map codecsByName = new LinkedHashMap<>(); + private final Set names = new LinkedHashSet<>(); + private final Set namesView = + Collections.unmodifiableSet(names); + private final Map valuesByName = new LinkedHashMap<>(); + private final Map valuesView = Collections.unmodifiableMap(valuesByName); + + private int totalLen; + + public Sctl2018Context() { + } + + @Override + public void setSelectedRegisters(List regdefs) { + int byteLoc = 0; + codecsByName.clear(); + for (SctlRegisterDefinition def : regdefs) { + int byteLen = (int) ((def.nbits + 7) / 8); + codecsByName.put(def.name.str, new RegisterCodec(byteLoc, byteLen)); + names.add(def.name.str); + byteLoc += byteLen; + } + totalLen = byteLoc; + checkData(); + parseData(); + } + + public void setData(byte[] data) { + if (totalLen != data.length) { + throw new IllegalArgumentException( + "data length must match total from selected registers"); + } + System.arraycopy(data, 0, this.data, 0, totalLen); + parseData(); + } + + protected void parseData() { + valuesByName.clear(); + if (data == null) { + return; + } + for (Map.Entry ent : codecsByName.entrySet()) { + valuesByName.put(ent.getKey(), ent.getValue().decode()); + } + } + + @Override + public Map toMap() { + return valuesView; + } + + @Override + public Set getRegisterNames() { + return namesView; + } + + protected void checkData() { + if (data == null) { + data = new byte[totalLen]; + } + } + + @Override + public void updateFromMap(Map values) { + for (Map.Entry ent : values.entrySet()) { + String name = ent.getKey(); + byte[] value = ent.getValue(); + doUpdate(name, value); + } + } + + protected void doUpdate(String name, byte[] value) { + RegisterCodec codec = codecsByName.get(name); + if (codec == null) { + throw new IllegalArgumentException("Register invalid or not selected: " + name); + } + if (value.length != codec.byteLen) { + throw new IllegalArgumentException("Register value for " + name + + " has incorrect length: " + value.length + ". Expected " + codec.byteLen); + } + codec.encode(value); + valuesByName.put(name, value); + } + + @Override + public void update(String name, byte[] value) { + doUpdate(name, value); + } + + @PacketField + public byte[] data; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ObjectEntry.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ObjectEntry.java new file mode 100644 index 0000000000..bbf6a34a54 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ObjectEntry.java @@ -0,0 +1,91 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base.any; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlObjectEntry; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for each process of {@code bytes[]} of SCTL's {@code Rps} reply for Linux dialects + */ +public class Sctl2018ObjectEntry extends AbstractSctlObjectEntry { + @PacketField + public SctlString path; + + @PacketField + public SctlString key; + + @PacketField + public SctlString kind; + + @PacketField + public SctlString value; + + @PacketField + public SctlString type; + + @Override + public SctlString getPath() { + return path; + } + + @Override + public void setPath(SctlString path) { + this.path = path; + } + + @Override + public SctlString getKey() { + return key; + } + + @Override + public void setKey(SctlString key) { + this.key = key; + } + + @Override + public SctlString getKind() { + return kind; + } + + @Override + public void setKind(SctlString kind) { + this.kind = kind; + } + + @Override + public SctlString getValue() { + return value; + } + + @Override + public void setValue(SctlString value) { + this.value = value; + } + + @Override + public SctlString getType() { + return type; + } + + @Override + public void setType(SctlString type) { + this.type = type; + } + +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ProcessEntry.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ProcessEntry.java new file mode 100644 index 0000000000..be3ddb7487 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ProcessEntry.java @@ -0,0 +1,76 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base.any; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlProcessEntry; +import ghidra.dbg.sctl.protocol.common.SctlString; + +/** + * The format for each process of {@code bytes[]} of SCTL's {@code Rps} reply for Linux dialects + */ +public class Sctl2018ProcessEntry extends AbstractSctlProcessEntry { + @PacketField + public long pid; + + @PacketField + public SctlString cmd; + + @PacketField + public long nt; + + @PacketField + @RepeatedField + @CountedByField("nt") + public List threads = new ArrayList<>(); + + @Override + public long getProcessID() { + return pid; + } + + @Override + public void setProcessID(long pid) { + this.pid = pid; + } + + @Override + public String getCommand() { + return cmd.str; + } + + @Override + public void setCommand(String cmd) { + this.cmd = new SctlString(cmd); + } + + @Override + public List getThreads() { + return threads; + } + + @Override + public Sctl2018ThreadEntry addThread() { + Sctl2018ThreadEntry t = new Sctl2018ThreadEntry(); + threads.add(t); + return t; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ProcessList.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ProcessList.java new file mode 100644 index 0000000000..20673fd51b --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ProcessList.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base.any; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlProcessList; + +/** + * The format for {@code bytes[]} of SCTL's {@code Rps} reply for Linux dialects + */ +public class Sctl2018ProcessList extends AbstractSctlProcessList { + @PacketField + public long ntarg; + + @PacketField + @RepeatedField + @CountedByField("ntarg") + public List processes = new ArrayList<>(); + + @Override + public List getProcesses() { + return processes; + } + + @Override + public Sctl2018ProcessEntry addProcess() { + Sctl2018ProcessEntry proc = new Sctl2018ProcessEntry(); + processes.add(proc); + return proc; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Region.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Region.java new file mode 100644 index 0000000000..fef80c64a4 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Region.java @@ -0,0 +1,78 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base.any; + +import ghidra.comm.packet.annot.BitmaskEncoded; +import ghidra.comm.packet.fields.PacketField; +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.sctl.client.SctlMemoryProtection; +import ghidra.dbg.sctl.protocol.common.AbstractSctlRegion; +import ghidra.dbg.sctl.protocol.common.SctlString; + +public class Sctl2018Region extends AbstractSctlRegion { + @Override + public void setName(String name) { + this.name = new SctlString(name); + } + + @Override + public String getName() { + return name.str; + } + + @Override + public void setAddress(long address) { + this.addr = address; + } + + @Override + public long getAddress() { + return addr; + } + + @Override + public void setLength(long length) { + this.len = length; + } + + @Override + public long getLength() { + return len; + } + + @Override + public void setProtections(BitmaskSet flags) { + this.flags = flags; + } + + @Override + public BitmaskSet getProtections() { + return flags; + } + + @PacketField + public SctlString name; + + @PacketField + public long addr; + + @PacketField + public long len; + + @PacketField + @BitmaskEncoded(universe = SctlMemoryProtection.class) + public BitmaskSet flags; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Section.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Section.java new file mode 100644 index 0000000000..0b739c6b5d --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Section.java @@ -0,0 +1,61 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base.any; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlSection; +import ghidra.dbg.sctl.protocol.common.SctlString; + +public class Sctl2018Section extends AbstractSctlSection { + @Override + public void setAddress(long address) { + this.addr = address; + } + + @Override + public long getAddress() { + return addr; + } + + @Override + public void setLength(long length) { + this.len = length; + } + + @Override + public long getLength() { + return len; + } + + @Override + public void setName(String name) { + this.name = new SctlString(name); + } + + @Override + public String getName() { + return name.str; + } + + @PacketField + public long addr; + + @PacketField + public long len; + + @PacketField + public SctlString name; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Status.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Status.java new file mode 100644 index 0000000000..4e8d4a7142 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018Status.java @@ -0,0 +1,81 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base.any; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.comm.packet.annot.CountedByField; +import ghidra.comm.packet.annot.RepeatedField; +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlStatus; + +public class Sctl2018Status extends AbstractSctlStatus { + @Override + public boolean supportsProcessID() { + return false; + } + + @Override + public void setProcessID(long pid) { + throw new UnsupportedOperationException(); + } + + @Override + public long getProcessID() { + throw new UnsupportedOperationException(); + } + + @Override + public Sctl2018Region addRegion() { + Sctl2018Region r = new Sctl2018Region(); + regions.add(r); + return r; + } + + @Override + public List getRegions() { + return regions; + } + + @Override + public Sctl2018Binary addBinary() { + Sctl2018Binary b = new Sctl2018Binary(); + bins.add(b); + return b; + } + + @Override + public List getBinaries() { + return bins; + } + + @PacketField + public long nr; + + @PacketField + @RepeatedField + @CountedByField("nr") + public List regions = new ArrayList<>(); + + @PacketField + public long nb; + + @PacketField + @RepeatedField + @CountedByField("nb") + public List bins = new ArrayList<>(); +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ThreadEntry.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ThreadEntry.java new file mode 100644 index 0000000000..32efb87bc3 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018ThreadEntry.java @@ -0,0 +1,37 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base.any; + +import ghidra.comm.packet.fields.PacketField; +import ghidra.dbg.sctl.protocol.common.AbstractSctlThreadEntry; + +/** + * The format for each thread in SCTL's {@code Rps} for Linux dialects + */ +public class Sctl2018ThreadEntry extends AbstractSctlThreadEntry { + @PacketField + public long tid; + + @Override + public long getThreadID() { + return tid; + } + + @Override + public void setThreadID(long tid) { + this.tid = tid; + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018TrapSpec.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018TrapSpec.java new file mode 100644 index 0000000000..7755ad7e10 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/protocol/v2018base/any/Sctl2018TrapSpec.java @@ -0,0 +1,178 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.protocol.v2018base.any; + +import ghidra.comm.packet.annot.BitmaskEncoded; +import ghidra.comm.packet.fields.PacketField; +import ghidra.comm.util.BitmaskSet; +import ghidra.comm.util.BitmaskUniverse; +import ghidra.dbg.sctl.protocol.common.AbstractSctlTrapSpec; + +public class Sctl2018TrapSpec extends AbstractSctlTrapSpec { + public static final BitmaskSet FLAGS_HARDWARE = + BitmaskSet.of(Flag.TRAP_HR, Flag.TRAP_HW, Flag.TRAP_HX); + + public enum Flag implements BitmaskUniverse { + /** Suspend execution when trapped */ + ACTION_STOP(0), + /** Snapshot target when trapped */ + ACTION_SNAP(1 << 0), + /** Trap on execute using a software breakpoint */ + TRAP_SW(0), + /** Trap on read using a hardware breakpoint */ + TRAP_HR(1 << 1), + /** Trap on write using a hardware breakpoint */ + TRAP_HW(1 << 2), + /** Trap on execute using a hardware breakpoint */ + TRAP_HX(1 << 3); + + private final long mask; + + Flag(long mask) { + this.mask = mask; + } + + @Override + public long getMask() { + return mask; + } + } + + public Sctl2018TrapSpec() { + } + + @Override + public void setAddress(long addr) { + this.addr = addr; + } + + @Override + public long getAddress() { + return addr; + } + + @Override + public void setLength(long len) { + this.len = len; + } + + @Override + public long getLength() { + return len; + } + + @Override + public void setActionStop() { + mode.remove(Flag.ACTION_SNAP); + } + + @Override + public boolean isActionStop() { + return !mode.contains(Flag.ACTION_SNAP); + } + + @Override + public void setActionSnap() { + mode.add(Flag.ACTION_SNAP); + } + + @Override + public boolean isActionSnap() { + return mode.contains(Flag.ACTION_SNAP); + } + + @Override + public void setSoftwareExecute() { + mode.removeAll(FLAGS_HARDWARE); + } + + @Override + public boolean isSoftwareExecute() { + return !mode.containsAny(FLAGS_HARDWARE); + } + + @Override + public void setHardware(boolean read, boolean write, boolean execute) { + if (read) { + mode.add(Flag.TRAP_HR); + } + else { + mode.remove(Flag.TRAP_HR); + } + if (write) { + mode.add(Flag.TRAP_HW); + } + else { + mode.remove(Flag.TRAP_HW); + } + if (execute) { + mode.add(Flag.TRAP_HX); + } + else { + mode.remove(Flag.TRAP_HX); + } + } + + @Override + public boolean isHardware() { + return mode.containsAny(FLAGS_HARDWARE); + } + + @Override + public boolean isHardwareRead() { + return mode.contains(Flag.TRAP_HR); + } + + @Override + public boolean isHardwareWrite() { + return mode.contains(Flag.TRAP_HW); + } + + @Override + public boolean isHardwareExecute() { + return mode.contains(Flag.TRAP_HX); + } + + /** + * Flags identifying when to trap and what action to take + * + * Note that {@link Flag#ACTION_STOP} and {@link Flag#ACTION_SNAP} are mutually exclusive. Note + * also, that {@link Flag#TRAP_SW} is mutually exclusive with the {@link Flag#TRAP_HR}, + * {@link Flag#TRAP_HW}, and {@link Flag#TRAP_HX} flags. The hardware trap flags may be used in + * combination, and the server must accept such requests. + */ + @PacketField + @BitmaskEncoded(universe = Flag.class) + public BitmaskSet mode = BitmaskSet.of(); + + /** + * The address of the (start of) the trap + * + * Note that different platforms may place different restrictions on the address and length of + * hardware traps. + */ + @PacketField + public long addr; + + /** + * For hardware breakpoints, the length of the trap + * + * Note that different platforms may place different restrictions on the address and length of + * hardware traps. + */ + @PacketField + public long len; +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/AbstractSctlClientHandler.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/AbstractSctlClientHandler.java new file mode 100644 index 0000000000..03c209cf40 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/AbstractSctlClientHandler.java @@ -0,0 +1,281 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.server; + +import static ghidra.async.AsyncUtils.loop; +import static ghidra.async.AsyncUtils.sequence; + +import java.nio.channels.AsynchronousSocketChannel; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import ghidra.async.*; +import ghidra.comm.packet.AsynchronousPacketChannel; +import ghidra.comm.service.AbstractAsyncClientHandler; +import ghidra.dbg.sctl.client.err.SctlIncorrectVersionRequest; +import ghidra.dbg.sctl.dialect.SctlDialect; +import ghidra.dbg.sctl.dialect.SctlNullDialect; +import ghidra.dbg.sctl.err.SctlError; +import ghidra.dbg.sctl.protocol.*; +import ghidra.dbg.sctl.protocol.common.reply.*; +import ghidra.dbg.sctl.protocol.common.request.SctlPingRequest; +import ghidra.dbg.sctl.protocol.common.request.SctlVersionRequest; +import ghidra.dbg.sctl.protocol.consts.Mkind; +import ghidra.dbg.util.HandlerMap; +import ghidra.util.Msg; + +/** + * A base class for SCTL servers to handle a single client + * + * This is a bare-bones base class for handling SCTL clients on the server side. This instantiates + * the marshaller, creates a handler map, and provides version/dialect negotiation and ping response + * logic. If any exception is thrown in a handler, it automatically produces a {@code Rerror} + * response. Throwing a {@code SctlError} provides the given message verbatim. Any other exception + * will include the exception class name in the transmitted response, and it will log a stacktrace. + * + * This class anticipates supporting a single dialect. If that is not the case, the implementor must + * override {@link #processVersion(SctlVersionRequest, int)} or provide an alternative handler for + * {@link SctlVersionRequest}. + */ +public abstract class AbstractSctlClientHandler + extends AbstractAsyncClientHandler { + private static final boolean PRINT_STACK_TRACES_FOR_ERROR_REPLIES = true; + protected final AsynchronousPacketChannel channel; + private final SctlMarshaller marshaller; + private SctlDialect dialect = SctlDialect.NULL_DIALECT; + + protected final HandlerMap> handlerMap = + new HandlerMap<>(); + + /** + * Create a new SCTL client handler with the given server + * + * @param server the server handling the client + * @param sock a connection to the client + */ + public AbstractSctlClientHandler(AbstractSctlServer server, AsynchronousSocketChannel sock) { + super(server, sock); + marshaller = new SctlMarshaller(); + marshaller.setPacketFactory(dialect.getPacketFactory()); + channel = new AsynchronousPacketChannel<>(sock, marshaller); + + defaultHandlers(); + } + + /** + * Get the dialects known to the server, in order of preference + * + * NOTE: Use {@link LinkedHashSet} to control the order + * + * @return the ordered set of known dialects + */ + protected abstract Set getKnownDialects(); + + /** + * Choose the dialect for this handler from those offered by the client + * + * Ordinarily, every dialect offered by the client is provided in the {@code supported} set. If + * another client is connected and completed negotiation, then the set can only contain the + * current dialect. If the client does not offer the same dialect, the client will be rejected. + * Override {@link #chooseDialect(String)} to change that behavior. + * + * By default, this implementation will take the intersection of supported dialects and the set + * returned by {@link #getKnownDialects()}, taking the most-preferred dialect. Note that the set + * of dialects known to this server is typically a subset of those known to the SCTL framework. + * + * @param supported the dialects supported by the client and the SCTL framework + * @return the chosen dialect + * @throws SctlError if the server cannot support the client + */ + protected SctlDialect chooseDialect(Set supported) { + for (SctlDialect preferred : getKnownDialects()) { + if (supported.contains(preferred)) { + return preferred; + } + } + throw new SctlError("Server does not support any offered dialects."); + } + + /** + * Choose the dialect for this handler from the version string in the client's request + * + * It is preferred that you override {@link #chooseDialect(Set)} or {@link #getKnownDialects()}. + * + * @param offered the version string presented by the client, including the ctl version + * @return the chosen dialect + * @throws SctlError if the server cannot support the client + */ + protected SctlDialect chooseDialect(String offered) { + Set supported = SctlVersionInfo.collectSupported(offered); + SctlDialect curDialect = server.getDialect(); + if (curDialect != SctlDialect.NULL_DIALECT) { + if (!supported.contains(curDialect)) { + throw new SctlIncorrectVersionRequest( + "Client does not support the version active on the bus: " + + curDialect.getFullVersion()); + } + supported.clear(); + supported.add(curDialect); + // Pass it through chooseDialect instead of returning it + // May not be necessary, but at least gives the implementation a chance + } + return chooseDialect(supported); + } + + /** + * Get the chosen dialect, or the {@link SctlNullDialect} if still negotiating + * + * @return the current dialect + */ + protected SctlDialect getDialect() { + return dialect; + } + + /** + * Install the basic request handlers + * + * The client handler uses the {@link HandlerMap} utility to match each packet type to its + * respective handler. This implementation provides handlers for {@link Mkind#Tversion} and + * {@code Tping}. More than likely, an implementor should override this method, but call the + * superclass's method immediately. The overriding method can then modify the handler map + * freely. + * + * All handlers have nearly the same signature: + * + *
+	 * CompletableFuture processX(X req, int tag)
+	 * 
+ * + * The handler is called upon receiving a request having exactly type {@code X} (not a subtype). + * The tag is given as the extra argument. The handler must immediately return (no blocking on + * {@code send}) a future which completes when the reply has been sent. Please use + * {@link #reply(AsyncLock.Hold, int, SctlPacket)}. This satisfies the requirement to return + * immediately, and it ensures all clients receive a copy of the reply. + */ + protected void defaultHandlers() { + handlerMap.put(SctlVersionRequest.class, this::processVersion); + handlerMap.put(SctlPingRequest.class, this::processPing); + } + + /** + * Upon receiving a {@link Mkind#Tversion} request, negotiate the version for the connection + * + * @param req the request + * @param tag the tag of the request + * @return a future which completes when the {@link Mkind#Rversion} reply has been sent + */ + protected CompletableFuture processVersion(SctlVersionRequest req, int tag) { + SctlDialect newDialect = chooseDialect(req.version); + server.setDialect(newDialect); + marshaller.setPacketFactory(newDialect.getPacketFactory()); + dialect = newDialect; + return reply(null, tag, new SctlVersionReply(newDialect.getFullVersion())); + } + + /** + * Respond to a {@code Tping} request + * + * @param req the request + * @param tag the tag of the request + * @return a future which completes when the {@link Mkind#Rping} reply has been sent + */ + protected CompletableFuture processPing(SctlPingRequest req, int tag) { + return reply(null, tag, new SctlPingReply(req.bytes.length)); + } + + /** + * Send an error reply for a tag and given throwable + * + * Implementors should not call this method directly. Throwing an exception, particularly a + * {@link SctlError}, from within a handler will cause the handler to send an error response + * using this method. + * + * @param tag the tag of the request causing the exception + * @param e the error the request caused + * @return a future which completes when the {@link Mkind#Rerror} reply has been sent + */ + protected CompletableFuture replyErr(int tag, Throwable e) { + Throwable t = AsyncUtils.unwrapThrowable(e); + String msg; + if (t instanceof SctlError) { + msg = t.getMessage(); + } + else { + if (PRINT_STACK_TRACES_FOR_ERROR_REPLIES) { + Msg.debug(this, "Error caused by request " + tag, e); + } + else { + Msg.debug(this, "Error caused by request " + tag + ": " + e); + } + msg = t.getClass() + ": " + t.getMessage(); + } + // TODO: Should this subvert the server's sendLock? + return reply(null, tag, new SctlErrorReply(msg)); + } + + @Override + protected CompletableFuture launchAsync() { + return loop(TypeSpec.VOID, (loop) -> { + if (sock.isOpen()) { + channel.read(AbstractSelSctlPacket.class).handle(loop::consume); + } + else { + server.removeHandler(this); + loop.exit(); + } + }, TypeSpec.cls(AbstractSelSctlPacket.class), (pkt, loop) -> { + loop.repeat(); + sequence(TypeSpec.VOID).then((seq) -> { + server.broadcast(null, pkt, this).handle(seq::next); + }).then((seq) -> { + if (pkt.sel instanceof AbstractSctlRequest) { + handlerMap.handle((AbstractSctlRequest) pkt.sel, pkt.tag).handle(seq::next); + } + else { + throw new SctlError("Client can only send requests."); + } + }).finish().exceptionally((e) -> { + replyErr(pkt.tag, e).exceptionally((ee) -> { + Msg.error(this, "Could not send error reply: " + ee); + return null; + }); + return null; + }); + }); + } + + /** + * Send a reply to a request + * + * The reply is broadcast to all connected clients. To ensure that replies are sent in order, a + * handler may acquire the server's send lock outside of this method. + * + * @param hold a hold on the server's send lock, if applicable, to reenter + * @param tag the tag of the request to which this reply corresponds + * @param reply the reply to send + * @return a future which completes when the reply has been sent + */ + protected CompletableFuture reply(AsyncLock.Hold hold, int tag, AbstractSctlReply reply) { + AbstractSelSctlPacket pkt = dialect.createSel(tag, reply); + return server.sendLock.with(TypeSpec.VOID, hold).then((newHold, seq) -> { + AsyncFence fence = new AsyncFence(); + fence.include(channel.write(pkt)); + fence.include(server.broadcast(newHold, pkt, this)); + fence.ready().handle(seq::nextIgnore); + }).finish(); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/AbstractSctlServer.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/AbstractSctlServer.java new file mode 100644 index 0000000000..33625c9d41 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/AbstractSctlServer.java @@ -0,0 +1,155 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.server; + +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import ghidra.async.AsyncLock; +import ghidra.async.TypeSpec; +import ghidra.comm.service.AbstractAsyncServer; +import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.sctl.dialect.SctlDialect; +import ghidra.dbg.sctl.protocol.AbstractSelSctlPacket; +import ghidra.dbg.sctl.protocol.common.notify.AbstractSctlEventNotification; +import ghidra.dbg.sctl.protocol.common.notify.SctlEventNotify; +import ghidra.dbg.sctl.protocol.consts.Evkind; +import ghidra.util.Msg; + +/** + * A base class for SCTL servers + * + * This class and the base {@link AbstractSctlClientHandler} implement all of the socket operations, + * message parsing, message forwarding, etc., of a SCTL-bus-compliant server. Implementors need only + * override {@link #newHandler(AsynchronousSocketChannel)} to provide a concrete handler. The + * handler implementation is where all of the message processing occurs. + */ +public abstract class AbstractSctlServer + extends AbstractAsyncServer { + private SctlDialect activeDialect = SctlDialect.NULL_DIALECT; + + public final AsyncLock sendLock = new AsyncLock(); + + /** + * Start a SCTL server bound to the given address + * + * @param addr the socket address to bind + * @throws IOException if the socket bind or listen fails + */ + public AbstractSctlServer(SocketAddress addr) throws IOException { + super(addr); + } + + @Override + protected void removeHandler(AbstractSctlClientHandler handler) { + super.removeHandler(handler); + if (handlers.isEmpty()) { + // Allow another client to freely re-negotiate. + activeDialect = SctlDialect.NULL_DIALECT; + } + } + + protected void setDialect(SctlDialect dialect) { + if (activeDialect != SctlDialect.NULL_DIALECT && dialect != activeDialect) { + throw new IllegalStateException("Server cannot speak multiple dialects on the bus"); + } + activeDialect = dialect; + } + + protected SctlDialect getDialect() { + return activeDialect; + } + + @Override + protected boolean checkAcceptable(AsynchronousSocketChannel sock) { + if (!handlers.isEmpty() && activeDialect != SctlDialect.NULL_DIALECT && + !activeDialect.isBusSupported()) { + Msg.error(this, "Rejected connection. Current client is not bus aware."); + return false; + } + return true; + } + + protected CompletableFuture sendTo(AbstractSctlClientHandler to, + AbstractSelSctlPacket pkt) { + return to.channel.write(pkt).exceptionally((exc) -> { + if (exc instanceof CompletionException) { + exc = exc.getCause(); + } + if (exc instanceof IOException) { + Msg.info(this, "Send failed: " + exc.getMessage() + + ". Probably the client closed the connection"); + removeHandler(to); + } + else { + Msg.error(this, "Send failed for unknown reason", exc); + } + return null; + }); + } + + /** + * Broadcast a message to all clients except an optional source + * + * To ensure replies are sent in order, a handler may acquire the server's send lock outside of + * this method. + * + * @param hold a hold on the server's send lock, if applicable, to reenter + * @param pkt the packet to broadcast + * @param from the source client, if applicable, to exclude from the broadcast + * @return a future which completes when the packet has been sent + */ + protected CompletableFuture broadcast(AsyncLock.Hold hold, AbstractSelSctlPacket pkt, + AbstractSctlClientHandler from) { + return sendLock.with(TypeSpec.VOID, hold).then((newHold, seq) -> allHandlers(to -> { + if (to == from) { + return null; + } + return sendTo(to, pkt); + }).handle(seq::exit)).finish(); + } + + /** + * Broadcast a notification to all clients about a thread event + * + * @param hold a hold on the server's send lock, if applicable, to reenter + * @param ctlid the CTLID of the thread producing the event + * @param notification the notification about the event + * @return a future which completes when the notification has been sent + */ + public CompletableFuture notify(AsyncLock.Hold hold, long ctlid, + AbstractSctlEventNotification notification) { + return notify(hold, ctlid, notification, BitmaskSet.of()); + } + + /** + * Broadcast a notification with extra flags to all clients about a thread event + * + * @param hold a hold on the server's send lock, if applicable, to reenter + * @param ctlid the CTLID of the thread producing the event + * @param notification the notification about the event + * @param addFlags the extra (non-event) flags to include + * @return a future which completes when the notification has been sent + */ + public CompletableFuture notify(AsyncLock.Hold hold, long ctlid, + AbstractSctlEventNotification notification, BitmaskSet addFlags) { + return broadcast(hold, + activeDialect.createSel(0, new SctlEventNotify(ctlid, notification, addFlags)), null); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/SctlSyntheticClient.java b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/SctlSyntheticClient.java new file mode 100644 index 0000000000..4f85ae2f86 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/main/java/ghidra/dbg/sctl/server/SctlSyntheticClient.java @@ -0,0 +1,208 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.server; + +import java.util.List; + +import org.apache.commons.lang3.StringUtils; + +import ghidra.async.AsyncLock; +import ghidra.dbg.sctl.client.SctlTargetObject; +import ghidra.dbg.sctl.dialect.SctlDialect; +import ghidra.dbg.sctl.protocol.common.AbstractSctlContext; +import ghidra.dbg.sctl.protocol.common.AbstractSctlTrapSpec; +import ghidra.dbg.sctl.protocol.common.reply.*; +import ghidra.dbg.sctl.protocol.common.request.*; +import ghidra.dbg.sctl.protocol.v2018base.*; + +public class SctlSyntheticClient { + private final AbstractSctlServer server; + + private final byte busId; + private int tagPart = 0; + + public SctlSyntheticClient(AbstractSctlServer server, byte busId) { + this.server = server; + this.busId = busId; + } + + /** + * Generate a tag for the synthesized client + * + * @return + */ + public int tag() { + int tag = (busId << 24) | tagPart; + tagPart++; + tagPart %= 0x00ffffff; + return tag; + } + + /** + * Synthesize an attach command + * + * @param hold an optional hold for re-entry + * @param pid the PID of the target process + * @param ctlid the CTLID for the response + * @param ctx the context for the response + */ + public void synthAttach(AsyncLock.Hold hold, long pid, long ctlid, String platform, + AbstractSctlContext ctx) { + int tag = tag(); + SctlDialect dialect = server.getDialect(); + server.broadcast(hold, dialect.createSel(tag, new SctlAttachRequest(pid)), null); + AbstractSctlAttachReply reply = dialect.create(AbstractSctlAttachReply.class); + reply.ctlid = ctlid; + reply.ctx = ctx; + if (reply.supportsPlatform()) { + reply.setPlatform(platform); + } + server.broadcast(hold, dialect.createSel(tag, reply), null); + } + + /** + * Synthesize a detach command + * + * @param hold an optional hold for re-entry + * @param ctlid the CTLID for the command + */ + public void synthDetach(AsyncLock.Hold hold, long ctlid) { + int tag = tag(); + SctlDialect dialect = server.getDialect(); + server.broadcast(hold, dialect.createSel(tag, new SctlDetachRequest(ctlid)), null); + server.broadcast(hold, dialect.createSel(tag, new SctlDetachReply()), null); + } + + /** + * Synthesize a continue command + * + * @param hold an optional hold for re-entry + * @param ctlid the CTLID for the command + */ + public void synthContinue(AsyncLock.Hold hold, long ctlid) { + int tag = tag(); + SctlDialect dialect = server.getDialect(); + server.broadcast(hold, dialect.createSel(tag, new SctlContinueRequest(ctlid)), null); + server.broadcast(hold, dialect.createSel(tag, new SctlContinueReply()), null); + } + + /** + * Synthesize a stop command + * + * @param hold an optional hold for re-entry + * @param ctlid the CTLID for the command + * @param ctx the context to include in the synthesized reply + */ + public void synthStop(AsyncLock.Hold hold, long ctlid, AbstractSctlContext ctx) { + int tag = tag(); + SctlDialect dialect = server.getDialect(); + server.broadcast(hold, dialect.createSel(tag, new SctlStopRequest(ctlid)), null); + server.broadcast(hold, dialect.createSel(tag, new SctlStopReply(ctx)), null); + } + + /** + * Synthesize a kill command + * + * @param hold an optional hold for re-entry + * @param ctlid the CTLID for the command + */ + public void synthKill(AsyncLock.Hold hold, long ctlid) { + int tag = tag(); + SctlDialect dialect = server.getDialect(); + server.broadcast(hold, dialect.createSel(tag, new SctlKillRequest(ctlid)), null); + server.broadcast(hold, dialect.createSel(tag, new SctlKillReply()), null); + } + + /** + * Synthesize a focus command + * + * @param hold an optional hold for re-entry + * @param ctlid the CTLID for the command + */ + public void synthFocus(AsyncLock.Hold hold, int ctlid) { + int tag = tag(); + SctlDialect dialect = server.getDialect(); + server.broadcast(hold, dialect.createSel(tag, new SctlFocusRequest(ctlid)), null); + server.broadcast(hold, dialect.createSel(tag, new SctlFocusReply()), null); + } + + /** + * Synthesize a set trap command + * + * @param hold an optional hold for re-entry + * @param ctlid the CTLID for the command + * @param spec the trap specification for the command + * @param trpid the TRPID for the response + */ + public void synthSetTrap(AsyncLock.Hold hold, long ctlid, AbstractSctlTrapSpec spec, + long trpid) { + int tag = tag(); + SctlDialect dialect = server.getDialect(); + //Msg.debug(this, "Synthesizing set trap thread=" + threadId + ",trap=" + trapId); + server.broadcast(hold, dialect.createSel(tag, new SctlSetTrapRequest(ctlid, spec)), null); + server.broadcast(hold, dialect.createSel(tag, new SctlSetTrapReply(trpid)), null); + } + + /** + * Synthesize a clear trap command + * + * @param hold an optional hold for re-entry + * @param ctlid the CTLID for the command + * @param trpid the TRPID for the command + */ + public void synthClearTrap(AsyncLock.Hold hold, long ctlid, long trpid) { + int tag = tag(); + SctlDialect dialect = server.getDialect(); + //Msg.debug(this, "Synthesizing clear trap thread=" + threadId + ",trap=" + trapId); + server.broadcast(hold, dialect.createSel(tag, new SctlClearTrapRequest(ctlid, trpid)), + null); + server.broadcast(hold, dialect.createSel(tag, new SctlClearTrapReply()), null); + } + + /** + * Synthesize a get children command + * + * @param hold an optional hold for re-entry + * @param path the tree path identifying the object + */ + // TODO: It'd be better to use an event than command synthesis here + public void synthGetChildren(AsyncLock.Hold hold, List path) { + int tag = tag(); + SctlDialect dialect = server.getDialect(); + //Msg.debug(this, "Synthesizing clear trap thread=" + threadId + ",trap=" + trapId); + String joinedPath = StringUtils.join(path, SctlTargetObject.PATH_SEPARATOR_STRING); + server.broadcast(hold, dialect.createSel(tag, new SctlGetElementsRequest(joinedPath)), + null); + server.broadcast(hold, dialect.createSel(tag, new SctlGetElementsReply()), null); + } + + /** + * Synthesize a get attributes command + * + * @param hold an optional hold for re-entry + * @param path the tree path identifying the object + */ + // TODO: It'd be better to use an event than command synthesis here + public void synthGetAttributes(AsyncLock.Hold hold, List path) { + int tag = tag(); + SctlDialect dialect = server.getDialect(); + //Msg.debug(this, "Synthesizing clear trap thread=" + threadId + ",trap=" + trapId); + String joinedPath = StringUtils.join(path, SctlTargetObject.PATH_SEPARATOR_STRING); + server.broadcast(hold, dialect.createSel(tag, new SctlGetAttributesRequest(joinedPath)), + null); + server.broadcast(hold, dialect.createSel(tag, new SctlGetAttributesReply()), null); + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlClientTest.java b/Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlClientTest.java new file mode 100644 index 0000000000..d9514973f0 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlClientTest.java @@ -0,0 +1,380 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import static ghidra.async.AsyncUtils.completable; +import static ghidra.async.AsyncUtils.sequence; +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.ArrayList; +import java.util.Random; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.junit.Test; + +import ghidra.async.AsyncFence; +import ghidra.async.TypeSpec; +import ghidra.comm.packet.binary.ByteBufferPacketCodec; +import ghidra.comm.packet.err.PacketDecodeException; +import ghidra.comm.packet.err.PacketEncodeException; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.attributes.TypedTargetObjectRef; +import ghidra.dbg.sctl.dialect.SctlDialect; +import ghidra.dbg.sctl.protocol.*; +import ghidra.dbg.sctl.protocol.common.reply.*; +import ghidra.dbg.sctl.protocol.common.request.*; +import ghidra.dbg.sctl.protocol.v2012base.x86.SctlX86Context; +import ghidra.dbg.sctl.protocol.v2012base.x86.linux.Sctl2012LinuxStatus; +import ghidra.dbg.target.*; +import ghidra.dbg.util.PathUtils; +import ghidra.util.Msg; +import ghidra.util.SystemUtilities; + +public class SctlClientTest { + private static final String DEFAULT_PROTOCOL_VERSION = "sctl-2012:x86-linux-2012"; + private static final String EXPECT_VERSIONS = + "sctl-2012:any-any-2018,x86-linux-2012-bus,x86-win-2012-bus,x86-linux-2012,x86-win-2012"; + private static final long TIMEOUT_MILLISECONDS = + SystemUtilities.isInTestingBatchMode() ? 1000 : Long.MAX_VALUE; + + protected static T waitOn(TestServerThread srv, CompletableFuture future) + throws Throwable { + try { + AsyncFence fence = new AsyncFence(); + fence.include(srv); + fence.include(future.exceptionally(exc -> { + // Cheating, but I want to see the real exception + srv.completeExceptionally(exc); // srv.cancel(true); + return ExceptionUtils.rethrow(exc); + })); + fence.ready().get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); + return future.getNow(null); + } + catch (ExecutionException e) { + throw e.getCause(); + } + } + + interface TestClientHandler { + void handle(SocketChannel cli, SctlDialect dialect) throws Exception; + } + + class TestServerThread extends CompletableFuture implements AutoCloseable { + private final TestClientHandler handler; + private final int port; + private final String version; + private final SctlDialect dialect; + private final Thread thread; + + public TestServerThread(TestClientHandler handler, int port, String version) { + this.handler = handler; + this.port = port; + this.version = version; + this.dialect = SctlVersionInfo.agreeDialect(version); + this.thread = new Thread(this::run); + } + + @Override + public void close() throws Exception { + thread.interrupt(); + thread.join(500); + } + + private void run() { + try (ServerSocketChannel srv = ServerSocketChannel.open()) { + synchronized (this) { + srv.bind(new InetSocketAddress("localhost", port)); + notifyAll(); + } + SocketChannel cli = srv.accept(); + handleVersion(cli, version); + handler.handle(cli, dialect); + complete(null); + } + catch (ClosedByInterruptException e) { + complete(null); + return; + } + catch (Throwable e) { + completeExceptionally(e); + e.printStackTrace(); + } + } + + public void start() { + thread.start(); + } + } + + private static final String HELLO_WORLD = "Hello, World!"; + + private static final String TEST_HOST = "localhost"; + private static int PORT = new Random().nextInt(2000) + 1024; + private static InetSocketAddress ADDR; + + private static final int TEST_CTLID = 1234; + private static final int TEST_PID = 5678; + + private final SctlMarshaller marshaller = new SctlMarshaller(); + private final ByteBuffer inbuf = ByteBuffer.allocate(1024); + private final ByteBuffer outbuf = ByteBuffer.allocate(1024); + + protected void expect(SocketChannel cli, SctlDialect dialect, AbstractSelSctlPacket pkt) + throws IOException, PacketDecodeException, PacketEncodeException { + // Ensure the packet can be encoded, and initialize meta fields + Msg.trace(this, "Expecting: " + pkt); + marshaller.setPacketFactory(dialect.getPacketFactory()); + ByteBufferPacketCodec.getInstance().encodePacket(pkt); + + // Now, receive the expected packet + while (true) { + try { + inbuf.flip(); + AbstractSelSctlPacket recv = marshaller.unmarshall(inbuf); + inbuf.compact(); + + Msg.debug(this, "Server Received: " + recv); + assertEquals(pkt, recv); + return; + } + catch (BufferUnderflowException e) { + inbuf.compact(); + Msg.trace(this, "Waiting for data. Currently have " + inbuf.position()); + if (Thread.interrupted()) { + return; + } + int len = cli.read(inbuf); + Msg.trace(this, "Read " + len + " bytes"); + } + } + } + + protected void handleAttach(SocketChannel cli, SctlDialect dialect, int tag) + throws IOException, PacketDecodeException, PacketEncodeException { + expect(cli, dialect, dialect.createSel(tag, new SctlAttachRequest(TEST_PID))); + SctlX86Context ctx = new SctlX86Context(); + ctx.rax = 0x1122334455667788L; + AbstractSctlAttachReply reply = dialect.create(AbstractSctlAttachReply.class); + reply.ctlid = TEST_CTLID; + reply.ctx = ctx; + send(cli, dialect, dialect.createSel(tag, reply)); + } + + protected void handleStat(SocketChannel cli, SctlDialect dialect, int tag) + throws IOException, PacketDecodeException, PacketEncodeException { + expect(cli, dialect, dialect.createSel(tag, new SctlStatusRequest(TEST_CTLID))); + SctlStatusReply reply = new SctlStatusReply(); + Sctl2012LinuxStatus status = new Sctl2012LinuxStatus(); + reply.status = status; + status.pid = TEST_PID; + status.bins = new ArrayList<>(); + status.regions = new ArrayList<>(); + send(cli, dialect, dialect.createSel(tag, reply)); + } + + protected void handleContinue(SocketChannel cli, SctlDialect dialect, int tag) + throws IOException, PacketDecodeException, PacketEncodeException { + expect(cli, dialect, dialect.createSel(tag, new SctlContinueRequest(TEST_CTLID))); + send(cli, dialect, dialect.createSel(tag, new SctlContinueReply())); + } + + protected void handleDetach(SocketChannel cli, SctlDialect dialect, int tag) + throws IOException, PacketDecodeException, PacketEncodeException { + expect(cli, dialect, dialect.createSel(tag, new SctlDetachRequest(TEST_CTLID))); + send(cli, dialect, dialect.createSel(tag, new SctlDetachReply())); + } + + protected void handlePing(SocketChannel cli, SctlDialect dialect, int tag) + throws IOException, PacketDecodeException, PacketEncodeException { + expect(cli, dialect, dialect.createSel(tag, new SctlPingRequest(HELLO_WORLD))); + send(cli, dialect, + dialect.createSel(tag, new SctlPingReply(HELLO_WORLD.getBytes().length))); + } + + protected void handleVersion(SocketChannel cli, String version) + throws IOException, PacketDecodeException, PacketEncodeException { + SctlDialect dialect = SctlDialect.NULL_DIALECT; + expect(cli, dialect, dialect.createSel(0, new SctlVersionRequest(EXPECT_VERSIONS))); + send(cli, dialect, dialect.createSel(0, new SctlVersionReply(version))); + } + + protected TestServerThread runServer(TestClientHandler handler) throws InterruptedException { + return runServer(handler, DEFAULT_PROTOCOL_VERSION); + } + + protected TestServerThread runServer(TestClientHandler handler, String version) + throws InterruptedException { + PORT++; + ADDR = new InetSocketAddress(TEST_HOST, PORT); + TestServerThread result = new TestServerThread(handler, PORT, version); + result.start(); + synchronized (result) { + result.wait(); + } + return result; + } + + protected void send(SocketChannel cli, SctlDialect dialect, AbstractSelSctlPacket pkt) + throws PacketEncodeException, IOException { + Msg.debug(this, "Server Sending: " + pkt); + marshaller.setPacketFactory(dialect.getPacketFactory()); + marshaller.marshall(outbuf, pkt); + outbuf.flip(); + while (outbuf.hasRemaining()) { + if (Thread.interrupted()) { + return; + } + cli.write(outbuf); + } + outbuf.clear(); + } + + protected void seqAttachContinueDetach(SocketChannel cli, SctlDialect dialect) + throws IOException, PacketDecodeException, PacketEncodeException { + handleAttach(cli, dialect, 1); + handleStat(cli, dialect, 2); + handleContinue(cli, dialect, 3); + handleDetach(cli, dialect, 4); + } + + private static String keyInt(int i) { + return PathUtils.makeKey(PathUtils.makeIndex(i)); + } + + private static TypedTargetObjectRef> refPid( + DebuggerObjectModel model, int pid) { + return model.createRef("Attachable", keyInt(pid)).as(TargetAttachable.tclass); + } + + @Test + public void testAttachContinueDetach() throws Throwable { + try (TestServerThread srv = runServer(this::seqAttachContinueDetach)) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference thread = new AtomicReference<>(); + waitOn(srv, sequence(TypeSpec.VOID).then(seq -> { + completable(TypeSpec.VOID, socket::connect, ADDR).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Connecting"); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Attaching"); + TargetAttacher attacher = root.get().as(TargetAttacher.tclass); + attacher.attach(refPid(client, TEST_PID)).handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Getting created thread"); + client.fetchModelObject("Processes", keyInt(TEST_CTLID), "Threads", + keyInt(TEST_CTLID)).handle(seq::next); + }, thread).then(seq -> { + Msg.debug(this, "Continuing"); + TargetResumable resumable = thread.get().as(TargetResumable.tclass); + resumable.resume().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Detaching"); + TargetDetachable detachable = thread.get().as(TargetDetachable.tclass); + detachable.detach().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Disconnecting"); + client.close().handle(seq::next); + }).finish()); + socket.close(); + } + } + + protected void seqAttachDetach(SocketChannel cli, SctlDialect dialect) + throws IOException, PacketDecodeException, PacketEncodeException { + handleAttach(cli, dialect, 1); + handleStat(cli, dialect, 2); + handleDetach(cli, dialect, 3); + } + + @Test + public void testAttachDetach() throws Throwable { + try (TestServerThread srv = runServer(this::seqAttachDetach)) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference thread = new AtomicReference<>(); + waitOn(srv, sequence(TypeSpec.VOID).then(seq -> { + completable(TypeSpec.VOID, socket::connect, ADDR).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Connecting"); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Attaching"); + TargetAttacher attacher = root.get().as(TargetAttacher.tclass); + attacher.attach(refPid(client, TEST_PID)).handle(seq::nextIgnore); + }).then(seq -> { + Msg.debug(this, "Getting created thread"); + client.fetchModelObject("Processes", keyInt(TEST_CTLID), "Threads", + keyInt(TEST_CTLID)).handle(seq::next); + }, thread).then(seq -> { + Msg.debug(this, "Detaching"); + TargetDetachable detachable = thread.get().as(TargetDetachable.tclass); + detachable.detach().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Disconnecting"); + client.close().handle(seq::next); + }).finish()); + socket.close(); + } + } + + protected void seqPing(SocketChannel cli, SctlDialect dialect) + throws IOException, PacketDecodeException, PacketEncodeException { + handlePing(cli, dialect, 1); + } + + @Test + public void testPing() throws Throwable { + try (TestServerThread srv = runServer(this::seqPing)) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + waitOn(srv, sequence(TypeSpec.VOID).then(seq -> { + completable(TypeSpec.VOID, socket::connect, ADDR).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Connecting"); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Pinging"); + client.ping(HELLO_WORLD).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Disconnecting"); + client.close().handle(seq::next); + }).finish()); + socket.close(); + } + } + + /** + * NOTE: I'm no longer testing the "bus" stuff, since pretty much all extension we made to SCTL + * are now deprecated by GADP. + */ +} diff --git a/Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlOnReferenceTest.java b/Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlOnReferenceTest.java new file mode 100644 index 0000000000..e2842dba61 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlOnReferenceTest.java @@ -0,0 +1,1455 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import static ghidra.async.AsyncUtils.completable; +import static ghidra.async.AsyncUtils.sequence; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeNoException; + +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.ProcessBuilder.Redirect; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiFunction; +import java.util.function.Predicate; + +import org.junit.Ignore; +import org.junit.Test; + +import ghidra.async.*; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.attributes.*; +import ghidra.dbg.sctl.err.SctlError; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointListener; +import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionStateListener; +import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.target.TargetObject.TargetObjectListener; +import ghidra.dbg.testutil.DummyProc; +import ghidra.dbg.util.PathUtils; +import ghidra.dbg.util.TargetDataTypeConverter; +import ghidra.framework.Application; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.*; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; +import ghidra.util.*; +import ghidra.util.database.UndoableTransaction; +import ghidra.util.task.TaskMonitor; + +@Ignore("Not high priority") +public class SctlOnReferenceTest extends AbstractGhidraHeadlessIntegrationTest { + private static final long TIMEOUT_MILLISECONDS = + SystemUtilities.isInTestingBatchMode() ? 1000 : Long.MAX_VALUE; + + static class SctlStub implements AutoCloseable { + static int nextPort = 0; + static final boolean USER_LAUNCHED = false; + + static synchronized int nextPort() { + return nextPort++; + } + + final int port = USER_LAUNCHED ? 12345 : nextPort() + 12345; + final InetSocketAddress addr = new InetSocketAddress("localhost", port); + final Process process; + + SctlStub() throws IOException { + if (!USER_LAUNCHED) { + String sctl = Application.getOSFile("sctl").getAbsolutePath(); + process = new ProcessBuilder(sctl, "-p", "" + port) + .redirectError(Redirect.INHERIT) + .redirectOutput(Redirect.INHERIT) + .start(); + } + else { + Msg.info(this, "Test is expecting user-launched sctl at " + addr); + process = null; + } + } + + @Override + public void close() throws Exception { + if (!USER_LAUNCHED) { + process.destroyForcibly().waitFor(); + } + } + } + + SctlStub runSctl() throws IOException { + try { + SctlStub stub = new SctlStub(); + return stub; + } + catch (IOException e) { + assumeNoException("Could not launch sctl. Is it installed and in the path?", e); + return null; + } + } + + protected static Void handleFailure(T result, Throwable exc) { + if (exc != null) { + fail(exc.getMessage()); + } + return null; + } + + protected static final BiFunction NOP_OR_FAIL = + SctlOnReferenceTest::handleFailure; + + protected static T waitOn(CompletableFuture future) throws Throwable { + try { + return future.get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); + } + catch (ExecutionException e) { + throw e.getCause(); + } + } + + @Test + public void testConnect() throws Throwable { + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).finish()); + } + } + + @Test + public void testPing() throws Throwable { + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Pinging..."); + client.ping("Hello, SCTL!").handle(seq::next); + }).finish()); + } + } + + @Test + public void testListAttachable() throws Throwable { + try (DummyProc expCloneSpin = DummyProc.run("expCloneSpin"); SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting process list"); + client.fetchObjectElements("Attachable") + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((elems, seq) -> { + Msg.debug(this, "Got repsonse"); + for (TargetObject proc : elems.values()) { + if (Objects.equals(proc.getIndex(), Long.toString(expCloneSpin.pid))) { + assertEquals("expCloneSpin", + proc.getTypedAttributeNowByName("cmd_line", String.class, null)); + seq.exit(); + return; + } + } + fail("Did not find expected PID"); + }).finish()); + } + } + + protected static class ThreadTracker { + protected static ThreadTracker strongRef; + + protected static class BreakHit { + final TargetObjectRef trapped; + final TypedTargetObjectRef> spec; + + public BreakHit(TargetObjectRef trapped, + TypedTargetObjectRef> spec) { + this.trapped = trapped; + this.spec = spec; + } + } + + protected final AsyncReference lastThreadRef = + new AsyncReference<>(); + protected final AsyncReference lastThreadRemoved = + new AsyncReference<>(); + protected final AsyncReference lastProcRef = new AsyncReference<>(); + protected final AsyncReference lastExitCode = new AsyncReference<>(); + protected final AsyncReference exitCount = new AsyncReference<>(0); + protected final AsyncReference lastBreakHit = new AsyncReference<>(); + protected final TargetObject root; + protected TargetObject processes; + + protected final ListenerForProcesses listenerForProcesses = new ListenerForProcesses(); + protected final ListenerForThreads listenerForThreads = new ListenerForThreads(); + protected final ListenerForExit listenerForExit = new ListenerForExit(); + protected final ListenerForBreak listenerForBreak = new ListenerForBreak(); + + class ListenerForProcesses implements TargetObjectListener { + @Override + public void elementsChanged(TargetObject parent, Collection removed, + Map added) { + for (TargetObjectRef ref : added.values()) { + lastProcRef.set(ref, null); + ref.getSuccessor("Threads").fetch().thenAccept(t -> { + t.addListener(listenerForThreads); + for (TargetObjectRef last : t.getCachedElements().values()) { + lastThreadRef.set(last, null); + } + }).exceptionally(exc -> { + Msg.error(this, "Could not get new process's Thread container: ", exc); + return null; + }); + } + } + } + + class ListenerForThreads implements TargetObjectListener { + @Override + public void elementsChanged(TargetObject parent, Collection removed, + Map added) { + for (TargetObjectRef ref : added.values()) { + lastThreadRef.set(ref, null); + ref.fetch().thenAccept(t -> { + t.addListener(listenerForExit); + }).exceptionally(exc -> { + Msg.error(this, "Could not get new process: ", exc); + return null; + }); + ref.fetchSuccessor("Breakpoints").thenAccept(b -> { + b.addListener(listenerForBreak); + }).exceptionally(exc -> { + Msg.error(this, "Could not get break container: ", exc); + return null; + }); + } + for (String name : removed) { + lastThreadRemoved.set(parent.getSuccessor(name), null); + } + } + } + + class ListenerForExit implements TargetObjectListener { + @Override + public void attributesChanged(TargetObject parent, Collection removed, + Map added) { + Object exitCode = added.get("exit_code"); + if (exitCode != null) { + Msg.debug(this, "Object " + parent + " exited with code " + exitCode); + lastExitCode.set((Long) exitCode, null); + exitCount.compute(c -> c + 1, null); + } + } + } + + class ListenerForBreak implements TargetBreakpointListener { + @Override + public void breakpointHit(TargetBreakpointContainer container, + TargetObjectRef trapped, + TypedTargetObjectRef> frame, + TypedTargetObjectRef> spec, + TypedTargetObjectRef> breakpoint) { + lastBreakHit.set(new BreakHit(trapped, spec), null); + } + } + + public ThreadTracker(TargetObject root) { + strongRef = this; + this.root = root; + } + + protected CompletableFuture init() { + return root.fetchSuccessor("Processes").thenAccept(p -> { + this.processes = p; + processes.addListener(listenerForProcesses); + }); + } + } + + protected ThreadTracker trackThreads(TargetObject root) { + return new ThreadTracker(root); + } + + protected static class WhileResumer extends CompletableFuture + implements TargetExecutionStateListener { + protected static WhileResumer strongRef; + + protected final TargetResumable resumable; + protected final Predicate predicate; + + public WhileResumer(TargetObject thread, Predicate predicate) { + strongRef = this; + thread.addListener(this); + resumable = thread.as(TargetResumable.tclass); + this.predicate = predicate; + TargetExecutionStateful stateful = thread.as(TargetExecutionStateful.tclass); + doState(stateful.getExecutionState()); + } + + @Override + public void executionStateChanged(TargetExecutionStateful object, + TargetExecutionState state) { + doState(state); + } + + protected void doState(TargetExecutionState state) { + Msg.debug(this, "Thread " + resumable + " now " + state); + if (predicate.test(resumable)) { + if (state == TargetExecutionState.STOPPED) { + resumable.resume().exceptionally(exc -> { + resumable.removeListener(this); + completeExceptionally(exc); + return null; + }); + } + else if (state == TargetExecutionState.TERMINATED) { + resumable.removeListener(this); + complete(null); + } + } + else { + resumable.removeListener(this); + complete(null); + } + } + } + + protected CompletableFuture resumeWhile(TargetObject thread, + Predicate predicate) { + return new WhileResumer(thread, predicate); + } + + protected static String keyInt(int i) { + return PathUtils.makeKey(PathUtils.makeIndex(i)); + } + + protected static TypedTargetObjectRef> refPid( + DebuggerObjectModel model, int pid) { + return model.createRef("Attachable", keyInt(pid)).as(TargetAttachable.tclass); + } + + static void stupidSleep(long millis) { + try { + Thread.sleep(millis); + } + catch (InterruptedException e) { + // Whatever + } + } + + @Test + public void testLaunchCont() throws Throwable { + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + long status = waitOn(sequence(TypeSpec.LONG).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, + DummyProc.which("echo") + " test")).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((thread, seq) -> { + Msg.debug(this, "Continuing until exit..."); + resumeWhile(thread, t -> true).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for exit code..."); + ThreadTracker.strongRef.lastExitCode.waitUntil(v -> v != null).handle(seq::exit); + }).finish()); + assertEquals(0, status); + } + } + + @Test + public void testLaunchKill() throws Throwable { + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, + DummyProc.which("echo") + " test")).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((thread, seq) -> { + Msg.debug(this, "Killing..."); + TargetKillable killable = thread.as(TargetKillable.tclass); + killable.kill().handle(seq::next); + }).finish()); + } + } + + @Test + public void testAttachKill() throws Throwable { + try (DummyProc dd = DummyProc.run("dd"); SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Attaching..."); + TargetAttacher attacher = root.get().as(TargetAttacher.tclass); + attacher.attach(refPid(client, (int) dd.pid)).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((thread, seq) -> { + Msg.debug(this, "Killing..."); + TargetKillable killable = thread.as(TargetKillable.tclass); + killable.kill().handle(seq::next); + }).finish()); + } + } + + @Test + public void testLaunchSetClearBreakpoint() throws Throwable { + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference> regs = new AtomicReference<>(); + AtomicReference> traps = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, + DummyProc.which("echo") + " test")).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + AsyncFence fence = new AsyncFence(); + fence.include(threadRef.fetchSuccessor("Registers").thenAccept(obj -> { + regs.set(obj.as(TargetRegisterBank.tclass)); + })); + fence.include(threadRef.fetchSuccessor("Breakpoints").thenAccept(obj -> { + traps.set(obj.as(TargetBreakpointContainer.tclass)); + })); + fence.ready().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Reading RIP..."); + regs.get().readRegister("rip").handle(seq::next); + }, TypeSpec.BYTE_ARRAY).then((ripBytes, seq) -> { + long ripVal = ByteBuffer.wrap(ripBytes).order(ByteOrder.BIG_ENDIAN).getLong(0); + Address rip = client.getAddress("ram", ripVal); + Msg.debug(this, "Got RIP=" + rip); + traps.get() + .placeBreakpoint(rip, Set.of(TargetBreakpointKind.SOFTWARE)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Breakpoints: "); + for (TargetObjectRef bref : traps.get().getCachedElements().values()) { + Msg.debug(this, " " + bref); + } + traps.get() + .getCachedElements() + .values() + .iterator() + .next() + .fetch() + .handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + TargetDeletable spec = obj.as(TargetDeletable.tclass); + spec.delete().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Deleted breakpoint"); + seq.exit(); + }).finish()); + } + } + + @Test(expected = SctlError.class) + public void testErr() throws Throwable { + // TODO: Translate the exception to the appropriate model exception + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Attaching to bogus PID..."); + TargetAttacher attacher = root.get().as(TargetAttacher.tclass); + attacher.attach(refPid(client, -1)).handle(seq::next); + }).then(seq -> { + fail("Got a thread"); + seq.exit(); + }).finish()); + } + } + + /* + * TODO: + * + * 1(DONE). See what happens when we fork the process (esp., wrt. threads). According to UNIX + * docs, should cause a single ForkEvent, yielding a new process with one thread. + * + * 2(DONE). See what happens when we clone the process (esp., wrt. threads). Just another + * thread. + * + * 3(DONE). See what happens on pthread_create. Causes CloneEvent. + * + * 4(DONE). See what happens when a thread exits. Do we get the status code? If not, then I need + * to remove the threadExited callback from the listener. It probably belongs in process + * listener anyway. Then again, even if the reference does not give it, shouldn't I model it + * here for extensibility? It seems only threads that were alive at the time the process exited + * are given the status code. All of them are given the same code. Threads that exit before the + * process are reported with a status of 0, no matter what they return. + * + * 5(DONE). When I set a breakpoint on one CTLID of a process, will that cause the other threads + * to break there, too? No, it does not. + * + * 6(DONE). Does killing one thread kill the whole process? Yes and no. When I kill the primary + * thread, nothing seems to be affected. When I kill the secondary thread, sctl and the target + * process die altogether. + * + * 7(DONE). Does detaching one thread detach the whole process? (try main thread and spawned + * thread). No, detaching from either leaves the other thread attached. However, sctl appears + * confused beyond that point. Continuing one thread actually continues both, or the attached + * thread may continue immediately after the other is detached. In general, detach should be + * avoided until we're ready to detach the whole process. Whatever the case, it appears SCTL is + * meant to be able to detach a single thread, so we should assume thread granularity. + * + * 8(DONE). What if I start a process outside of sctl, it clones, and then I attach? It seems I + * can only ever attach to the main thread. + */ + + @Test + public void testExpFork() throws Throwable { + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch( + Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, DummyProc.which("expFork"))) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + ThreadTracker.strongRef.lastThreadRef.set(null, null); + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((thread, seq) -> { + Msg.debug(this, "Resuming first (parent) indefinitely"); + resumeWhile(thread, t -> true).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for parent to exit"); + ThreadTracker.strongRef.lastExitCode.waitUntil(v -> v != null).handle(seq::next); + }, TypeSpec.cls(Long.class)).then((exit, seq) -> { + Msg.debug(this, "Parent exited with " + exit); + ThreadTracker.strongRef.lastExitCode.set(null, null); + // TODO: I'm not sure why this is off by a factor of 256. A byte offset somewhere? + assertEquals(256, exit.longValue()); + Msg.debug(this, "Waiting for second (child) thread (and process)"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got child thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((thread, seq) -> { + Msg.debug(this, "Resuming child indefinitely"); + resumeWhile(thread, t -> true).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for child to exit"); + ThreadTracker.strongRef.lastExitCode.waitUntil(v -> v != null).handle(seq::next); + }, TypeSpec.cls(Long.class)).then((exit, seq) -> { + Msg.debug(this, "Child exited with " + exit); + assertEquals(0, exit.longValue()); + seq.exit(); + }).finish()); + } + } + + /** + * This experiment confirms that sctl has a separate list of breakpoints per thread + * + * @throws Throwable + */ + @Test + public void testExpCloneBreak() throws Throwable { + String expCloneExit = DummyProc.which("expCloneExit"); + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference launchedThread = new AtomicReference<>(); + AtomicReference clonedThread = new AtomicReference<>(); + AtomicReference> work = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expCloneExit)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for launched thread ref"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + ThreadTracker.strongRef.lastThreadRef.set(null, null); + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, launchedThread).then(seq -> { + Msg.debug(this, "Resuming until another thread appears: " + launchedThread.get()); + resumeWhile(launchedThread.get(), t -> { + TargetObjectRef ref = ThreadTracker.strongRef.lastThreadRef.get(); + Msg.debug(this, "lastThreadRef = " + ref); + return ref == null; + }).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for cloned thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, clonedThread).then(seq -> { + // Verify nothing has exited, yet + assertNull(ThreadTracker.strongRef.lastExitCode.get()); + Msg.debug(this, "Placing breakpoint at 'work'"); + ThreadTracker.strongRef.lastProcRef.get() + .fetchSuccessor( + PathUtils.parse("Modules[" + expCloneExit + "].Symbols[work]")) + .handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + work.set(obj.as(TargetSymbol.tclass)); + Msg.debug(this, "Resolved 'work' to " + work.get().getValue()); + launchedThread.get().fetchSuccessor("Breakpoints").handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + TargetBreakpointContainer traps = obj.as(TargetBreakpointContainer.tclass); + traps.placeBreakpoint(work.get().getValue(), Set.of(TargetBreakpointKind.SOFTWARE)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Continuing secondary thread indefinitely"); + resumeWhile(clonedThread.get(), t -> true).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for secondary thread to exit"); + ThreadTracker.strongRef.lastExitCode.waitUntil(v -> v != null).handle(seq::next); + }, TypeSpec.cls(Long.class)).then((exit, seq) -> { + ThreadTracker.strongRef.lastExitCode.set(null, null); + Msg.debug(this, "Exited with " + exit); + assertNull(ThreadTracker.strongRef.lastBreakHit.get()); + Msg.debug(this, "Continuing primary thread indefinitely"); + resumeWhile(launchedThread.get(), t -> true).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for primary thread to exit"); + ThreadTracker.strongRef.lastExitCode.waitUntil(v -> v != null).handle(seq::next); + }, TypeSpec.cls(Long.class)).then((exit, seq) -> { + ThreadTracker.strongRef.lastExitCode.set(null, null); + Msg.debug(this, "Exited with " + exit); + assertNotNull(ThreadTracker.strongRef.lastBreakHit.get()); + seq.exit(); + }).finish()); + } + } + + /** + * This experiment determined that a thread exiting unrelated to process termination is assigned + * an exit status of 0. If any thread calls exit(), then all threads are terminated with a + * status given by that call. + * + * @throws Throwable + */ + @Test + public void testExpCloneExit() throws Throwable { + String expCloneExit = DummyProc.which("expCloneExit"); + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference launchedThread = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expCloneExit)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for launched thread ref"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + ThreadTracker.strongRef.lastThreadRef.set(null, null); + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, launchedThread).then(seq -> { + Msg.debug(this, "Resuming launched thread indefinitely"); + resumeWhile(launchedThread.get(), t -> true).handle(seq::next); + // Note that the cloned thread will be terminated despite not returning + }).then(seq -> { + Msg.debug(this, "Waiting for 2 exits"); + ThreadTracker.strongRef.exitCount.waitValue(2).handle(seq::next); + }).finish()); + } + } + + /** + * Check the behavior of sctl when a process clones and you detach the secondary thread + * + * This test reveals what appears to be a bug. If I remove the call to continue, then neither + * prints until sctl is forcibly terminated. + */ + @Test + public void testExpCloneDetachSecondary() throws Throwable { + String expCloneExit = DummyProc.which("expCloneExit"); + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference launchedThread = new AtomicReference<>(); + AtomicReference clonedThread = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expCloneExit)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for launched thread ref"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + ThreadTracker.strongRef.lastThreadRef.set(null, null); + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, launchedThread).then(seq -> { + Msg.debug(this, "Resuming until another thread appears: " + launchedThread.get()); + resumeWhile(launchedThread.get(), t -> { + TargetObjectRef ref = ThreadTracker.strongRef.lastThreadRef.get(); + Msg.debug(this, "lastThreadRef = " + ref); + return ref == null; + }).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for cloned thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, clonedThread).then(seq -> { + // Verify nothing has exited, yet + assertNull(ThreadTracker.strongRef.lastExitCode.get()); + Msg.debug(this, "Detaching from cloned thread"); + TargetDetachable detachable = clonedThread.get().as(TargetDetachable.tclass); + detachable.detach().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Continuing the primary thread indefinitely"); + resumeWhile(launchedThread.get(), t -> true).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting on primary thread to exit"); + ThreadTracker.strongRef.exitCount.waitValue(1).handle(seq::next); + }).finish()); + } + } + + /** + * Check the behavior of sctl when a process clones and you detach the primary thread + * + * This test reveals a minor annoyance. The primary thread resumes execution upon detaching, and + * so it exits, terminating the secondary thread (to which I'm still attached) immediately with + * the exit code of the whole process. + */ + @Test + public void testExpCloneDetachPrimary() throws Throwable { + String expCloneExit = DummyProc.which("expCloneExit"); + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference launchedThread = new AtomicReference<>(); + AtomicReference clonedThread = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expCloneExit)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for launched thread ref"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + ThreadTracker.strongRef.lastThreadRef.set(null, null); + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, launchedThread).then(seq -> { + Msg.debug(this, "Resuming until another thread appears: " + launchedThread.get()); + resumeWhile(launchedThread.get(), t -> { + TargetObjectRef ref = ThreadTracker.strongRef.lastThreadRef.get(); + Msg.debug(this, "lastThreadRef = " + ref); + return ref == null; + }).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for cloned thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, clonedThread).then(seq -> { + // Verify nothing has exited, yet + assertNull(ThreadTracker.strongRef.lastExitCode.get()); + Msg.debug(this, "Detaching from primary thread"); + TargetDetachable detachable = launchedThread.get().as(TargetDetachable.tclass); + detachable.detach().handle(seq::next); + }).then(seq -> { + // NOTE: I don't have to continue to observe the exit event + Msg.debug(this, "Waiting on cloned thread to exit"); + ThreadTracker.strongRef.exitCount.waitValue(1).handle(seq::next); + }).finish()); + } + } + + /** + * Killing the secondary thread causes the primary thread to terminate with status 9 (not + * shifted by 8). + */ + @Test + public void testExpCloneKillSecondary() throws Throwable { + String expCloneExit = DummyProc.which("expCloneExit"); + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference launchedThread = new AtomicReference<>(); + AtomicReference clonedThread = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expCloneExit)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for launched thread ref"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + ThreadTracker.strongRef.lastThreadRef.set(null, null); + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, launchedThread).then(seq -> { + Msg.debug(this, "Resuming until another thread appears: " + launchedThread.get()); + resumeWhile(launchedThread.get(), t -> { + TargetObjectRef ref = ThreadTracker.strongRef.lastThreadRef.get(); + Msg.debug(this, "lastThreadRef = " + ref); + return ref == null; + }).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for cloned thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, clonedThread).then(seq -> { + // Verify nothing has exited, yet + assertNull(ThreadTracker.strongRef.lastExitCode.get()); + Msg.debug(this, "Killing cloned thread"); + TargetKillable killable = clonedThread.get().as(TargetKillable.tclass); + killable.kill().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for exit"); + ThreadTracker.strongRef.lastExitCode.waitUntil(v -> v != null).handle(seq::next); + }, TypeSpec.cls(Long.class)).then((exit, seq) -> { + assertEquals(9, exit.longValue()); + seq.exit(); + }).finish()); + } + } + + /** + * There seems to be a bug: The primary thread is not killed, but rather seems to resume, as it + * prints. It then exits (with code 1, or status 256) causing the secondary thread to be + * terminated. + */ + @Test + public void testExpCloneKillPrimary() throws Throwable { + String expCloneExit = DummyProc.which("expCloneExit"); + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference launchedThread = new AtomicReference<>(); + AtomicReference clonedThread = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expCloneExit)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for launched thread ref"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + ThreadTracker.strongRef.lastThreadRef.set(null, null); + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, launchedThread).then(seq -> { + Msg.debug(this, "Resuming until another thread appears: " + launchedThread.get()); + resumeWhile(launchedThread.get(), t -> { + TargetObjectRef ref = ThreadTracker.strongRef.lastThreadRef.get(); + Msg.debug(this, "lastThreadRef = " + ref); + return ref == null; + }).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for cloned thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, clonedThread).then(seq -> { + // Verify nothing has exited, yet + assertNull(ThreadTracker.strongRef.lastExitCode.get()); + Msg.debug(this, "Killing launched thread"); + TargetKillable killable = launchedThread.get().as(TargetKillable.tclass); + killable.kill().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for exit"); + ThreadTracker.strongRef.lastExitCode.waitUntil(v -> v != null).handle(seq::next); + }, TypeSpec.cls(Long.class)).then((exit, seq) -> { + assertEquals(256, exit.longValue()); + seq.exit(); + }).finish()); + } + } + + @Test + public void testExpCloneThenAttachKill() throws Throwable { + try (DummyProc expCloneSpin = DummyProc.run("expCloneSpin"); SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference attachedThread = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Attaching..."); + TargetAttacher attacher = root.get().as(TargetAttacher.tclass); + attacher.attach(refPid(client, (int) expCloneSpin.pid)).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, attachedThread).then(seq -> { + Msg.debug(this, "Killing..."); + TargetKillable killable = attachedThread.get().as(TargetKillable.tclass); + killable.kill().handle(seq::next); + }).finish()); + } + } + + @Test + public void testExpReadRIP() throws Throwable { + String expCloneExit = DummyProc.which("expCloneExit"); + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference> regs = new AtomicReference<>(); + AtomicReference> memory = new AtomicReference<>(); + AtomicReference
rip = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expCloneExit)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetchSuccessor("Registers").thenAccept(obj -> { + regs.set(obj.as(TargetRegisterBank.tclass)); + }).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Reading rip"); + regs.get().readRegister("rip").handle(seq::next); + }, TypeSpec.BYTE_ARRAY).then((data, seq) -> { + long ripVal = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN).getLong(0); + rip.set(client.getAddress("ram", ripVal)); + Msg.debug(this, "RIP = " + Long.toHexString(ripVal)); + ThreadTracker.strongRef.lastProcRef.get() + .fetchSuccessor("Memory") + .thenAccept(obj -> { + memory.set(obj.as(TargetMemory.tclass)); + }) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Reading memory[rip:8]"); + memory.get().readMemory(rip.get(), 8).handle(seq::next); + // TODO: Can I assert what actual reads went over the network? + // Expect: [rip:8], [rip+8:1], (none) + }, TypeSpec.BYTE_ARRAY).then((data, seq) -> { + Msg.debug(this, + "Read memory[rip:8]: " + NumericUtilities.convertBytesToString(data, ":")); + memory.get().readMemory(rip.get().add(1), 8).handle(seq::next); + }, TypeSpec.BYTE_ARRAY).then((data, seq) -> { + Msg.debug(this, + "Read memory[rip+1:8]: " + NumericUtilities.convertBytesToString(data, ":")); + memory.get().readMemory(rip.get().add(2), 5).handle(seq::next); + }, TypeSpec.BYTE_ARRAY).then((data, seq) -> { + Msg.debug(this, + "Read memory[rip+2:5]: " + NumericUtilities.convertBytesToString(data, ":")); + seq.exit(); + }).finish()); + } + } + + @Test + public void testExpWrite() throws Throwable { + String expPrint = DummyProc.which("expPrint"); + final String toWrite = "Speak"; + final String expected = "Speak, World!"; + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference thread = new AtomicReference<>(); + AtomicReference> overwrite = new AtomicReference<>(); + AtomicReference> memory = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expPrint)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for thread"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, thread).then(seq -> { + ThreadTracker.strongRef.lastProcRef.get() + .fetchSuccessor( + PathUtils.parse("Modules[" + expPrint + "].Symbols[overwrite]")) + .handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + overwrite.set(obj.as(TargetSymbol.tclass)); + Msg.debug(this, "overwrite is at addr: " + overwrite.get().getValue()); + ThreadTracker.strongRef.lastProcRef.get() + .fetchSuccessor("Memory") + .handle(seq::next); + }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { + memory.set(obj.as(TargetMemory.tclass)); + Msg.debug(this, "Writing memory"); + memory.get() + .writeMemory(overwrite.get().getValue(), toWrite.getBytes()) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Reading memory"); + memory.get() + .readMemory(overwrite.get().getValue(), expected.getBytes().length) + .handle(seq::next); + }, TypeSpec.BYTE_ARRAY).then((data, seq) -> { + Msg.debug(this, "Read: " + new String(data)); + assertEquals(expected, new String(data)); + resumeWhile(thread.get(), t -> true).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for exit"); + ThreadTracker.strongRef.lastExitCode.waitUntil(v -> v != null).handle(seq::next); + }, TypeSpec.cls(Long.class)).then((exit, seq) -> { + assertEquals(toWrite.getBytes()[0], exit / 256); + seq.exit(); + }).finish()); + } + } + + @Test + public void testExpTypes() throws Throwable { + String expTypes = DummyProc.which("expTypes"); + DataTypeManager dtm = new StandAloneDataTypeManager("Test") { + { + dataOrganization = + DataOrganizationImpl.getDefaultOrganization(getSLEIGH_X86_64_LANGUAGE()); + } + }; + + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expTypes)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for process"); + ThreadTracker.strongRef.lastProcRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((procRef, seq) -> { + Msg.debug(this, "Got process ref: " + procRef); + procRef.fetchSubElements(PathUtils.parse("Modules[" + expTypes + "].Types")) + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((types, seq) -> { + Msg.debug(this, "Converting data types"); + TargetDataTypeConverter conv = new TargetDataTypeConverter(dtm); + AsyncFence fence = new AsyncFence(); + for (TargetObject obj : types.values()) { + TargetDataType tdt = obj.as(TargetNamedDataType.tclass); + fence.include(conv.convertTargetDataType(tdt).thenAccept(t -> { + synchronized (dtm) { + try (UndoableTransaction tid = + UndoableTransaction.start(dtm, "Add type", true)) { + dtm.addDataType(t, DataTypeConflictHandler.DEFAULT_HANDLER); + } + } + })); + } + fence.ready().handle(seq::next); + }).then(seq -> { + // TODO: Figure out why offsets are lining up + // I figure it's either alignment, or incorrect int size + Msg.debug(this, "Printing data types:"); + try (PrintWriter writer = new PrintWriter(System.out)) { + DataTypeWriter dtw = new DataTypeWriter(dtm, writer); + List all = new ArrayList<>(); + dtm.getAllDataTypes(all); + dtw.write(all, TaskMonitor.DUMMY); + } + catch (Throwable e) { + throw new AssertionError(e); + } + seq.exit(); + }).finish()); + } + } + + @Test + public void testExpSymbols() throws Throwable { + String expTypes = DummyProc.which("expTypes"); + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expTypes)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for process"); + ThreadTracker.strongRef.lastProcRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((procRef, seq) -> { + Msg.debug(this, "Got process ref: " + procRef); + procRef.fetchSubElements(PathUtils.parse("Modules[" + expTypes + "].Symbols")) + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((symbols, seq) -> { + Msg.debug(this, "Printing symbols:"); + for (TargetObject sym : symbols.values()) { + Msg.debug(this, " " + sym); + } + Msg.debug(this, "Done"); + seq.exit(); + }).finish()); + } + } + + /** + * Experiment to see what notifications SCTL sends when we clone then exec + * + * In particular what does it say about the threads that are destroyed by the call to exec? + */ + @Test + @Ignore("Eexec is unreliable in the reference stub") + public void testCloneExec() throws Throwable { + String expCloneExec = DummyProc.which("expCloneExec"); + try (SctlStub stub = runSctl()) { + AsynchronousSocketChannel socket = AsynchronousSocketChannel.open(); + SctlClient client = new SctlClient("Test", socket); + AtomicReference root = new AtomicReference<>(); + AtomicReference launchedThread = new AtomicReference<>(); + waitOn(sequence(TypeSpec.VOID).then(seq -> { + Msg.debug(this, "Connecting..."); + completable(TypeSpec.VOID, socket::connect, stub.addr).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Negotiating..."); + client.connect().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Getting model root"); + client.fetchModelRoot().handle(seq::next); + }, root).then(seq -> { + Msg.debug(this, "Initializing thread tracker"); + trackThreads(root.get()).init().handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Launching..."); + TargetLauncher launcher = root.get().as(TargetLauncher.tclass); + launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, expCloneExec)) + .handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Waiting for launched thread ref"); + ThreadTracker.strongRef.lastThreadRef.waitUntil(r -> r != null).handle(seq::next); + }, TypeSpec.cls(TargetObjectRef.class)).then((threadRef, seq) -> { + ThreadTracker.strongRef.lastThreadRef.set(null, null); + Msg.debug(this, "Got thread ref: " + threadRef); + threadRef.fetch().handle(seq::next); + }, launchedThread).then(seq -> { + Msg.debug(this, "Resuming until another thread appears: " + launchedThread.get()); + resumeWhile(launchedThread.get(), t -> { + TargetObjectRef ref = ThreadTracker.strongRef.lastThreadRef.get(); + Msg.debug(this, "lastThreadRef = " + ref); + return ref == null; + }).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Resuming until a thread disappears: " + launchedThread.get()); + ThreadTracker.strongRef.lastThreadRemoved.set(null, null); + resumeWhile(launchedThread.get(), t -> { + TargetObjectRef ref = ThreadTracker.strongRef.lastThreadRemoved.get(); + Msg.debug(this, "lastThreadRemoved = " + ref); + return ref == null; + }).handle(seq::next); + }).then(seq -> { + Msg.debug(this, "Checking number of threads"); + ThreadTracker.strongRef.lastProcRef.get() + .fetchSubElements("Threads") + .thenCompose(DebugModelConventions::fetchAll) + .handle(seq::next); + }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((threads, seq) -> { + Msg.debug(this, "Got threads: " + threads); + assertEquals(1, threads.size()); + seq.exit(); + }).finish()); + } + } +} diff --git a/Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlPacketsTest.java b/Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlPacketsTest.java new file mode 100644 index 0000000000..13d9eba986 --- /dev/null +++ b/Ghidra/Debug/Debugger-sctl/src/test/java/ghidra/dbg/sctl/client/SctlPacketsTest.java @@ -0,0 +1,88 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.sctl.client; + +import static org.junit.Assert.assertEquals; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; + +import org.junit.Test; + +import ghidra.comm.packet.PacketCodec; +import ghidra.comm.packet.PacketFactory; +import ghidra.comm.packet.binary.ByteBufferPacketCodec; +import ghidra.comm.packet.err.PacketDecodeException; +import ghidra.comm.packet.err.PacketEncodeException; +import ghidra.dbg.sctl.protocol.AbstractSelSctlPacket; +import ghidra.dbg.sctl.protocol.common.notify.AbstractSctlListsLibrariesEventNotification.PathBase; +import ghidra.dbg.sctl.protocol.common.notify.SctlEventNotify; +import ghidra.dbg.sctl.protocol.common.notify.SctlLoadNotification; +import ghidra.dbg.sctl.protocol.v2012base.x86.SctlX86Context; +import ghidra.dbg.sctl.protocol.v2012ext.SelSctl2012ExtPacket; +import ghidra.dbg.sctl.protocol.v2012ext.x86.linux.Sctl2012ExtLinuxX86Dialect; +import ghidra.util.NumericUtilities; + +public class SctlPacketsTest { + /* + * This one was giving me trouble. Decoding by hand seems to work. + */ + @Test + public void testDecode2012BusLoadNotification() + throws PacketDecodeException, PacketEncodeException { + String hexStr = "" + // + "10010000000000000300000000ffffff" + // ................ + "ffffffffff0004000000000000010000" + // ................ + "00000000000b000000000000002f7573" + // ............./us + "722f62696e2f6464ffffffffffffffff" + // r/bin/dd........ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "00000000000000000000000000000000" + // ................ + "0000000000000000"; // ........ + ByteBuffer inbuf = ByteBuffer.wrap(NumericUtilities.convertStringToBytes(hexStr)); + inbuf.order(ByteOrder.LITTLE_ENDIAN); + int len = (int) inbuf.getLong(); + assertEquals(272, inbuf.remaining()); + assertEquals(272, len); + + final PacketCodec codec = ByteBufferPacketCodec.getInstance(); + PacketFactory factory = Sctl2012ExtLinuxX86Dialect.INSTANCE.getPacketFactory(); + factory.registerTypes(codec); + + AbstractSelSctlPacket rcvd = + codec.decodePacket(AbstractSelSctlPacket.class, inbuf, factory); + + SelSctl2012ExtPacket exp = new SelSctl2012ExtPacket(0, + new SctlEventNotify(-1, + new SctlLoadNotification( + Arrays.asList(new PathBase[] { new PathBase("/usr/bin/dd", -1) }), + new SctlX86Context()))); + codec.encodePacket(exp); // Fill the auto fields + + assertEquals(exp, rcvd); + } +} diff --git a/Ghidra/Debug/Debugger/Module.manifest b/Ghidra/Debug/Debugger/Module.manifest new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Ghidra/Debug/Debugger/build.gradle b/Ghidra/Debug/Debugger/build.gradle new file mode 100644 index 0000000000..cff01868bb --- /dev/null +++ b/Ghidra/Debug/Debugger/build.gradle @@ -0,0 +1,22 @@ +apply from: "${rootProject.projectDir}/gradle/javaProject.gradle" +apply from: "${rootProject.projectDir}/gradle/helpProject.gradle" +apply from: "${rootProject.projectDir}/gradle/jacocoProject.gradle" +apply from: "${rootProject.projectDir}/gradle/javaTestProject.gradle" +apply from: "${rootProject.projectDir}/gradle/distributableGhidraModule.gradle" + +apply plugin: 'eclipse' +eclipse.project.name = 'Debug Debugger' + +dependencies { + compile project(':Framework-AsyncComm') + compile project(':Framework-Debugging') + compile project(':Framework-TraceModeling') + compile project(':Base') + compile project(':ByteViewer') + compile project(':ProposedUtils') + + helpPath project(path: ':Base', configuration: 'helpPath') + + testCompile project(path: ':Framework-Debugging', configuration: 'testArtifacts') + testCompile project(path: ':Framework-TraceModeling', configuration: 'testArtifacts') +} diff --git a/Ghidra/Debug/Debugger/certification.manifest b/Ghidra/Debug/Debugger/certification.manifest new file mode 100644 index 0000000000..fb03a3e939 --- /dev/null +++ b/Ghidra/Debug/Debugger/certification.manifest @@ -0,0 +1,183 @@ +##VERSION: 2.0 +##MODULE IP: FAMFAMFAM Icons - CC 2.5 +##MODULE IP: Modified Nuvola Icons - LGPL 2.1 +##MODULE IP: Nuvola Icons - LGPL 2.1 +##MODULE IP: Oxygen Icons - LGPL 3.0 +.classpath||NONE||reviewed||END| +.project||NONE||reviewed||END| +Module.manifest||GHIDRA||||END| +build.gradle||GHIDRA||||END| +data/ExtensionPoint.manifest||GHIDRA||||END| +src/main/help/help/TOC_Source.xml||GHIDRA||||END| +src/main/help/help/shared/arrow.gif||GHIDRA||||END| +src/main/help/help/shared/close16.gif||GHIDRA||||END| +src/main/help/help/shared/menu16.gif||GHIDRA||||END| +src/main/help/help/shared/note-red.png||Oxygen Icons - LGPL 3.0||||END| +src/main/help/help/shared/note.png||Oxygen Icons - LGPL 3.0||||END| +src/main/help/help/shared/note.yellow.png||Oxygen Icons - LGPL 3.0||||END| +src/main/help/help/shared/redo.png||GHIDRA||||END| +src/main/help/help/shared/tip.png||Oxygen Icons - LGPL 3.0||||END| +src/main/help/help/shared/undo.png||GHIDRA||||END| +src/main/help/help/shared/warning.png||Oxygen Icons - LGPL 3.0||||END| +src/main/help/help/topics/Debugger/Debugger.html||GHIDRA||||END| +src/main/help/help/topics/Debugger/GettingStarted.html||GHIDRA||||END| +src/main/help/help/topics/Debugger/Troubleshooting.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerBots/DebuggerBots.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerBreakpointMarkerPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerPlaceBreakpointDialog.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-clear.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-disable.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-enable.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-ed.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-disable-all.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-enable-all.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerGoToDialog.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerModuleImportDialog.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModuleMapProposalDialog.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModulesPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerSectionMapProposalDialog.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/DebuggerBreakpointDialog.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/DebuggerMethodInvocationDialog_ForLaunch.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/DebuggerObjectsPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/attach.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/blank.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/breakpoint-set.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/console.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/continue.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/debugger.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/detach.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/display_as_graph.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/display_as_table.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/display_as_tree.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/display_as_xml.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/display_filtered_graph.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/display_filtered_table.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/display_filtered_tree.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/display_filtered_xml.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/export_as_facts.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/export_as_xml.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/import_from_facts.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/import_from_xml.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/kill.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/launch.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/record.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/reload.png||Nuvola Icons - LGPL 2.1||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/stepinto.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/stepout.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/stepover.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerObjectsPlugin/images/stop.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerRegionsPlugin/DebuggerRegionsPlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerRegionsPlugin/images/DebuggerRegionsPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerRegistersPlugin/images/DebuggerAvailableRegistersDialog.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerRegistersPlugin/images/DebuggerRegistersPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerRegistersPlugin/images/select-registers.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerStackPlugin/DebuggerStackPlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerStackPlugin/images/DebuggerStackPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerStaticMappingPlugin/images/DebuggerStaticMappingPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerTargetsPlugin/DebuggerTargetsPlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerTargetsPlugin/images/DebuggerConnectDialog.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerTargetsPlugin/images/DebuggerTargetsPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerTargetsPlugin/images/connect.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerTargetsPlugin/images/disconnect.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerThreadsPlugin/images/DebuggerThreadsPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerThreadsPlugin/images/continue.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerThreadsPlugin/images/stepback.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerThreadsPlugin/images/stepinto.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html||GHIDRA||||END| +src/main/help/help/topics/DebuggerTimePlugin/images/DebuggerTimePlugin.png||GHIDRA||||END| +src/main/resources/defaultTools/Debugger.tool||GHIDRA||||END| +src/main/resources/define_info_proc_mappings||GHIDRA||||END| +src/main/resources/images/add.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| +src/main/resources/images/attach.png||GHIDRA||||END| +src/main/resources/images/autoread.png||GHIDRA||||END| +src/main/resources/images/blank.png||GHIDRA||||END| +src/main/resources/images/breakpoint-clear.png||GHIDRA||||END| +src/main/resources/images/breakpoint-disable.png||GHIDRA||||END| +src/main/resources/images/breakpoint-enable.png||GHIDRA||||END| +src/main/resources/images/breakpoint-mixed-de.png||GHIDRA||||END| +src/main/resources/images/breakpoint-mixed-ed.png||GHIDRA||||END| +src/main/resources/images/breakpoint-set.png||GHIDRA||||END| +src/main/resources/images/breakpoints-clear-all.png||GHIDRA||||END| +src/main/resources/images/breakpoints-disable-all.png||GHIDRA||||END| +src/main/resources/images/breakpoints-enable-all.png||GHIDRA||||END| +src/main/resources/images/breakpoints.png||GHIDRA||||END| +src/main/resources/images/closedFolder.png||Modified Nuvola Icons - LGPL 2.1||||END| +src/main/resources/images/connect.png||GHIDRA||||END| +src/main/resources/images/console.png||GHIDRA||||END| +src/main/resources/images/continue.png||GHIDRA||||END| +src/main/resources/images/debugger.png||GHIDRA||||END| +src/main/resources/images/debugger32.png||GHIDRA||||END| +src/main/resources/images/delete.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| +src/main/resources/images/detach.png||GHIDRA||||END| +src/main/resources/images/disconnect.png||GHIDRA||||END| +src/main/resources/images/kill.png||GHIDRA||||END| +src/main/resources/images/launch.png||GHIDRA||||END| +src/main/resources/images/modules.png||GHIDRA||||END| +src/main/resources/images/object-populated.png||GHIDRA||||END| +src/main/resources/images/object-running.png||GHIDRA||||END| +src/main/resources/images/object-unpopulated.png||GHIDRA||||END| +src/main/resources/images/process.png||GHIDRA||||END| +src/main/resources/images/record.png||GHIDRA||||END| +src/main/resources/images/register-marker.png||GHIDRA||||END| +src/main/resources/images/registers.png||GHIDRA||||END| +src/main/resources/images/select-registers.png||GHIDRA||||END| +src/main/resources/images/skipover.png||GHIDRA||||END| +src/main/resources/images/stack.png||GHIDRA||||END| +src/main/resources/images/stepback.png||GHIDRA||||END| +src/main/resources/images/stepinto.png||GHIDRA||||END| +src/main/resources/images/stepout.png||GHIDRA||||END| +src/main/resources/images/stepover.png||GHIDRA||||END| +src/main/resources/images/stop.png||GHIDRA||||END| +src/main/resources/images/sync_enabled.png||GHIDRA||||END| +src/main/resources/images/table.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| +src/main/resources/images/text-xml.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| +src/main/resources/images/thread.png||GHIDRA||||END| +src/main/resources/images/time.png||FAMFAMFAM Icons - CC 2.5||||END| +src/main/svg/attach.svg||GHIDRA||||END| +src/main/svg/blank.svg||GHIDRA||||END| +src/main/svg/breakpoint-clear.svg||GHIDRA||||END| +src/main/svg/breakpoint-disable.svg||GHIDRA||||END| +src/main/svg/breakpoint-enable.svg||GHIDRA||||END| +src/main/svg/breakpoint-mixed-de.svg||GHIDRA||||END| +src/main/svg/breakpoint-mixed-ed.svg||GHIDRA||||END| +src/main/svg/breakpoint-set.svg||GHIDRA||||END| +src/main/svg/breakpoints-clear-all.svg||GHIDRA||||END| +src/main/svg/breakpoints-disable-all.svg||GHIDRA||||END| +src/main/svg/breakpoints-enable-all.svg||GHIDRA||||END| +src/main/svg/breakpoints.svg||GHIDRA||||END| +src/main/svg/connect.svg||GHIDRA||||END| +src/main/svg/console.svg||GHIDRA||||END| +src/main/svg/continue.svg||GHIDRA||||END| +src/main/svg/debugger.svg||GHIDRA||||END| +src/main/svg/detach.svg||GHIDRA||||END| +src/main/svg/disconnect.svg||GHIDRA||||END| +src/main/svg/kill.svg||GHIDRA||||END| +src/main/svg/launch.svg||GHIDRA||||END| +src/main/svg/memory.svg||GHIDRA||||END| +src/main/svg/process.svg||GHIDRA||||END| +src/main/svg/recording.svg||GHIDRA||||END| +src/main/svg/register-marker.svg||GHIDRA||||END| +src/main/svg/registers.svg||GHIDRA||||END| +src/main/svg/select-registers.svg||GHIDRA||||END| +src/main/svg/skipover.svg||GHIDRA||||END| +src/main/svg/stack.svg||GHIDRA||||END| +src/main/svg/stepback.svg||GHIDRA||||END| +src/main/svg/stepinto.svg||GHIDRA||||END| +src/main/svg/stepout.svg||GHIDRA||||END| +src/main/svg/stepover.svg||GHIDRA||||END| +src/main/svg/stop.svg||GHIDRA||||END| +src/main/svg/thread.svg||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger/data/ExtensionPoint.manifest b/Ghidra/Debug/Debugger/data/ExtensionPoint.manifest new file mode 100644 index 0000000000..0275af0d94 --- /dev/null +++ b/Ghidra/Debug/Debugger/data/ExtensionPoint.manifest @@ -0,0 +1,5 @@ +DebuggerBot +DebuggerMappingOpinion +DebuggerModelFactory +DisassemblyInject + diff --git a/Ghidra/Debug/Debugger/ghidra_scripts/PopulateDemoTrace.java b/Ghidra/Debug/Debugger/ghidra_scripts/PopulateDemoTrace.java new file mode 100644 index 0000000000..1ab5fb60e7 --- /dev/null +++ b/Ghidra/Debug/Debugger/ghidra_scripts/PopulateDemoTrace.java @@ -0,0 +1,730 @@ + +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.*; + +import com.google.common.collect.Range; + +import ghidra.app.plugin.assembler.Assembler; +import ghidra.app.plugin.assembler.Assemblers; +import ghidra.app.script.GhidraScript; +import ghidra.app.services.DebuggerTraceManagerService; +import ghidra.program.model.address.*; +import ghidra.program.model.data.PointerDataType; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.CodeUnit; +import ghidra.program.model.listing.Instruction; +import ghidra.program.model.symbol.SourceType; +import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.program.util.DefaultLanguageService; +import ghidra.trace.database.DBTrace; +import ghidra.trace.model.Trace; +import ghidra.trace.model.listing.TraceCodeRegisterSpace; +import ghidra.trace.model.memory.*; +import ghidra.trace.model.symbol.TraceLabelSymbol; +import ghidra.trace.model.symbol.TraceNamespaceSymbol; +import ghidra.trace.model.thread.TraceThread; +import ghidra.trace.model.time.TraceSnapshot; +import ghidra.util.database.UndoableTransaction; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * This script populates a trace database for demonstrations purposes and opens it in the current + * tool. + * + *

+ * Your current tool had better be the "TraceBrowser"! The demonstration serves two purposes. 1) It + * puts interesting data into the TraceBrowser and leaves some annotations as an exercise. 2) It + * demonstrates how a decent portion the Trace API works. + * + *

+ * A Trace is basically a collection of observations of memory and registers over the lifetime of an + * application or computer system. In Ghidra, the Trace object also supports many of the same + * annotations as does Program. In the same way that Program brings knowledge markup to an image of + * bytes, Trace brings knowledge markup to bytes observed over time. + * + *

+ * Effectively, if you take the cross-product of Program with time and add Threads, Breakpoints, + * etc., you get Trace. It's a lot. In order to use all the UI components which take a Program, + * Trace can present itself as a Program at a particular point in time. + * + *

+ * Each particular component will be introduced as its used in the script below, but for now some + * core concepts: + * + *

    + *
  • A point in time is called a "snap." These don't necessarily correspond to any real unit of + * time, though they may. The only requirement is that they are numbered in chronological + * order.
  • + *
  • Every annotation has a "lifespan," which is the range of snaps for which the annotation is + * effective. Some annotations may overlap, others may not. In general, if the corresponding concept + * in Program permits address overlap, then Trace permits both address and time overlap. If not, + * then neither is permitted. In essense, Trace defines overlap as the intersection of rectangles, + * where an annotation's X dimension is it's address range, and its Y dimension is its lifespan. + *
  • + *
  • Observations in memory happen at a particular snap and are assumed in effect until another + * observation changes that. To record the "freshness" of observations, the memory manager tags + * regions as KNOWN, UNKNOWN, or ERROR. An observation implicitly marks the affected region as + * KNOWN. The intent is to grey the background for regions where memory is UNKNOWN for the current + * snap.
  • + *
  • Observations of registers behave exactly the same as observations for memory, by leveraging + * Ghidra's "register space." The only difference is that those observations must be recorded with + * respect to a given thread. Each thread is effectively allocated its own copy of the register + * space. Most the the API components require you to obtain a special "register space" for a given + * thread before recording observations of or applying annotations to that thread.
  • + *
+ * + *

+ * After you've run this script, a trace should appear in the UI. Note that there is not yet a way + * to save a trace in the UI. As an exercise, try adding data units to analyze the threads' stacks. + * It may take some getting accustomed to, but the rules for laying down units should be very + * similar to those in a Program. However, the Trace must take the applied units and decide how far + * into the future they are effective. In general, it defaults to "from here on out." However, two + * conditions may cause the trace to choose an ending tick: 1) The underlying bytes change sometime + * in the future, and 2) There is an overlapping code unit sometime in the future. + * + *

+ * The trace chooses the latest tick possible preceding any byte change or existing code unit, so + * that the unit's underlying bytes remain constant for its lifespan, and the unit does not overlap + * any existing unit. This rule causes some odd behavior for null-terminated strings. I intend to + * adjust this rule slightly for static data types wrt/ byte changes. For those, the placed unit + * should be truncated as described above, however, another data unit of the same type can be placed + * at the change. The same rule is then applied iteratively into the future until an overlapping + * unit is encountered, or there are no remaining byte changes. + */ +public class PopulateDemoTrace extends GhidraScript { + + /** + * The Memory APIs all use Java NIO ByteBuffer. While it has it can sometimes be annoying, it + * provides most of the conveniences you'd need for packing arbitrary data into a memory buffer. + * I'll allocate one here large enough to write a couple values at a time. + */ + private ByteBuffer buf = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN); + + /** + * I will imagine the execution of two threads, so I'll need a stack for each. + */ + private static final long STACK1_BOTTOM = 0x001f0000; + private static final long STACK2_BOTTOM = 0x002f0000; + + /** + * Objects I will need to construct the trace, and a few convenient handles to keep around. + */ + private LanguageService langServ; + private Language x86Lang; + private CompilerSpec cspec; + private Trace trace; + private TraceMemoryManager memory; + private AddressSpace defaultSpace; + private TraceNamespaceSymbol global; + + /** + * Labels I will place in the trace, and whose addresses I'll use instead of hardcoding. + * + * Note that the other symbol types are implemented, but not demonstrated here as they haven't + * been tested in the UI. + */ + private TraceLabelSymbol mainLabel; + private TraceLabelSymbol cloneLabel; + private TraceLabelSymbol childLabel; + private TraceLabelSymbol exitLabel; + + /** + * Fields to store the handle to the first (main) thread and its registers + */ + private TraceThread thread1; + private TraceMemoryRegisterSpace regs1; + + /** + * Fields to store the handle to the second (cloned) thread and its registers + */ + private TraceThread thread2; + private TraceMemoryRegisterSpace regs2; + + /** + * Create an address in the processor's (x86_64) default space. + * + * @param offset the byte offset + * @return the address + */ + protected Address addr(long offset) { + return defaultSpace.getAddress(offset); + } + + /** + * Create an address range in the processor's default space. + * + * @param min the minimum byte offset + * @param max the maximum (inclusive) byte offset + * @return the range + */ + protected AddressRange rng(long min, long max) { + return new AddressRangeImpl(addr(min), addr(max)); + } + + /** + * Get an x86_64 register by name + * + * @param name the name + * @return the register + */ + protected Register reg(String name) { + return x86Lang.getRegister(name); + } + + /** + * Set RIP at the given tick for the given space to the address of a given instruction + * + * @param tick the tick + * @param regs the register space for a given thread + * @param ins the instructions + */ + protected void putRIP(long tick, TraceMemoryRegisterSpace regs, Instruction ins) { + regs.setValue(tick, + new RegisterValue(reg("RIP"), ins.getAddress().getOffsetAsBigInteger())); + } + + /** + * (Re-)place a data unit if necessary. + * + * This is a TODO item in the Trace database. Currently, the API allows a caller to modify bytes + * in the middle of a code unit's lifespan. The intended rule is: If the modification is at the + * unit's start tick, the behavior should be the same as Program (permit changes under static + * data types only). If the modification is after, then the code unit's lifespan should be + * truncated to allow the modification. Additionally, for static data types, a new unit should + * be placed to fill out the old lifespan. + * + * Because that TODO is not implemented yet, this method corrects the issue by implementing + * effectively the same rule. + * + * @param tick the tick where a register change may have occurred + * @param thread the thread whose registers to check/correct + * @param reg the register to check/correct + * @throws CodeUnitInsertionException shouldn't happen + * @throws CancelledException shouldn't happen + */ + protected void placeRegUnitIfNeeded(long tick, TraceThread thread, Register reg) + throws CodeUnitInsertionException, CancelledException { + // NOTE: This is compensating for a TODO in the memory and code managers + + // TODO: Consider convenience methods TraceThread#getMemorySpace(boolean), etc + TraceMemoryRegisterSpace mem = + thread.getTrace().getMemoryManager().getMemoryRegisterSpace(thread, true); + // First check if the value was set at all + if (mem.getState(tick, reg) != TraceMemoryState.KNOWN) { + return; + } + // The value may have been set, but not changed + RegisterValue oldValue = mem.getValue(tick - 1, reg); + RegisterValue newValue = mem.getValue(tick, reg); + if (Objects.equals(oldValue, newValue)) { + return; + } + + TraceCodeRegisterSpace code = + thread.getTrace().getCodeManager().getCodeRegisterSpace(thread, true); + code.definedUnits().clear(Range.atLeast(tick), reg, TaskMonitor.DUMMY); + code.definedData().create(Range.atLeast(tick), reg, PointerDataType.dataType); + } + + /** + * Invoke the above method for the three registers I modify during this demonstration. + * + * @param tick the tick where the registers may have changed + * @param thread the thread to check/correct + * @throws CodeUnitInsertionException shouldn't happen + * @throws CancelledException shoudn't happen + */ + protected void placeRegUnits(long tick, TraceThread thread) + throws CodeUnitInsertionException, CancelledException { + placeRegUnitIfNeeded(tick, thread, reg("RIP")); + placeRegUnitIfNeeded(tick, thread, reg("RSP")); + placeRegUnitIfNeeded(tick, thread, reg("RBP")); + } + + @Override + protected void run() throws Exception { + /** + * Construct a Trace with x86_64 and the default compiler + */ + langServ = DefaultLanguageService.getLanguageService(); + x86Lang = langServ.getLanguage(new LanguageID("x86:LE:64:default")); + defaultSpace = x86Lang.getAddressFactory().getDefaultAddressSpace(); + cspec = x86Lang.getDefaultCompilerSpec(); + trace = new DBTrace("Demo", cspec, this); + + /** + * Grab the memory manager and global namespace symbol for convenience + */ + memory = trace.getMemoryManager(); + global = trace.getSymbolManager().getGlobalNamespace(); + + /** + * A list of instructions in the main function for ease of setting RIP + */ + List mainInstructions = new ArrayList<>(); + + /** + * Instead of tracking this by hand, use variables! + */ + int stack1offset = 0; + int stack2offset = 0; + int pc1 = 0; + int pc2 = 0; + + /** + * For clarity, I will add each tick to the trace in its own transaction. The + * UndoableTransaction class eases the syntax and reduces errors in starting and ending + * transactions. This Utility deprecates ProgramTransaction, as it can be used on any domain + * object. + */ + try (UndoableTransaction tid = + UndoableTransaction.start(trace, "Populate First Snapshot", true)) { + /** + * While not strictly required, each tick should be explicitly added to the database and + * given a description. Some things may mis-behave if there does not exist at least one + * tick. + */ + TraceSnapshot snapshot = trace.getTimeManager().createSnapshot("Launched"); + long snap = snapshot.getKey(); + + /** + * Add two regions to the trace: ".text" for the program instructions and "[STACK 1]" + * for the main thread's stack. Again, while not strictly required all memory + * observations should occur within a recorded region. As regions may come and go + * through the course of execution, this is the first place where we see a lifespan. + * These are represented using a Range object from Guava. While you may specify open or + * closed ends, the range will be normalized to use closed ends. Also Long.MIN and + * Long.MAX will be normalized to negative and positive infinity, respectively. In + * general, observations should be given the range [currentTick..INF) meaning "from here + * on out". Most annotations allow mutation of the end tick. + * + * The trace database DOES permit recording and retrieving observations outside any + * recorded region. However, when viewed as a Program, only the current regions are + * presented as memory blocks. Thus, observations outside a region are not visible in + * the UI. + */ + memory.addRegion(".text", Range.atLeast(snap), rng(0x00400000, 0x00400fff), + TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); + memory.addRegion("[STACK 1]", Range.atLeast(snap), rng(0x00100000, 0x001effff), + TraceMemoryFlag.READ, TraceMemoryFlag.WRITE); + + /** + * Create the main thread, assumed alive from here on out. + */ + thread1 = trace.getThreadManager().addThread("Thread 1", Range.atLeast(snap)); + /** + * Get a handle to the main thread's register values. + * + * Note that the values are accessed via the memory manager. The memory implementation + * is re-used to record register values, since registers to Ghidra are just special + * addresses. As a convenience, the register-space interface of the memory manager + * presents conveniences which use Register and RegisterValue instead of Address and + * ByteBuffer. + */ + regs1 = memory.getMemoryRegisterSpace(thread1, true); + + /** + * Place these labels. I guessed and checked as far as positions, go. There just needs + * to be enough room in main for the assembled instructions below. "child" has to be + * placed more carefully. Perhaps in a later version of this demo, I will upgrade main, + * clone, and exit to actual functions. + */ + mainLabel = trace.getSymbolManager() + .labels() + .create(snap, null, addr(0x00400000), + "main", global, SourceType.USER_DEFINED); + cloneLabel = trace.getSymbolManager() + .labels() + .create(snap, null, addr(0x00400060), + "clone", global, SourceType.USER_DEFINED); + childLabel = trace.getSymbolManager() + .labels() + .create(snap, null, addr(0x00400034), + "child", global, SourceType.USER_DEFINED); + exitLabel = trace.getSymbolManager() + .labels() + .create(snap, null, addr(0x00400061), + "exit", global, SourceType.USER_DEFINED); + + /** + * Note the use of getProgramView as a means of using components intended for Program + * with Trace. Such views typically have a fixed tick, however, you can also obtain a + * view which can seek to a different tick. The variable-tick version is generally for + * UI compatibility, whereas the fixed-tick version is generally for API compatibility. + * Here we use it to apply the assembler. + * + * This is the "main" function of the demonstration. it is imagined with a decent bit of + * fidelity, but some bits are elided for my sanity and to stay succinct. + * + * It starts with the typical stack frame setup and then immediately clones a second + * thread. I arbitrarily decided which thread would execute for each step. The cloned + * thread then jumps to the "child" portion of the code while the main thread falls + * through. Each places some data on its stack and then terminate. The "clone" and + * "exit" functions are stubbed out, but one should imagine they are system calls. + * + * A call to "clone" results in the creation of a second thread with stack. That stack + * contains only the return address. The caller's RAX is set to 0, the clone's RAX is + * set to 1. + * + * A call to "exit" results in the immediate termination of the calling thread. + */ + Assembler asm = Assemblers.getAssembler(trace.getFixedProgramView(snap)); + InstructionBlock mainBlock = asm.assemble(mainLabel.getAddress(), // + "PUSH RBP", // + "MOV RBP,RSP", // + "CALL clone", // + "TEST EAX,EAX", // + "JNZ child", // + "SUB RSP,0x10", // + "MOV dword ptr [RSP],0x6c6c6548", // + "MOV dword ptr [RSP+4],0x57202c6f", // + "MOV dword ptr [RSP+8],0x646c726f", // + "MOV word ptr [RSP+0xc],0x21", // + "CALL exit", // + "SUB RSP,0x10", // + "MOV dword ptr [RSP],0x2c657942", // + "MOV dword ptr [RSP+4],0x726f5720", // + "MOV dword ptr [RSP+8],0x21646c", // + "CALL exit" // + ); + mainBlock.forEach(mainInstructions::add); + + /** + * Stub out "clone" + */ + asm.assemble(cloneLabel.getAddress(), "RET"); + trace.getCodeManager() + .codeUnits() + .getAt(0, cloneLabel.getAddress()) + .setComment( + CodeUnit.EOL_COMMENT, "Pretend this is a syscall"); + + /** + * Stub out "exit" + */ + asm.assemble(exitLabel.getAddress(), "HLT"); + trace.getCodeManager() + .codeUnits() + .getAt(0, exitLabel.getAddress()) + .setComment( + CodeUnit.EOL_COMMENT, "Pretend this is a syscall"); + + /** + * "Launch" the program by initializing RIP and RSP of the main thread + */ + putRIP(snap, regs1, mainInstructions.get(pc1)); + regs1.setValue(snap, + new RegisterValue(reg("RSP"), BigInteger.valueOf(STACK1_BOTTOM + stack1offset))); + + placeRegUnits(snap, thread1); + } + + /** + * Just hand emulate the stepping + */ + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = trace.getTimeManager().createSnapshot("Stepped: PUSH RBP").getKey(); + + stack1offset -= 8; + putRIP(snap, regs1, mainInstructions.get(++pc1)); + /** + * This demonstrates recording a memory observation, i.e., writing to trace memory. + * + * The ByteBuffer API can be a bit annoying, but it generally follows the same pattern, + * and it permits packing an arbitrary number of "fields" into the buffer (limited by + * the allocated size of the buffer). If this API is too inconvenient, you can also use + * getBufferAt, to use the familiar MemBuffer API. + */ + memory.putBytes(snap, addr(STACK1_BOTTOM - 8), buf.clear().putLong(0).flip()); + /** + * Since register "memory" is just an extension of physical memory, the same API is + * available for recording register observations. + */ + regs1.putBytes(snap, reg("RSP"), + buf.clear().putLong(STACK1_BOTTOM + stack1offset).flip()); + + placeRegUnits(snap, thread1); + } + + /** + * More hand emulation + */ + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = trace.getTimeManager().createSnapshot("Stepped: MOV RBP,RSP").getKey(); + + putRIP(snap, regs1, mainInstructions.get(++pc1)); + regs1.putBytes(snap, reg("RBP"), + buf.clear().putLong(STACK1_BOTTOM + stack1offset).flip()); + + placeRegUnits(snap, thread1); + } + + /** + * Emulate the clone "syscall" + * + * While this is a complicated call, there is nothing new to demonstrate in its + * implementation. As an exercise, see if you can follow what is happening within. + */ + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = trace.getTimeManager() + .createSnapshot("Stepped Thread 1: CALL clone -> Thread 2") + .getKey(); + + memory.addRegion("[STACK 2]", Range.atLeast(snap), rng(0x00200000, 0x002effff), + TraceMemoryFlag.READ, TraceMemoryFlag.WRITE); + + thread2 = trace.getThreadManager().addThread("Thread 2", Range.atLeast(snap)); + regs2 = memory.getMemoryRegisterSpace(thread2, true); + + stack1offset -= 8; + regs1.putBytes(snap, reg("RIP"), + buf.clear().putLong(cloneLabel.getAddress().getOffset()).flip()); + regs1.putBytes(snap, reg("RSP"), + buf.clear().putLong(STACK1_BOTTOM + stack1offset).flip()); + regs1.putBytes(snap, reg("RAX"), buf.clear().putLong(0).flip()); + memory.putBytes(snap, addr(STACK1_BOTTOM + stack1offset), + buf.clear().putLong(mainInstructions.get(++pc1).getAddress().getOffset()).flip()); + + stack2offset -= 8; + regs2.putBytes(snap, reg("RIP"), + buf.clear().putLong(cloneLabel.getAddress().getOffset()).flip()); + regs2.putBytes(snap, reg("RSP"), + buf.clear().putLong(STACK2_BOTTOM + stack2offset).flip()); + regs2.putBytes(snap, reg("RAX"), buf.clear().putLong(1).flip()); + memory.putBytes(snap, addr(STACK2_BOTTOM + stack2offset), buf.clear() + .putLong( + mainInstructions.get(pc2 = pc1).getAddress().getOffset()) + .flip()); + + placeRegUnits(snap, thread1); + placeRegUnits(snap, thread2); + } + + /** + * Hand emulate thread1 a few steps + */ + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 1: RET from clone").getKey(); + + stack1offset += 8; + putRIP(snap, regs1, mainInstructions.get(pc1)); + regs1.putBytes(snap, reg("RSP"), + buf.clear().putLong(STACK1_BOTTOM + stack1offset).flip()); + + placeRegUnits(snap, thread1); + } + + /** + * ... + */ + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 1: TEST EAX,EAX").getKey(); + + putRIP(snap, regs1, mainInstructions.get(++pc1)); + regs1.putBytes(snap, reg("ZF"), buf.clear().put((byte) 1).flip()); + + placeRegUnits(snap, thread1); + } + + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 1: JNZ child").getKey(); + + putRIP(snap, regs1, mainInstructions.get(++pc1)); + + placeRegUnits(snap, thread1); + } + + /** + * Switch to thread2 + */ + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 2: RET from clone").getKey(); + + stack2offset += 8; + putRIP(snap, regs2, mainInstructions.get(pc2)); + regs2.putBytes(snap, reg("RSP"), + buf.clear().putLong(STACK2_BOTTOM + stack2offset).flip()); + + placeRegUnits(snap, thread2); + } + + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 2: TEST EAX,EAX").getKey(); + + putRIP(snap, regs2, mainInstructions.get(++pc2)); + regs2.putBytes(snap, reg("ZF"), buf.clear().put((byte) 0).flip()); + + placeRegUnits(snap, thread2); + } + + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 2: JNZ child").getKey(); + + putRIP(snap, regs2, mainInstructions.get(pc2 = 11)); + + placeRegUnits(snap, thread2); + } + + /** + * Switch to thread1 + */ + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 1: SUB RSP,0x10").getKey(); + + stack1offset -= 0x10; + putRIP(snap, regs1, mainInstructions.get(++pc1)); + regs1.putBytes(snap, reg("RSP"), + buf.clear().putLong(STACK1_BOTTOM + stack1offset).flip()); + + placeRegUnits(snap, thread1); + } + + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 1: MOV...(1)").getKey(); + + putRIP(snap, regs1, mainInstructions.get(++pc1)); + memory.putBytes(snap, addr(STACK1_BOTTOM + stack1offset + 0), + buf.clear().putInt(0x6c6c6548).flip()); + + placeRegUnits(snap, thread1); + } + + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 1: MOV...(2)").getKey(); + + putRIP(snap, regs1, mainInstructions.get(++pc1)); + memory.putBytes(snap, addr(STACK1_BOTTOM + stack1offset + 4), + buf.clear().putInt(0x57202c6f).flip()); + + placeRegUnits(snap, thread1); + } + + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 1: MOV...(3)").getKey(); + + putRIP(snap, regs1, mainInstructions.get(++pc1)); + memory.putBytes(snap, addr(STACK1_BOTTOM + stack1offset + 8), + buf.clear().putInt(0x646c726f).flip()); + + placeRegUnits(snap, thread1); + } + + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 1: MOV...(4)").getKey(); + + putRIP(snap, regs1, mainInstructions.get(++pc1)); + memory.putBytes(snap, addr(STACK1_BOTTOM + stack1offset + 0xc), + buf.clear().putShort((short) 0x21).flip()); + + placeRegUnits(snap, thread1); + } + + /** + * Switch to thread2 + */ + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 2: SUB RSP,0x10").getKey(); + + stack2offset -= 0x10; + putRIP(snap, regs2, mainInstructions.get(++pc2)); + regs2.putBytes(snap, reg("RSP"), + buf.clear().putLong(STACK2_BOTTOM + stack2offset).flip()); + + placeRegUnits(snap, thread2); + } + + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 2: MOV...(1)").getKey(); + + putRIP(snap, regs2, mainInstructions.get(++pc2)); + memory.putBytes(snap, addr(STACK2_BOTTOM + stack2offset + 0), + buf.clear().putInt(0x2c657942).flip()); + + placeRegUnits(snap, thread2); + } + + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 2: MOV...(2)").getKey(); + + putRIP(snap, regs2, mainInstructions.get(++pc2)); + memory.putBytes(snap, addr(STACK2_BOTTOM + stack2offset + 4), + buf.clear().putInt(0x726f5720).flip()); + + placeRegUnits(snap, thread2); + } + + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 2: MOV...(3)").getKey(); + + putRIP(snap, regs2, mainInstructions.get(++pc2)); + memory.putBytes(snap, addr(STACK2_BOTTOM + stack2offset + 8), + buf.clear().putInt(0x21646c).flip()); + + placeRegUnits(snap, thread2); + } + + /** + * Let thread2 exit first + */ + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 2: CALL exit").getKey(); + + thread2.setDestructionSnap(snap); + } + + /** + * Terminate + */ + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Step", true)) { + long snap = + trace.getTimeManager().createSnapshot("Stepped Thread 1: CALL exit").getKey(); + + thread1.setDestructionSnap(snap); + } + + /** + * Give a program view to Ghidra's program manager + * + * NOTE: Eventually, there will probably be a TraceManager service as well, but to use the + * familiar UI components, we generally take orders from the ProgramManager. + */ + DebuggerTraceManagerService manager = + state.getTool().getService(DebuggerTraceManagerService.class); + manager.openTrace(trace); + manager.activateTrace(trace); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml b/Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml new file mode 100644 index 0000000000..9cd0c3ccf4 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/shared/Frontpage.css b/Ghidra/Debug/Debugger/src/main/help/help/shared/Frontpage.css new file mode 100644 index 0000000000..c8616e85e8 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/shared/Frontpage.css @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + WARNING! + This file is copied to all help directories. If you change this file, you must copy it + to each src/main/help/help/shared directory. + + + Java Help Note: JavaHelp does not accept sizes (like in 'margin-top') in anything but + px (pixel) or with no type marking. + +*/ + +body { margin-bottom: 50px; margin-left: 10px; margin-right: 10px; margin-top: 10px; } /* some padding to improve readability */ +li { font-family:times new roman; font-size:14pt; } +h1 { color:#000080; font-family:times new roman; font-size:36pt; font-style:italic; font-weight:bold; text-align:center; } +h2 { margin: 10px; margin-top: 20px; color:#984c4c; font-family:times new roman; font-size:18pt; font-weight:bold; } +h3 { margin-left: 10px; margin-top: 20px; color:#0000ff; font-family:times new roman; `font-size:14pt; font-weight:bold; } +h4 { margin-left: 10px; margin-top: 20px; font-family:times new roman; font-size:14pt; font-style:italic; } + +/* + P tag code. Most of the help files nest P tags inside of blockquote tags (the was the + way it had been done in the beginning). The net effect is that the text is indented. In + modern HTML we would use CSS to do this. We need to support the Ghidra P tags, nested in + blockquote tags, as well as naked P tags. The following two lines accomplish this. Note + that the 'blockquote p' definition will inherit from the first 'p' definition. +*/ +p { margin-left: 40px; font-family:times new roman; font-size:14pt; } +blockquote p { margin-left: 10px; } + +p.providedbyplugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px } +p.ProvidedByPlugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px } +p.relatedtopic { color:#800080; margin-left: 10px; font-size:14pt; } +p.RelatedTopic { color:#800080; margin-left: 10px; font-size:14pt; } + +/* + We wish for a tables to have space between it and the preceding element, so that text + is not too close to the top of the table. Also, nest the table a bit so that it is clear + the table relates to the preceding text. +*/ +table { margin-left: 20px; margin-top: 10px; width: 80%;} +td { font-family:times new roman; font-size:14pt; vertical-align: top; } +th { font-family:times new roman; font-size:14pt; font-weight:bold; background-color: #EDF3FE; } + +/* + Code-like formatting for things such as file system paths and proper names of classes, + methods, etc. To apply this to a file path, use this syntax: + ... +*/ +code { color: black; font-weight: bold; font-family: courier new, monospace; font-size: 14pt; white-space: nowrap; } +code.path { color: #4682B4; font-weight: bold; font-family: courier new, monospace; font-size: 14pt; white-space: nowrap; } + +.menu { background-color: #EDF3FE; } + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/shared/arrow.gif b/Ghidra/Debug/Debugger/src/main/help/help/shared/arrow.gif new file mode 100644 index 0000000000000000000000000000000000000000..bcb3db70578a43a6aef37fbb13aeb0b3b0ca81be GIT binary patch literal 69 zcmZ?wbhEHb6k_0GXkY+=|Ns9h{$ycfU|?j>0r5dH3`{aT{VPwu<>#Lfcu7j_z|Sb& THV==isgesP{Mz8g$Y2csRcRCu literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/shared/close16.gif b/Ghidra/Debug/Debugger/src/main/help/help/shared/close16.gif new file mode 100644 index 0000000000000000000000000000000000000000..f5238646392f8e1bc4b29109b96a7244fbf17182 GIT binary patch literal 859 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 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/shared/note.png b/Ghidra/Debug/Debugger/src/main/help/help/shared/note.png new file mode 100644 index 0000000000000000000000000000000000000000..51e1c8f8c1fa1688a64436ed366e3dba15a6b750 GIT binary patch literal 4193 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 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/shared/note.yellow.png b/Ghidra/Debug/Debugger/src/main/help/help/shared/note.yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..f8ab6489f2afca01576947572918d51cf3fa7978 GIT binary patch literal 4170 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 P&=3YsS3j3^P6Eyh)6Gs@ zkuH#=l-3Vj=tBr?aOgrIebHNKUeXuSHqbT@2!=vwFqBgK(n6XND2+?n*xB65R%I!c zEJasWSJK>V*7JR3lvk7V-devo-{C9+^W$SU3yBE&Vs>1~WBzdl`_>`vO$=Yn(FeT= zTsd0J!|*geF-0XPEei}QHlu!5D9h>f=D5v z(44pM#t%qrb6Gzad>#WUj1NHJL*Tz1JS*Mkk!$0hJIInfzkBttS zdJ%2*E3JI;%NrEMC-N)#Pw*jVd;s$1kuli`pUJAjYvNilnRO;600g4jyf3>o*mFAh zz_8I_bL6Si&y&+;EkR0#wn#V@pUoT^|qBR5R|W=2m+GZC_qdsEK8x9cvQ*C ztAnVo{RY4TeF-20K!n6}44Xzq#cc3^ zi>Z53di$9p`1hobkG>oA^bg1zN5{x;nFc>)LpHmFj4ki5Me-G>qO_+PjU5^us5ONg6~|;bbhArT)*wwNO)$d0-raKsNLlkBkZHZr zAUp1Y1X|{sjmA)ExY~(a2n?$yCv-fqN z;#{FDbC4{gVzzT42q}PIgpe~4bESfGtHXLAh>~Ki-PN|ETPX;DbaRR4nrK}ISv1$P z0NuN|NLtmcxa$)$2m%6Ru+oZ_*Tg3haXsR4+0Bb~HxlL8#sGq%k8C63)t{|X_^*sC z{GqRvkV^iq)z;RS%2OMF6h#pWGj_XFJZg7b-rLCCeU&5uiJ-uwO6}B!;M;%3$(6J} zfvofk%9EU0VHKP!K%|HRqa4pKMqbe-Frt)|w!j1yD#XNqI;}vp!U_fcZ2x<~=Wb+F z9hg;H^@EheE`)a-JXd(Vx!=FB>X)vrx}|IHmdcZdR3su0VMr1?s$M$?-+uuoS8D(F zZYVDgy~IIqfIo8As&bqhMAGVNT`6-;AT&a44^^`ag+OK$)oY_v-zd>cc8|5G|He%AQutjm?QM0*^v^9rME;sf>;PvH^i{onJTUg$_*OL|*TO%Ms znSsunfh65L_9ven1+XUqKYnqdwswo1r=I*OMu$AiUw#$L2FFj#;ERtwj>pdaP=Dd! zGnb#(D*-lUrPYb~?S&mRIdvHM{)a#yln$N5%9g@*GeHzZx6lI$y+Fw0Bte>{@IAjU zf~7U?zyAbU%_i#gI@;~FgT2ZCfl`wjilP`vnqYZ(8QS#$0!l#uOQ|9Q_MT;+I0AW| zmz9DN3LgLV6R7SR0yAT2X$g(3h7eJ6&+di=r$2q_*iP7d>DaMj<3TBa6k=KdfiySY ns8$E)H%9 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/shared/undo.png b/Ghidra/Debug/Debugger/src/main/help/help/shared/undo.png new file mode 100644 index 0000000000000000000000000000000000000000..edd57f5c15f02bf0fd9b572f42bcd1c67ed088ef GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFP2=EDU{r~?zkjc=%(9i&)fHaU; zv}jR7Ljy?SKLc1ENPt*CpgCP>15li?B*-tA!Qt7BG$6;^)5S5Q;#P8k0^bJiY0RE( z3b71HhFly>I~~qwL@)^HGZ?WrykcqKnIK|4gHc4QMe2Zt!xUczBN65o6N)Xj0gYhr MboFyt=akR{0Pznvr~m)} literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/shared/warning.png b/Ghidra/Debug/Debugger/src/main/help/help/shared/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..8e9133789b0c5f9a2fd7cfd29fce85a35e8fa120 GIT binary patch literal 1354 zcmV-Q1-1H#P)E2L1Q&_V>FBm^-K zqXA4L(MT}7Nc71>ebr!yF~$TFjp&0f`pyGBX@Z#WqA!LtVgSFQAzIM3(C+TcZ0GZO zr#p3;-Bd|wC7$G$b29gyIsbEJ=VM7qiF;{G@jn)DmiRn(VavY+pHdOM#pCd2YY1=- z`{e9@C*VBgBAtI7wVxORB-rf7msy*?8#|=4`j_A$iAZBF26pGD=y)2U?J$BFeKfMf zO91iJ7Z4yPUG}nQe~Msq0GS?$j$;(rcm)@bUo`<~_u|#i>*`{?2LZ1-59@mjp;PFD z5GUh3t0$m_+Wvzj{;E*fm=gVS2z~@xf&2v!E);%(Utdo_FkN`asqs;^&NVGm-YjZ zHD8RgsuEx!bZX$d3dooWc>69TiVnd=-^vI$OL}0j_emel@=PBPHHvYe1gPRtO+n}t z05yd=Zsq|okO^$|VVrw+0*DA5*bvs~QQo;5AWM-HQblFmR#-|v6`%ynK$M0M*mNMy z1raS@z-Q3?fSo~htIo|#Tv$q23IQIiwfwoKmL}=5t zdlug#%3A=cGz72)+^B*)3*=j>V5M=KC+jo|t=tR0hCR5YZ3C8`g1}Pt2@J0syvy^$ z8W&RlbU-rz5{e8U0+{6-h--kZ1Gxx*Y#;$n1e%%<-t5E~{d4Ta<8bd>z$s$bCm!~0 zuwCYT)4<#h044LHm^jG8`z;UY+JI95++-~_*>GBSH|kg9PS(Tsic|C@UYF6G2^fJ< zzZJgC+IqMs{Qh_0)JMu!k?Cr53$8GcSSmmm(_p1;=FLu?79)5FK>ZGW1H{5ke%QO6 z9n*VVKt}^3!A!gc^gmle|M6O~-h{5uLYPAuyh$1j+xVe)vj6@;a+9*w7Z|5C8-{b6 zeg;_5Axoe%c7D{sG7{!Gf1&Hxr$n)5u*U}=r>_5$--_+cA1$o;8_yqS$NWmiMsr;i z&=xvLSk&pvg&c04E@I-160C7^q3d+NNZ=7N_Y>SnZKHpK9R-LjEnpBi=em4N-`GdI zFbN2xXsM5j`M(Nq!aUwDT3VPdniw@;+3PU3u7^7JNnAy?NkH1YJHcKEoSs2*Xfj@D zVeyIE57uFHsqO|s6b{sZ2(8G4SR%72fIDpt)~~VW<65o1fs0g}`G&3;+aBo2Zoag* zUfFaVm+a#!;ASmrLHfnyzq&|rg(*3RZ1bA7P8?peaN3ECO zD&^Ek#qSEtfcW`0mcBAnf{sc=AeHhz8zbi17A@AzSsdxu0^wj3XXKM zTq0&F4i~B>F$-lpVE{Lf$7PI3yJ-SV?|~5zR>Q7BHop(<`QP)u0Nfb>!wJQD)Bpeg M07*qoM6N<$g1EwNvj6}9 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/Debugger.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/Debugger.html new file mode 100644 index 0000000000..60c567bbe7 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/Debugger.html @@ -0,0 +1,69 @@ + + + + + + + Ghidra Debugger + + + + + +

Ghidra Debugger

+ +

The Debugger is a collection of plugins comprising Ghidra's Dynamic Analysis Framework. This + includes a platform for connecting to and controlling debuggers. Ghidra is not a debugger in + itself, but rather, it relies on existing 3rd-party debuggers, their APIs, wire protocols, + and/or command-line interfaces. Such connectors are pluggable, allowing Ghidra to be extended + and integrated with additional debuggers.

+ +

When Ghidra recognizes the platform of a target in a connected debugger, it can record that + target into a local database and display the target state. Without recording, the UI will at + least allow interaction through a generic model and/or the debugger's command-line interface. + The recording, called a Trace in Ghidra, logs all the observations made by the framework or the + user. The user can rewind this recording at any point and the UI will recall those + observations, displaying the recorded machine state instead of the present machine state. These + traces can also be saved, loaded, and analyzed after a target has terminated or been + disconnected. Furthermore, they can be commited to a Ghidra Server for sharing and revision + control; however, conflicting changes cannot be merged.

+ +

A system of mappings, which is usually populated automatically, tracks the relationship of + imported Ghidra programs to modules recorded in a trace. By default, Ghidra will synchronize + the cursor in the dynamic listing with that in the static listing, and encourage the user to + import missing modules. In this way, existing static analysis is readily at hand during a + dynamic analysis session, and the user can further populate program databases during a + debugging session. However, target memories contain more spaces than program images, e.g., + stack and heap space, and some of those spaces are modified at runtime, e.g., .bss or .data. + This information, if observed, is dutifully recorded into the trace for immediate or offline + analysis.

+ +

A variety of plugins allow the user to interact with the target directly, view and + manipulate machine state, set breakpoints, view recordings, etc. See the table of contents for + a comprehensive list of current plugins. Plugins generally fall into one of these + categories:

+ +
    +
  1. Target manipulation - those used for managing connections and interacting with targets, + regardless of tracing
  2. + +
  3. Trace manipulation - those used for viewing and manipulating the trace database, + including machine state inspection. Most of these behave differently when the view is "at the + present," i.e., corresponds to a live target machine state. They may direct modifications to + the target, and/or request additional information from the target.
  4. + +
  5. Global manipulation - those which aggregate information from several targets or traces, + presenting a comprehensive picture. Modifications in these views may be directed to any + target or affect any trace, depending on the context of the user action.
  6. + +
  7. Services - those which do not present their own windows, but manage information and/or + add actions and options to other windows.
  8. +
+ +

This package is already enabled in the default "Debugger" tool. You may need to import the + tool using the Tools → Import Default Tools menu from Ghidra's + project window. You may also add the package to an existing tool using the File → Configure menu from your tool window and selecting "Debugger."

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/GettingStarted.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/GettingStarted.html new file mode 100644 index 0000000000..01910a582a --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/GettingStarted.html @@ -0,0 +1,165 @@ + + + + + + + Debugger: Getting Started + + + + + +

Debugger: Getting Started

+ +

During this testing phase, you will likely encounter many bugs, which is why these features + are not yet included in our binary release. As is, the debugger is poised to support debugging + native user-mode applications for Windows and Linux on 64-bit x86. This is accomplished by + "connecting" to the respective debugger for each platform: MS dbgeng.dll on Windows, and GDB/MI + on Linux. A variety of configurations are possible, and we are already developing more + connectors, but native desktop applications are the target for this initial roll-out. See the + Developer's Guide if you would like to contribute.

+ +

Pay Attention to Errors

+ +

There's still quite a bit of polish to get right, which is why this release is currently + source only. One of those areas deals with error handling and error reporting. Many actions are + taken automatically on behalf of the user, e.g., reading registers when a target is paused. In + most cases, errors on automatic actions are dropped to the console, as displaying them could + become a pest. That said, if things don't seem right, please check the Eclipse console for log + messages.

+ +

Launching a Target

+ +

Starting up the Ghidra Debugger for the simplest use case, user-mode debugging of a local + process, entails four steps:

+ +
    +
  1. Load the default Debugger tool
  2. + +
  3. Launch the tool and populate it with the executable you want to debug
  4. + +
  5. Start the agent
  6. + +
  7. Start the target process
  8. +
+ +

To load the default Debugger tool, from the main Ghidra application window select Tools → Import Default Tools... from the menus. Select + "defaultTools/Debugger.tool", and hit "OK". The Debugger tool will be added to the Tool + Chest.

+ +

To launch the tool, you have several options, identical to those you might use to launch the + CodeBrowser tool. You can double-click the Debugger icon to launch an empty Debugger tool. You + can drag a program that you have already imported from the Active Project window onto the tool + icon in the Tool Chest, or you can right-click on one of the project programs and pick Open With → Debugger. If you open an empty Debugger tool, you can add + programs to it later in the usual ways, e.g. via File → Import + File... or by dragging-and-dropping programs onto the running tool.

+ +

The default tool is pre-configured with a collection of plugins relevant for the Listing and + for Debugger-related operations. As always, there is some chance that the tool will launch with + some portion of the plugins not displayed or with a less-than-optimal layout. To verify which + plugins you have, you can select File → Configure.... "Debugger" + should already be selected. Choosing "Configure All Plugins" (the plug icon near the top + right), should show the full list of pre-selected plugins. Debugger-related plugins all begin + with "Debugger". At a bare minimum, you will need the "DebuggerTargetsPlugin" and the + "DebuggerObjectsPlugin", and the plugins on which they depend (DebuggerInterpreterPlugin, + DebuggerModelServiceProxyPlugin, DebuggerTraceMangerServicePlugin).

+ +

Steps 3 and 4 can be initiated together by hitting the bug icon in the main tool bar + entitled "Debug /path/to/my_program". For this to work, you must (a) have the program you wish + to run visible and selected in the static Listing window, and (b) have imported the program + from the place it lives on the local system. In other words, the file path associated with the + program should be the path to the executable for the current file system. You can verify this + using the Help → About my_program menu item in the main tool + bar. For example, on a Linux system, if you've imported "xclock", Help + → About xclock... should have an entry at the bottom of the page for "Executable + Location: /usr/bin/xclock".

+ +

When you launch the target by this method, a number of changes should be evident in the GUI. + A blank "Agent" window should appear, indicating the agent status and connection port. An + interpreter console will appear, potentially including various information about the launch. A + connection will be added to the Targets window. A new + tree-structure will be populated within the Objects window. The + remaining windows will be populated with current trace information.

+ +

Debugger Components

+ +

Each of these pieces require some explanation.

+ +

Debugging Agent

+ +

The "agent" is a process running on the local system, which acts as the mediator between the + Ghidra GUI and the native debugger. For systems such as Linux that support GDB, the agent wraps + the native GDB functionality via a Java container that implements the GDB machine interface + (GDB/MI). For Windows, the agent wraps the native dbgeng.dll functionality in a similar + fashion. The blank "Agent" window allows you to see the current status of the agent. If the + agent dies or is killed, the debugging session will be terminated. Of particular note, the + protocol used to communicate between the GUI and the agent is the Ghidra Asynchronous Debug + Protocol (GADP). It is not the native protocol associated with the debugger. Direct + communications with a native target are not currently supported, although that functionality + may be added in the future.

+ +

Interpreter

+ +

The interpreter window allows a user command-line access to the native debugger. For Linux, + this means the standard GDB command line interface; for Windows, the WinDbg/kd command set. + While basic tasks may not require using the command line interface, more complicated debugging + scenarios invariably require commands specific to the target which cannot or have not been + implemented generically in the GUI.

+ +

Targets / Connections

+ +

The "Debugger Targets" window adds an entry for every new GUI-to-agent connection. These may + be added directly from this window using the "Connect" button. This allows the user to select + non-default connection options and/or to initiate a connection without launching or attaching + to a target. For Linux, the default connection is equivalent to the menu choice, "GNU gdb local + agent via GADP/TCP". For Windows, the default is "MS dbgeng.dll (WinDbg) local agent via + GADP/TCP". Using this method of starting a connection requires the additional step of launching + or attaching to a specific target.

+ +

Objects

+ +

To launch or attach to a target without using the tool-bar "Debug" option, you will need to + use the "Objects" window. The "Objects" window provides a default tree-structured list of + everything the debugger knows about the target. On its tool bar are buttons for "Quick Launch', + "Launch", and "Attach". "Quick Launch", like the "Debug" button on the main tool bar, runs the + executable associated with the active program in the Listing view. "Launch" allows you to + specify a target and its parameters, typically, in the form of a command line. This target can + be any executable on the system and need not be associated with an imported program. The + "Attach" button populates a list with potential targets from the running process list for the + system, which the user may select and attach to.

+ +

Traces and Threads

+ +

The Targets, Objects, and Interpreter windows are the only windows populated directly using + information gleaned directly from the target. All other windows derive information from the + current trace. Once triggered, Ghidra captures information from the current debugging session + and uses this information to fill the other windows. The most important of these is the + "Threads" provider. The Threads window has two parts: one on the left listing the set of traced + target threads and one on the right indicating the current position in the trace by thread. If + no process is being traced or no thread is selected, all of the remaining windows will be + empty. If the current position (indicated by the draggable caret at the top of the right + display) lies outside the bounds of the current trace, all of the remaining windows will be + empty. Selecting a thread and a position causes the trace-based windows to display information + for that thread and time tick.

+ +

Commands / Miscellany

+ +

The control buttons in the Objects window or commands issued in the Interpreter cause the + target to advance in the usual ways. (The control buttons in the Thread window, by contrast, + cause the trace to move forward or backward without affecting the target.) Breakpoints can be + set from either the "Breakpoints" window or the listing. Breakpoints are typically aggregated + by target. The "Registers" and "Stack" display, on the other hand, reflect the selected thread + from the "Threads" window. Typically, the thread selected for the trace in the Threads display + is kept in sync with the active thread for the debugger selected in the Objects view, but this + need not be the case. Similarly, the "Dynamic Listing", marked RIP or RSP in most case, shows + the bytes from the target's actual memory and may or may not match the bytes from the imported + executable in the primary "Listing". When it can, the Ghidra debugger attempts to keep the + Static and Dynamic Listings synchronized.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/Troubleshooting.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/Troubleshooting.html new file mode 100644 index 0000000000..c851714c51 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/Troubleshooting.html @@ -0,0 +1,111 @@ + + + + + + + Debugger: Troubleshooting + + + + + +

Debugger: Troubleshooting

+ +

Error Console

+ +

The first place to look when you're having trouble is the error console. In Eclipse, this is + just the "Console" window. In Ghidra, it can be accessed from the main application window. + Sometimes it reports known issues; sometimes it reports unexpected behavior; etc., which may be + clues to exactly what has gone wrong.

+ +

Settings and Toggles

+ +

This list is not exhaustive, but here are some options to examine if you're having trouble. + In the FrontEnd tool, under Edit → Tool Options, select + "Debugger.Workflow."

+ +
    +
  • "Map modules..." and "Map sections..." control how the debugger attempts to map static + and dynamic memory.
  • + +
  • "Disassemble..." triggers automatic disassembly in the dynamic view.
  • + +
  • "Show debugger interpreter..." automatically provides access to the command line.
  • +
+ +

In the Dynamic Listing:

+ +
    +
  • "Auto-Import Current Module" will cause the user to be prompted for information to sync + static and dynamic listings.
  • + +
  • "Sync to Static Listing" controls the tracking of the Static listing.
  • +
+ +

In the Objects provider:

+ +
    +
  • "Record Automatically" causes a trace to be started when a Process object comes into + view. If the process object is not exposed automatically, you may have to expand the tree to + trigger the trace. If auto-record is toggled off, you'll need to hit "Record (R)" with the + process selected to start a trace.
  • + +
  • "Subscribe to..." causes a particular obejct to be tracked by the trace. Processes, + threads, registers, memory, stack, and so forth are tracked automatically, but you may wish + to add other objects ad hoc.
  • +
+ +

In the Threads provider:

+ +
    +
  • The "Track...to latest snap" toggle determines whether the caret (and all resulting + information) should track the current thread position as it updates.
  • + +
  • The "Set to automatically save..." toggle causes traces to be saved into the Ghidra + program database at the end of a session. (Open sessions will be re-opened with the tool in + the Threads window, reflected in the thin tabs at its top.)
  • + +
  • The "Synchronize..." toggle attempts to maintain synchronization between the Object + provider (i.e. the active target information) and the trace.
  • +
+ +

My Breakpoints Aren't Working

+ +

If your target is not breaking as expected, chances are the breakpoint has not actually been + sent to the debugger. Breakpoints in the static listing are not necessarily effective for a + target. Many things depend on tracing and module mapping to work correctly. Occasionally, + either because of configuration options, or some other hiccup, these dependencies are not met. + Futhermore, Ghidra can only send breakpoints to a target while it is suspended. If you are + trying, for example, to break before "main", you must ensure Ghidra has a chance to place the + breakpoint before the target would reach it. This may require using the command interpreter or + changing your native debugger's options. Otherwise, verify that you are actually tracing the + target where you expected a breakpoint to appear. Then, verify that the module containing your + breakpoint has been mapped into the trace.

+ +

My Target is not Being Traced

+ +

If the dynamic listing is empty, chances are, you are not recording your target. If that is + the case, right-click your target in the Objects window and select + "Record". Please note that this action is enabled on any object, but is ignored if nothing + knows how to record it. Currently, only user-mode processes running on a 64-bit x86 system are + known to work well. Be sure you have selected the process, not a thread.

+ +

My Modules are not Mapped in

+ +

If changing your cursor location in the static listing does not automatically synchronize + the dynamic listing to the same place, and vice versa, chances are the module is not mapped. + (It is also possible you disabled this sync feature, but that is not usually the case.) You can + also check the Static Mappings + window to see if the memory ranges are mapped properly. To attempt to map the module, use the + Modules window. + Right-click the module where you expected to sync and select "Map Modules." If you do not see + the program you expected, it may be because it is not named in a way that Ghidra would + recognize it as the given module. Currently, the program's name must contain (case insensitive) + the module's filename (ignoring the full path). A user action to remedy this is a known feature + gap.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBots/DebuggerBots.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBots/DebuggerBots.html new file mode 100644 index 0000000000..4f2ccc7c65 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBots/DebuggerBots.html @@ -0,0 +1,63 @@ + + + + + + + Debugger Bots + + + + + +

Debugger Bots: Workflow Automation

+ +

Bots a are pluggable part of the Debugger's Workflow plugin and provide useful automation, + taking actions on the user's behalf that would otherwise be tedious, or that should occur + "under the hood." Each can be toggled from the Edit → Tool + Options menu in Ghidra's Project window.

+ +

Show Interpreter

+ +

This bot automatically displays the interpreter console for new debugger connections. If a + connection does not present an interpreter, then this bot will take no action. This action can + be performed manually using the Console action in + the Commands and Objects window.

+ +

Disassemble at Program Counter

+ +

This bot automatically disassembles trace memory, starting at the program counter. It is + activated whenever an open trace's memory or program counters change. If the target presents a + stack, then the bot will prefer to use the program counter recorded in the innermost frame 0. + Otherwise, it will use the value of the program counter register. To accommodate multi-threaded + traces, the bot considers all threads when responding to memory changes. It considers only the + affected thread for program counter changes. This action can be performed manually using the Disassemble command in + the Dynamic Listing window.

+ +

Map Modules

+ +

This bot automatically maps trace modules to programs opened in the same tool. It is + activated whenever a trace or program is opened, or when an opened trace's modules change. For + a given trace, this bot considers only those modules not already mapped, seeking suitable + programs open in the same tool. If the trace is open in multiple tools (not common) the bot + will consider programs from all such tools where the workflow plugin is enabled. This action + can be performed manually using the Map Modules + action in the Modules and Sections window.

+ +

Map Sections

+ +

This bot automatically maps trace sections to memory blocks of programs opened in the same + tool. Its operation is analogous to that of the Map Modules Bot, except that it creates the + mapped ranges by section. It is not commonly used, as it's less efficient than the Map Modules + Bot, but it is required whenever a target presents sections which can be relocated + independently of other sections in the same module. This is more common in abstract machines, + such as the Java Virtual Machine, where each method is a "section." This action can be + performed manually using the Map Sections + action in the Modules and Sections window.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html new file mode 100644 index 0000000000..b618f1ac4a --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html @@ -0,0 +1,113 @@ + + + + + + + Debugger: Breakpoints in the Listings + + + + + +

Debugger: Breakpoints in the Listings

+ + + + + + + +
+ +

For a description of how breakpoints are managed logically in Ghidra, please read about the + Breakpoints + window. Each individual breakpoint location is placed in its respective listing. By default, + disabled breakpoints are not readily visible in the dynamic listing. Breakpoints are best + controlled using the static program listing, where they are stored as bookmarks. When this + plugin is active, additional actions are available for managing those bookmarks and their + mapped breakpoints on target, when applicable. The actions in the listing manipulate the + logical breakpoint as a whole, not just the individual location. Furthermore, depending on the + target, locations' states may be bound to others' via a common breakpoint specification.

+ +

Actions

+ +

The following actions are added to all listings by the breakpoint marker plugin. They allow + the placement and toggling of breakpoints by address, kind, and length. To set breakpoints on + arbitrary expressions, use the Set + Breakpoint action of the Commands and Objects window.

+ +

Toggle + Breakpoint (K)

+ +

This action is always available, and it is suitable for almost all cases. If there is a + breakpoint at the cursor, this simply toggles its state. If there is no breakpoint at the + cursor, this will prompt to set one, giving a reasonable set of default parameters based on the + context at the cursor. At an instruction, it will prefer to set a Software Execution + breakpoint. At defined data, it will prefer to set a Read/Write breakpoint of the size of data. + At undefined data, or if the target does not support the suggested default, an error dialog is + presented. Please use one of the Set Breakpoint actions to force specific commands. Please + beware: the default parameters are not always acceptable to the connected debugger.

+ +

Set + Breakpoint

+ +

This action is available on the dynamic listing when the target supports at least one + breakpoint kind. This menu is always available on the static listing. It displays set + breakpoint actions for each reasonable combination of kinds supported by the target. In the + static listing, all reasonable combinations are available, regardless of target support; + however, only those kinds supported by the target will be included in the resulting command. + NOTE: Breakpoints in the static listing can only be mapped to targets which are recorded + into a trace. Selecting one of the actions will display a prompt allowing adjustments to the + parameters before issuing the command.

+ + + + + + + +
+ +
    +
  • Address - the starting address of the breakpoint. It defaults to the starting address of + the instruction or data at the cursor.
  • + +
  • Length - the length in bytes of the breakpoint. Some debuggers for some kinds of + breakpoints, may not allow a value other than 1. For execution breakpoints, this defaults to + 1; for access breakpoints, this defaults to the size of the data at the cursor.
  • + +
  • Kinds - the kind(s) of breakpoint. Only the reasonable combinations are presented, but + the user may type any desired combination. A connected debugger may not support the desired + combination.
  • +
+ +

Enable + Breakpoint

+ +

This action is available when there is at least one disabled breakpoint at the cursor. It + enables those logical breakpoints.

+ +

Disable + Breakpoint

+ +

This action is available when there is at least one enabled breakpoint at the cursor. It + disables those logical breakpoints.

+ +

Clear + (Delete) Breakpoint

+ +

This action is available when there is at least one breakpoint (in any state) at the cursor. + It deletes those logical breakpoints.

+ +

Tool Options: Colors

+ +

The background coloring of enabled and disabled breakpoints can be configured in the tool's + options. By default, enabled breakpoints are colored a de-saturated red, while disabled + breakpoints have no background color at all.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerBreakpointMarkerPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerBreakpointMarkerPlugin.png new file mode 100644 index 0000000000000000000000000000000000000000..99a16b7d9ff96cb1b0bd32d1dcd57da452f157c0 GIT binary patch literal 64007 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 XQjTsoDDvZ$5b%PIk#@f3uXq0e)<1lD literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerPlaceBreakpointDialog.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerPlaceBreakpointDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..a7a15a84b9dd39f43b41507349aaa5382d28a214 GIT binary patch literal 7308 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 Iol`;+01eGvkN^Mx literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html new file mode 100644 index 0000000000..746711b1b8 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html @@ -0,0 +1,165 @@ + + + + + + + Debugger: Breakpoints + + + + + +

Debugger: Breakpoints

+ + + + + + + +
+ +

Breakpoints refer to any mechanism which may trap execution based on an address. The + breakpoints manager presents all active breakpoints among all open programs and live traces. + Note that dead traces are not considered, and only the breakpoints at the present are + considered, even if the user has stepped a trace backward. Breakpoints which map to the same + address in the same module, i.e., program image, and otherwise share the same attributes, are + grouped into a logical breakpoint. NOTE: The breakpoints window cannot display or + manipulate breakpoints from a target until that target is recorded into a trace. See the Static + Mappings window for the finer details of mapping traces to imported modules. In this + manner, breakpoints stored in Ghidra programs comprise the current breakpoint set, organized by + address — except for breakpoints outside a known imported module. The top table of the + provider displays logical breakpoints; the bottom table displays breakpoint locations.

+ +

Depending on what is supported by the connected debugger, breakpoints can trap a target when + an address or range is executed, read, or written; using software or hardware mechanisms. In + case of "read" or "write," debuggers may differ in terminology, e.g., GDB might call them + "watchpoints," but Ghidra still calls these "breakpoints." Some debuggers allow the user to + specify a breakpoint location other than by address, but ultimately each specification is + realized by 0 or more addressable locations. To accommodate this, the Objects window will + typically display a list of specifications, each listing its locations as children. However, + the grouping of breakpoint locations into logical breakpoints by this manager is done + without respect to the specifications. Often the specification is at a higher stratum + than Ghidra natively understands, e.g., the source filename and line number, and so such + specifications are not relevant. Note, however, that the model may not permit locations to be + manipulated independently of their specification, which may limit how the manager can operate + on each breakpoint location.

+ +

Because of the logical grouping of breakpoints, it is possible for a breakpoint to be in a + mixed or inconsistent state. This happens quite commonly, e.g., when a breakpoint is placed in + a Ghidra program before that program is mapped in any traced target. Once mapped in, the + location of that breakpoint in the trace is computed and noted as missing. A missing location + in a logical breakpoint is treated the same as a disabled breakpoint. Thus, the logical + breakpoint enters an inconsistent state, because it is "enabled" in the program, but "disabled" + in the trace. Toggling such a breakpoint enables all missing locations, bringing it into a + consistent enabled state. Toggling it again will disable all locations.

+ +

Tables and Columns

+ +

The top table, which lists logical breakpoints, has the following columns:

+ +
    +
  • Enabled - displays an icon indicating the state of the breakpoint: Enabled, + Disabled, or Inconsistent. Clicking the + icon toggles the breakpoint.
  • + +
  • Image - gives the name of the Ghidra program, if the breakpoint is mapped to one.
  • + +
  • Address - gives the address of the breakpoint. For a breakpoint mapped to a Ghidra + program, this gives the address in that program. For a breakpoint not mapped, this gives the + address in its trace.
  • + +
  • Length - usually 1. For access breakpoints, this notes the length in bytes of the address + range.
  • + +
  • Kinds - indicates the kind(s) of breakpoint: Software (execution), Execution (hardware), + Read (hardware), and/or Write (hardware).
  • + +
  • Locations - counts the number of trace locations mapped to this logical breakpoint, + applying the trace filter if active. Note that a logical breakpoint with 0 locations is + ineffective.
  • +
+ +

The bottom table, which lists trace breakpoint locations, has the following columns:

+ +
    +
  • Name - displays the name given to the location by the connected debugger. This field is + user modifiable.
  • + +
  • Address - gives the address of this location in its trace.
  • + +
  • Trace - gives the name of the location's trace — the target path by default.
  • + +
  • Threads - if the breakpoint applies to a limited set of threads, gives the list of + threads.
  • + +
  • Comment - gives a user comment — the specification expression by default. This + field is user modifiable.
  • +
+ +

Breakpoint Actions

+ +

The primary purpose of this provider is to manipulate existing breakpoints. It provides the + following actions to that end. Breakpoints can also be managed via the Breakpoint + Marker Actions in the disassembly listings.

+ +

Enable + Selected Breakpoints

+ +

This action is available when one or more breakpoints or locations are selected. It enables + each selected breakpoint. For any breakpoint that is already enabled, no action is taken.

+ +

+ Enable All Breakpoints

+ +

This action is always available. It enables every breakpoint. For any breakpoint that is + already enabled, no action is taken.

+ +

Disable + Selected Breakpoints

+ +

This action is available when one or more breakpoints or locations are selected. It disables + each selected breakpoint. For any breakpoint that is already disabled, no action is taken.

+ +

+ Disable All Breakpoints

+ +

This action is always available. It disables every breakpoint. For any breakpoint that is + already disabled, no action is taken.

+ +

Clear + Selected Breakpoints

+ +

This action is available when one or more breakpoints or locations are selected. It clears + (deletes) each selected breakpoint.

+ +

+ Clear All Breakpoints

+ +

This action is always available. Use with caution! It deletes every breakpoint.

+ +

Filter Actions

+ +

For organizing breakpoints the manager provides the following actions:

+ +

Filter to + Current Trace

+ +

This toggle is always available. It filters the bottom table to those locations in the + current trace only. Additionally, the "Locations" column of the top table will only count those + in the current trace.

+ +

Filter to + Breakpoint Selection

+ +

This action is always available. It filters the bottom table to those locations belonging to + a selected breakpoint in the top table.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png new file mode 100644 index 0000000000000000000000000000000000000000..89d75ff10cf7b7a78f2127efde5948f690bbf39c GIT binary patch literal 29159 zcmeFZXH=70w>G>tiYP@CL^=pk1O${SU8RF`2py#NZs-9~5s}_ID4h^MT0%#92dM!< zkzPWFP(pie)P2tLyyu*Ee1E?&PR3wxlaSnN&9&y7*Sh946RfH%ON38_|JPrC5y{I* zssHsC4hZvG-F<&Cb(>AmBH_lv=- zgc98`Dl$EBJqra*-5PW%611C1R-);&nWE_3DK~_ugl2l*XA|;ELqt|Ym z$=;A+%Ti84R{chl;5O%pj9kOn;GWODM^AY4nxfQq;$H@e;zy|ClT$KFJF@8=u(Ihr z)+?Qtkav*`9#qdXuZX70Q3wkLd56Ywkrolg(iX;Y3WB=hB0vpi&)AF^^a4LGWSi!= zs22uy28hPva|Cv2;Y4 zqes>|OB>ciRy_&E(sswAG$+tOPUC?n8HDe&XR+@zliotETaNs&m%^|mU0)Wz-BXoh zL?Sn*-pr6~Lxp|ibDPo0ETxJu2XeU-I6)kIQ*=_+4rvlqA^66CiB?z|J#JI_X5CjY zp>60Sg;{}om1;A^DM1doof+%&Rlj1phC9wt>|(gys)DXQUS36V+hPcPx}>lz-m~O$ z%%GRiz_A$Qce#f0&lz(1uCV4rQKf!^P%nq@Y**s_jgol!ASr6m0}08GMNwZ{oG43W zK8EQO64F7dZ zGevX_vMA3m#pFt*h>n^~T3USW7+FK~jz1puo5*~1l=yT%URQr9LEp0fFp<<@Y|4S> zG+t<5blFLJnQNSDpO662b0#eki%Um9K3is<_xST?__$e@dW1!7Z2nTMd4Wlrqb{6h za!;$)BG3dwr(4Z3$*xnYty}L{0@I~;;ItplgQ2Qa#+-OI5{Oc=9Hz>Q1t{Tkk$tF4 zhl$&GaYZ7xyWwer@a=b!-Qm+g_T)Dh@o7mpSU|5}i8@b9O--d2kyY}%4i-`D0<#4F z_QLM?p&_0qG7Or&FVC%S_~fO$%yOUcqL2jPQEEnwPrr@fh;e9^LB?1HC+g6%U-x~s z*Zy+)e#dov$di{|+yVH{O1%ZltZdGI66|R-w<@6UGGY>VE6q&XT0aMq{k|0roZmLC zmqXu(MeHDw2cbzhAu4zrvaw0Bh88I_w_(%BPMmTZ5KBkPTc8e_Zn9A7miv_xQt@=g zJnxG#>xk$KBv^SxAuF{0G)=rD#)j9j=ZDF|Y;cOe5KNbMpc;`Ddg>LuV(?@-&w^uY zGT!Xwa{YCVFRl3Uqn|zycU>O{FQrP_dEv#NNdBJ|X>0A~HaK8qRWYfJf2AC6FuFE8 zYZKMbAmMXw@431b)Y@g#HD`b{^m)u(CPlHvZ3aBztpr{xm7b=SQ_&@q`Kcq58fv79 z{bzOW!^ClXDA5ikyP&ej?|74FWc=~A{LoK^mfmcrMIk8Nt(|G0+O$kXEgh1}-SP9#=v&`& zZsH1Yz*R@;T; zG@kXmr$hBV9u}>g8KUU2K7lleD*hZ+&5vD|rx^}d)j%Ielq#i#HCYWUTEzLsYcQvb zYmUlxNNGb#0!$Tg9vTmIG0`(dn76)d(5UJQy3fX>vQOR@FMjUbg_y3*Z^ie28tyt` zCj4rL8EjDb#q?*b2gs(x;7rYv6_TkfIo%=I__A_7GJvPLx^~0Ms z>nVa`H1-xjR-ar<`PogPBS|>2+SYRtWIB&#OA=^B2Bhsx$zFy0*|mu}((0yxA$}2b zVq)aw#YUVyVPiOqE6d9)_r4K%`gb>{Fzjx!P@@uJ51ZAL{<*; z)x=(z%V62Jq7r=`)8`#;4!$;TiZdz8WkMTwYbtDz>dc_y5SQ32x?vOV-aPJ}ThWCD z!&XqHZoP0O+p$z!p4UGkcpR{gz(*;E8mLMNQ!w&QYavwt|4**aT%%lLUteY zI-P~+M)R)dhw3#4&pKMyr;q%i+*U3kjq1mst@wlWr&I%FC7n)<=WH>o z+iG3WQ95ytgk)Hhr#Q*E3}QmaOL|)6YsGF2>vY`bkU|!7EOkcBif*svZcKvvV^n+9 zgX8FAOh@ZZOog0(W}ab?v#uNC7HeY;|6$;^;8uK2Ht%TD=V{{g)NjtlNZDKJ_Q)KD zgPL`WD-P_Z%7)98+`GQbJv6Ims5%c?1YCNfS=!7AA|`*|(PNHk>Z>FkrI2lfgSTM0 zV!XhqW*isjC9{?zntEgS-9_}>?;8HAU&0$St2Tq~vomQ>!(nQGBbd@GU3miC{#s9& zUbSUbVg6EK4j4VQE?^RJpVNHntPQ@{!yb8Ls+5hR1by(Om0bl3hi27Wyr4+mVPH!x zEvWEXZOp?(i5=7}_6&*t1VWA0Kswrdp?%mWcyzu^Hd`wAsbz>3!qIMovjv~5U2%>v z%VN3r8jtOWJ8$VEV*A~SshDh~C>f`)UVFssl6=GABuSx6*!}JtMP!bAjI?D4ndx+m zM!LC0rsl3r<3%hXZB5%Nm`vz6JGDS+G}1VOX%wmyuax2!clROU;QN;Jb3?&Bz(>|&%>j3kR&fm+ zdxmIT-wj-eQSJ?KY5+N}u$>b;S1mWmc7@WJ8hj%YiG-Z%3G9Co(hgcYVp zb0)YR%x@JntA*GSt0O6NYD6!eY;cuwcHHUo3D8l3rTRU%$Ek%4u&_RF6fF*p8#Ki< z6SOR%oV0stFnM((9C4buBpghvmuLGWw-7gXk#ev|!mp!idrOU>h_CBK!DP|Q72<0U z*qye|VFILuz}_rGxyqOZnlQ=Bm}=GZ>S!%EM*Jot83}Zo&yZo0?bhaSCxt z@(KL{_0+JEN)@tbsOlKPYP*5G+EpPMhOTWYFJ2-bd#Vp-!7Xh7_DAVxhwbLaqGC`9 z>M_J`H7^=UC+1VopyPNB1NMctI+?XASypTJ>d4l0Z~znN?fqP||IC2`?4TbVM10Az z)N{E}zK`8q(9Tv$u}GJ)3SF--%9MH&ZKeb>^0IhmRnxQALQHKR99P+fkJvRGGF^(- zVPF^`<;dC>)+?NZ>qD_FS2*bkv}hT~!FKr4W~!;Yy}69WKwlG`H%BfSf2Ld3(xUAu zE*^a4>HeV}$Wx)WHhlUnd`;puU1o9hss+Tbu!Yzsz{xHUAMog_;D$2Bc3t=mdBg+2?74Ve_^!|o}x(X*_Xe-pLW6AZ$euJ~mT@^e5 zr_xXVfY$j}rer9`j^TH6AHvsl1g9<}T?4aiUAtqU_O{pq_!{H|{R1ZL3q5U9eA>}I zbeFBehCI`qnzGqr5CkqdH3^hGJmhMf5uf;Ee5eV zCn~7IQMsJEpWM)3eO?MTZNpu4)8caSi@Uk1LU5zD!LjQTSL7wxc(EDv+Gt*cFKCR8jlO%3_Oj!R zC?+UF^Sg1V{;4LzSKFP@1fHKAP4E+<<6V`50-UqZmqFpQTCRZ^S`?yhs&uU?P07-Q zE4T@+P8FYBOatlhhz{{yCjC0U{29V$h$ApN4gmm@YdMHiAmUEnUDA#Nel>8ig@=*+ z=3OsQdSaBQv(#9BIrQ-#izVu)G3nL$rWHL_Z(H^;`A@L@3ij#ItKSEtKQ~~+C(Q^Z zy9+#U2jX?sCdhwX0-pr@R!e+R$_PC0Ns+D4fwxgtG{hofeyhTEtn}6O;5%iQ<4hf8 z;Wu?}%DZYz%_sItUpe03Nq6%ztB>)D6ClNJ3YXUSoqNNYsfT+$ePX=ZZxwQAm@D!~ zy_hYkEPYqE>hQ&n;L*`SZ8Nf&L?aogd%@1)DV1o!n)i%L$s}xO%8Woe`s-;rhBa^4 zKyzVh7E)4CH&@ELP+V$+zEjYkdj*=3l;z7Gie@t8s&l2fz1ywkCh=adf>!#|LbN>1 z{ux(qZitD?fY{M^_q&1!%|P}MRV1T*&kM!d?TlJ^%xXerG8-6nt_V--?ro#c;;dpo9|GI;2qt*&7 zKNX+;ka)7j3ARHB?oHbt;H~=v6SclnHoA0nvY#7p4NqaAY2SYMgoF(l`+Ur;JC-e) z-wt%LlwfQ>9LQMg@Lr@3usR4Z>Re0EFQeXg_7|)rjEX>;On{h)OR56*aAeK`fB;YVY$autayLz$Jh5r zJ6cgs)p}6wDbnc^>2n_S>ouIh zY`BFD+{7%3Y{XW*?4u1><+KExq7LUUmU<@Xb;9TeFL1QR9R9{ z*7j7a>6ftW*FYf6yAt;)tG5GM)6KUpvq32Ve~-4oMJ=E`tAkle8IZ%ISf9p%x@qsX z6;zFb0`**xcArPngjY`WRSZ_>AO~tmf==m0{S;dh<$1P!3eil#bvw-jM#EbugvyN9 z3N5&(#Z}mSW0hY5zNw)i#vDw_8s94#loooIm6D&#*g1Z>{x?JEZWY<3RqVoiK9 z_C<_dUy5J?4laIYG_#_4%SR&hYF7)d%kv`#w5(Ki%L%M!zFdHIIcMg?uqZGrZ0&{i~iH5wf;$97J z_}Mh;i*+*&<|@?7r=P4nIjZ8LDOFGB*wuRuE+s-&TlP_-M6N6%Pe|w$K*@OZzpgW>SbnTnxo(jLtkE8>xyBG&^0bG62mlrU^)yv3ET<3 zCwpFf5i*|}AgA2`3848BRG`j`s%iCTIDGL9hz;)BGqi;}?FT&H|9(x|jz`d2slprQ|XH#+mR*E@)pj z<;2P!T6v~cw!&o_ZZ%*i1+ApA56|n3)LOr7FH)Kq=fu?-IPTU_{x|tL`YQ8yHDbNx`oM2j+(upV08~4!+ zF}}=u-BqcyMq|YnfsrbFs27{M4r`H140Yk-`-17B=%A%Em<`>f+DH~)4!vSd;-QXrH8UYGjuJZvGIkH?K~Et7_XS;2fvV(0rcc1gkZ!7__9dOVig zpbxi5q1R~qi%Mjr7O$_A_lFEjil!rCN2~{DX-a158b#AfDun#D&xz$ut2_E!2p8(^ zS&MxX;S^rLBv*wcJYncJ3l?1|U3H_ppu%Jj>piN*kBexiPh_jDLxByh7Ml}Ph5ku^ zHHh;>lsbq@K0w|p6an{p=np8T2^SP|%N-vG=b7a37@(aYyzLmf>5&4B9L)mtbr1vA zg@}@;6<&AT|0ke)l?*byiqe0k_E2$qroOg@=o5>u`L6f*J6wWZ7rp>J^zNp`M45$A z4=W@7bs#3hr|l0pV!7lKSB=2wr7tPhpAcb{_)lzdgj5!ydPe!$s~Wx0-x!qQJF=HP zD8XF~k8-S*TAVr*hBR&7K|?rK&A@ZbxEZ%;o;_%@8;c3&J(5rGlC|H*`?E;?=wS5t z!wAt#z2pO7$an!EvgTxeC1n9y+5cADQZnea7UBk`=07G;YSuwxf``kyMF7kscc6s3 z$6;!9Dx8=;`3;9={!gul(|*rTa>UKQ&x>Ff9r}aXm#m&|-gFF8R7E&I#26V_pZTw< z6x4%C-Y*s&#}|qEw>E02nLwdCsdO_37Y2bgB_j@1wYORYQ?JLis6{lp15v9UCR&ww!;x)_B|onI1_jQp?GOJX~N*{yZ32&tGI!Tm?YlV@EGC3N4C-#CAn3*E#8&* zG|N`Kax4qazeulgr0AvjfhXjZ-AKkWmzRC-^O%0fUsSK8Yv83}FrPl9{`AefELP9$ zNRMW@W;Nce7B93iZ0q^svxfRcrSP6=S!}eh8OSS7*8@DmlZ(-Iq7@+2byK$Wo7Nh# z>SH@%Rc6%9@HJWo)0>U?Uray3V2>msb;?99V0?5>#J<#wf1EAF=i7@bbWEAuOwN?7 zZUh1orrkzV)e&t&+rcSpP_lDSTdAC(%q`-&_-Ulr5arAM%??>?gt5-)j^jwEA^VK? zeMdwWyEk9^{3xJt_X}xZxz#{Bd)?Fe4fkeJGP!RDu8f(l=X`T7NuJ^6DerHoe|w2} zTZe?DI9!N8lx}He!>oMo7ens6F$M3=x9J25<|eekYryF4bTOY1(m-qWwkmO=yK3K@ z#fL9FXs3-l=95W2%jpgr|3MGHFn;Cs5Qka6)1n%sPZv7d^+)NcLT>E>zu)&8;%v3@ zGvE6c7*uJ8{N-T3VUT{+HqZ2nC|9N6SWM_Zb0zT=+M24m`zxX{X3BlopZF}Py?Xm| ztewJtjyRB<4AgXyg*|IB_aCm6nuG7Y*F=I+t<1=$7z9!xZAVR=|#K zDFesGp2{oDUBfH#P0ekK{^|Q~Y7Qu~4}Ivn0?bylrbC4kvo(|UqWNriw2mCpBKm|8 z;9#dIco8*XlP-T6Pgy<;F%!jSGhA@4nyn+B#ADWgwea6P7o*iqm^XY2pz35Dt-`Pd97aPmR(C&NJ<2yyOHnpFJ87&(N>Itgj&YpV*IhdYuIu7_Gpc zFS`koFZUSB$y|zR9ny(sMduEzLzpdkwA8`41Y~g;tbLkGRgu0E`PDTKu=)wdERhaq z78X>lPVf+W6v}O%}Z-_mngTgp0mo2&tssqsYDD z0gJHPBY*VRtX(ArBHQ0RBiY#Pv+ZwsN~6=JU>X?DE>^%JrB&pZB7Wz%{YNGkuPd6l zushjq4EnHRz^p{m5HE|Nit6R#4{YUqA))%TG-4249`jx%-W5(v4aG?c zF;2B7t+2R7Vpitxtv=%1 zyA9-+zo$o3f~NIFF5nelvDC|+4@yc@$}Lt*z+$bHWDB&y9vYmcw6$-v7aqx;j~UAL zdi}_psDY5j#WI{Y>x+k{waP~y!y&%U1q^nZqR>4B4H(v1t%%hr{ z7Ra;PQz}OUIkFloQo^A^J}vNb=-RCis8(sj27LR%iw#6 zQy9^`v862B3bhzew8%hDIL3um&vP(3KY`lO-!upE<67V~kD{E2`^nP0R$e1C9S}o1 zj#x5xifB$V8v4KNa#Q*3+;uDcf`r1C%zNTn;53Yx&T@%QzD{W4BJMp&eWb?N2v!8y zYjUDxBv{dHoetvf8$*!mL?T)o*Y(T~PW~zheG48t7jui@aGJFzUKN#o?SXjuie<67BL>X!KNXK!m76EP4!_ax}h92(0nftQugA z;~PLN0+0uhH|L(;pjo^{!lddS=1n7&*Kb(fho>ykp_e-lVq&Z`CnfcCX$)r`bo4Sk zA$#`woMjqDbXg!{_@D+?%}z6OB=7PLu%U%r)TsQXUxF<=ssI2icmd~ZI9ND81=5)v z>We?|*2tIb6t(*)oFLBu&uuxD&@twXqx`s(kyC_tr)rHoF6FaDRyW`8Kh=!RBSV+6 zh9&f7e;}Mj#=GFyD*N`0HfZf#aSrb^Ef^EX6H81xipjQRPrh+PQ?5jRldn{-T%R`m zrL$eBD~DMK+1+X7)$;eso$XaK9o^b#n3TG>#uGy2m6zX(h~9XPWv~o?b>qVpQ1!TG z4v^9xqu`#l#M2rx`MM|p?hkIYtLIbf+C^LA9r%!v(B0=ovq(O06!=Ax?Tod1U9p`Lodueo2{74V88-Blrv`FC?xNe`J8u)BPD% z^KU)qOC{)+9z?z7Fk>>*g@ZUEAii)1syhpHU>)tiKSHEob=G>y;~}F}Iok9bG<;Q; zg#(gO_ilT36LS7_Tz1oQz89Do|NTy1AlEoGpo<)P9_!WggMwsx2cOcW<+&K0 zAMA6FdVw3uxw2qhw@LnS?@J<6J5_zUrYzEh)B3OR8S7@He3u+*rR zlk6ojSRh{f{ZvwMG&!u+9;|H2S&A=vGYur3v*Y9-9lR`lz;nCt2N zPu5f26DtW37e9pVKnVV7X>jBqx!ZQH-%3v1Sp?VF2hZYS{ms~ZBFZ;W*~;)EsX?d9 z(>6}(ipZZ+pFl~5rz(?UlE#3~IG{3()n)@)Ms|p=eN@fIXm$e4HF2Kx zQ;_6pvo~&GnCK{oX{J18A?w_J-c5S+ooHIY zr12s;mQABOj9Rd_jNfjolXpPWEc_vz&&JcPB;Gu2%I2DlAbLL?b`b2*>EWgu42j%V z#ybSZuqch323htcS8rYZMjVQ9-G?fPd0wEKV1&qgcfSc>N~sKS$YLA0aV&5+?E%hR zOdeZPLyi9ajbNEqiBDd*E_E+mp{^3uuP=EVCi#SX4q*z(4cK*#0?mSGD2HA}es40r zVqAuVzfrQFQ!$tKn6r0%?ONSBaJUFgv@Zmxi$bX_plTWioZ}0bs-=QSEYIUP^_jry zBRpp9gj$z9+>QCW!094+`1&3e%LnjpQQnyKq=iE5643C2B%b*Y?uN)>!`kEpfZK?@ zqCRvhZR2#qzX6y-7d=+Q?0<0v=SVUX&#CsWrp7mLU0We*OgjE?vUif{F-g}jy#3c_I(nH?t z9E5^PzZ16p@ql0S_4mKLmg2N~*Fu3T)!b0!je#23%K#itU9rcfD+Ywxsm>%E@5)%R z;WTPUNdx`6)3qMjKyLCrP`v^mgIThDUOy&J`UGe5vo~T?#Gm%u#qPNnFv~}`(jd_b z>aUFl(#23T9nMYfa5*(RWdT!FVSmO{AC82M*_sdSjE zc#5VZyzwwKKwlW(NqIX4qhm$ATpPikuo!d?0KUF_EFypCzprbCJy=^0$)%I4au%(OSBg~VJkujukIW4dnNZ&h}^-saj`l~Zhz0TL{K?3L6gRBLHiMIsJqO=u#zI+ zpbmf)`bWROirvO!rD7%Dm(eyVo%^p(j!8Z{`-nYs-kqv)$x`^snfj^t$3}+`?)l@F zGjIp}@?I)s6z7lcSHPt`^a{9?TPg{8?PdQ9aACCB(B}~Qg#XlB=Dtnjg-D4~QTP7z*@2 z#i{K2hFKrQy`Be1-E_LX!$Qg=_vL~r;QcDFg7HEZ1bJy@oWm<^-%2c9fpGlE<{9ML z?d&V%hfy@Z4WmVJ(PR0pLh5?1J+^Oc=_PCKyekojdgQ{+SsPV&O~ZkGgM+ zEp<5DnzoDAZE>i@B?ahgH4ob#-2I=%?Ms_|+I?i-IaF}XC63p5m3v=~1<|l;=0C`i zjS%SY2e{CZ2JiO0!8Ph4r`01ti|%{+5Cg)zbAOaRZ0CSp7>W`ukvRLN%>jA>2y5)z z3-NxTn4uQE_WM8@aS^!&asP2^;%0qBT#4{Gs;+6)_vRZT**weh;HP3g{-%Pc-NmAH zj8!5u@{|j*Ie#rn-r*0i9H888nZ)nwP_FuN`{taimpBcW#Au@=F9Qn$Bw}{1agdSlS_S}wy#8Q;#Q#M5~ zZ~GwB{P%n-M8Eb|fkU%#7JvX*E<}WV-U!Thx$3TGCn03}j&e^4sOS%xxlX50wV;CA zF7vbCInC6nU7(Z3vu}fc58s^Hmm(ENH)^_dox1sE9<3#3OOeEz+ft3!zJDu0eZKOk z;qNDew+J2c{&L*@(rcVX_Z!Fb-{KmpdOZ&()O+E4A=yKqmyz0x^(JLij$O&{H%g0e zLET!JJ@%e?gMzPW^Z6b-^NZS#KVj3z4He30s81`FzQAeJ4a7t2P@Bso8u_1tOIUaB zw71+mn)b-RhGZChlX*~r29|WnbD%vY_&9`hzr?WCQh9%)d;mHtIeg6msQIK)x$B9i zS4c&_`+?BNM{x+?Ah-wBtB;?Z4A0q%5GTr%gyW{*6enn0#1Zv4(c)+;unw$P}*QsHgRzS0HMtIbDaLAUT;Q z?)>t4t~>`Toc^1VzX(uq%dA?QT58PMA}3<@+Pl}if}!p@;kZ05dT+x^t}x?^%j^=( z6wL*>u_j?H^?aZ57X_o7`qU~omR-Xrnnfu${)%B)`d+#Zb=*7fQvc@s^dw_Nk!Rhu z@hD2y>Ktr;&Oe{>98p9Ksl}V`GEnJh&aYfPUL){V_Mxy-M}$W3nngE1CF9_1KW7S&SY))E1;_?n}j0wwI3@$L{f-@LQh{(!ND z_J1Ty9Q&Keq9^y6!KjD`jUf zWr*|>A|NN=;jz7;o|8VFJS1^ND9$XGi3-g5R7EA9kaW9VJHAnuBRihM*UWmb<~@J~ zwJbLH?M9;(tcsH{R$SAncxnLu!K5_#X73jRBG7)tfLs%~busW$@x;7s(!uh)U>~^@ zGeDJp;yl#+r27E;fDu1PlzTZ&et0=~fi$;Qfcvc@Sk4xEo5oTRL}UgBeSb zkW&E~7iU=P_@^Y?^_lNpv}okBQtc#E+5FWh1~ODW@SK0D-ou|w`+}^*>)P~|GJHc6 zRxDrDU{WW!;^+#LkQ6((rbnN3t2Kg=clnFC=}W@mmCOuEh;#qUGdEY+Ofmn>*Qxuh z0h$2EhH?0NcEr|`gwNN9XI&q@mY)n0v`2B*5~oHVNxRRsC@BFd|yO&>O4>bsb%Y&dmDmxJ+ZOxHj8yBrZ-c zX@5{)Nc^|aro`Rh5pqqtq!NoZR!5Y3){0*I{$+Y3sg++shpTvTd(r-lvgzr%RK#0b zwN~dgZKy&Tv3Q(9NkeXXhZ0sAAhhh2?F_~BD5&{un5*~eHgxFiDRCPNuXZJH%9o?O zpHTE5L~KIrV}h1{k3b1EO_iPCra;FLjVNbQxhPOd0+>Q=fZGtb@rT>EpYcC&8!9&t zzu-dV+w~dmQ!(&GOCag_SEIti+}V?O*m%C)V$pfl0r(~5Yhi%OL2EmW7wF0U(90Ug zQ=y)%a?t<&56^-04Y2p{cRp$x?^xqCk9DYxfHqQ;tXIz*9{ar&Dk>!=lKWnCI<$s? z#0d@N)uOJ~TzT2tnnWImJ9-Fu8rF$N^JQUf^@QnF z%NM82A}ZMVos2wg*_1RP`4oNJFRI?i;qh8w2r0-ynI=SM8uT0xud%WcD#XFMS>Uz%N4Xd;fZhCC zCFv{XZ{z!atEB2?;2&rq;bz(eTS6sUVh4p@96K2OY4)k*Vch}$E6$m=5Fjq4(?lfB z&lJI2($j*o7!V@nKBu9T{>S06c@Bg_?VC}{(QQK7u3wbJM^(h=+or6Mx^{>0-59Ha zU51D4UP>z!XWE6DyiVl8qj-8U0Zs{kZ0?8alBfQUeIj%$YrtTh$NM}S7w4=JvIeEO z?=rFqS#(ErgCD~3xSbil4^zsd4Yz-bpBtm6vys01`&R}BSgyc*N)n2x?!QGpz31@< zS9vb%dW%wq^`on-sW^2U_t#)65(Y4ro|`H-^JhWv2>>0DJS^12DOumUCQ5Bw<$y9} zR!C8?y?F9E{~ot)yFequh{K0$@{*9o_GjS%0N1dS+;4N@nS57*c&>0pJBbuhQybVo zd*%=V4F500UkM-|4Q3sUXci4lttJ+&t7IDWVC*-=0N~EwRD+4>mGa~s3!v~AbGa&R zU?b8wp8i&;ME&m-{{Py5@qcXqD7OB0AN>E;1}93t%C(({x4&W@BRRoiM;&g_I#TY!? zCe~qfka3K6sJ%V0Ogs|r;FEp%_Qg=0mm4Lo>;Vy-xY1{A9Ts}}E@y7$WC%)IZdJ1s<9pjTW0-)I&ChPi=TS(;S2xBbkWkczRz!_29P53+*b=8LY1k*=#3lQLd)V zHwgg=2ZPCwF8YjUR{n|t&GL-T!3z0RqV z5QnY=ZtvR*9C$o9Ev4x?Yb_sH`t%eU&TuU*GA+t1&+mIJjk500M zz}7iiq6c<KmeH8g6`nDIE2p77jo(v8S4-Klat&yBUM?S_jA+6mf& zE4`;J*Y+hP;xy;{X)?J%Rj0a?0duHm9U)9qcRXk9W1YGw&=#M6)sugGaE`n5U8O#; zeCc8BQdWj?cgie%+$QT~l%AwYcb-H>yaygl0@>+Bv{HJu?fZ_&hk_Pitbj&&fu3zl zv?>n=?78mL-YW!kic^EzFA0yj&`Olw*^;4tg42RvE>(zQkLyjX|86(A%!!h_z`@lz z$>ppyZ_Zv*uajcyIGOH#_lGzNO_Y5QOcrZjuP}pllVS%;F!Ea(C3K%h$nQEu$j5ad z7b}g6Ikc=iyPZyeVp7iQtVbx%dhcn2iVeQbuD~6qmI==HckWZS$~t%*xSH z>3&$s0rYM6)Y+oP6#p6`$egNQg&S9KVG_h8(luHfwG`yj7ju~Bs^7w&M0hRq;m|0)q~$$YdeKK0b%`p5UmFhZ41T{8as8653 zm#Wq#3K4Hka_uU(X7ew}t(ib_Wxc{7Y{QH@#*?km|2p){f04; zRwZ2l`u&FWUw_JfFGOC(&t9JCJ6@b4Vo$tFf!(>Ykg=+PdrTR4n^pw6h%Zt2#BdEK;2;JxBT9V4uLlmEJmPwCc~bw+=JukqtZK@@k4AE_V;aHq5F z??7#=>j>hvgPq$QH^2T7(CGh8ee5_&u&w(tuYm@uUmg9SU)}}B2|j$%`-e%CQEh7N zPPt3bfF>!-&(FC!yIgw(tNWcqe;s8Um4$$=qpLdVoo7p`zol9)f5uAwIn)Gb++O2Z6UlpiBq&O~sl8$$pP3!j zJ(_4hM{Z2HF){sLFI_f01oDLu(6$@`)2kJFl!&))N{Mw1{^SUB982A^N)?5GE-p^# z7v9`^^ZK8g&L`?XL>%z`8DoxwjpWtXw}7+$)$e`i_-zlz^RRxoo$U}R375up3E)VGEHTdWyg|PqeafXF`VIo!w)$*8 zmA6ZJWOqgiFt?a>t24R@_ zY;gf3mMxb<)t$)CYLB^iow0tJ#7KQ)bJluJbqVw%q=|Th&r|w->#(n(xPLU|7E$y# z(fczf|0NOTq@{tfV)1R!41I6j)d0S3X!yD^At4TMUfCdBa&+%6c2F6!qyMJ#mz};Uzr9{n;L-aoT zyqw5$v6yX-!+Z)!JuEaiSRLFO(NO8SMlAY3MRfNAAr)VzfKqlfb$B443S8)j5NU2_ z2TH^T+q1KvVim=!o1cTyJVva4*vp?X za9pGGLlzdJGGo-gt)K3_E{olBZilIF7H*G-69R&9{^Wz`dj-65@2=m{)Q!mJRljIi zj%GKsl#k{MtTUM7Tob;`7gCuvieWbEw_0q#Bk_r@zl_myxB=*OZT4adiW`ck#7{SP z*^c})ylOFj+3@>iIe60^7BplO!7>!>bdkk|%s~0v*8fZItkvQ7fNSc2h`tLDDqwrv zSAR6Y7OeQ{UQXwU`5eXq3Q?dxL4Gli+(>oB#;(3$;CxfizvFuU<#yeEIMg2~apstH zgd3qPE1H>ULMBO#z8Axn zXmd_W@7O`J4h!{~PXRW<`RS2JvrO}>;EboFUWK&@2Ogir#lf&j8RXO*^P)AV1SoDB z4L6)0qRPDXEdhZ<$Rt~0I|WeH%j>gA5p?3h#lIKHtQg<i&(A6)J%~n_w8;}hsafe-uLasQkE%9H}&-G zs`wi~4|M@!I_?b1lpuYX+>}pz#}(Y_29If@MIBh&M0M8*)wjy~_m&d-jP_3oL-+<_ zVJv`Xx{^}dx9VQuw3PUwi_r&fiZB`C+1NsWK6{}9++Du0Wc-iMu3(LL(Uuxvu-{+{nKYk~!%{=2^pEB_FDt&OAms`Znf!G71DU`=uLso1r{r2D3+w z3yxrHRpWwWhBknoBbdK1N2-_M<#TG%KIYztol+Ru;OqaA4N;}`)MIu7Gy@bTk@|$b#tZ4bbs|vr^XWz zmLGXHR6b>i+>rLQ|VGgugEDeVoB+Ls1&-p-)b-q0pBeNdK z;--cd4J!S0p1zJOaT-IPjyX&fDbpW$_^$4+TMz?kh-B>y7I8pQ2Z*0yAKE`d9|N6N zqr-oVxlT2AMm-F=((^%+NL562&=`xVuwP9Y5qSzf2Dh%7H0agQ2lps^RS#xgbRUds z44MBV)EDaJS@G2)BGz+J_*@T?rBjGkR|gvLw)xP-hfz<9uG()*I+NYh_tC}C;&#f_ z5-ucN*u{PIl2eNEi~Vhx0%YT5Kx_2Uu2s(N+qJ((rK!%swZ24^7=Te{R}c(7B%$BX zeFm`vzBdG_yzJ?EQ{~>=E#9o~t8noz8|c}8^lRMd_)tk#q*?8{q>hOF!R9^R8Vm}7IR=g0p7 z6PLQl{vR+A(7ef9y_w+1FxT_{wD;XlP3`^Go?}4)K}AHRS?NVlL0V7|IrJVnK>?ML zASED#fXV@sjxe~2+39RX@=M-%LUP$&$udyIX( z_jl0Cj>!KLw$k<<9H3g5?h?dap3~BuB4A$^Dj-!Ng{X47vhb#d677lk#vm-^^GH4n z4!)?DYPQQK74LFSCA2a9f7uwjdQ!CB2l2xT$O{~?C`jwa_VoqXW%n<6O2NPx1b!V{Ft!h~+^=OnNj&wgaYjAg*Wwvk8MT@NoCrF?}*p`Fsg zo!P_N2Y6Cbmrs-l^FHmuc0XA3uNROqwYw9Wc0qL=6`!3iuUC=6$a(bj7;b9Ars=N} zxQ_s`YMHRnUh&0?o7!pFqbRHu`|q|tm^B0u{vMk2yVXzRXr%-l`OGo5*siy9WBen| z`@Opc(E4|nFr<4c^-^{d%$fqA(tQ(?k&I+EJ=Sk_3%)s7ap0vGR7D3R27nvWE_bKt zDNWMzN-mmB%ecK>2wb~H@3}Sfo4LUR+f{&tm4l~?tm8bZx6F%h9{@&(;f!vF)QP4mohCiZUPp-~-0>NS8)HCeFdrN$iiul93vf!-B5;eu^C2!15Y4POygHH!K$T3NV^+6>TpTjNn#vF z$g<$dW_peALhI&qLZ7QMKp44?Z(5CVzN=UKXJ@da_jSg%8#cubpO;_OdcyjCjzif1 zYs*Fe=&}(2{mq$t0=Z*ES@Q>e(RJ)4qp-9aM*lcbZTEG_DcWf;P!No*{!nr~z4#At z?-7TOknG_0rNgF~ru_X0AfUJJP%Snxd#~JXu}YYI>?&SgM!c(Scr0atC>{;zuuYk+DPE@k61YlgV)s-+vU*;A#{QE4=Gmr1DjILaWFN8NkGAWtCk7lM(B@Gl%v@3e_aD`7^VB9(eq^`!_qGx8mSq0wy7-|x?V^KR)Oij0E^M6Ak0i7MatQbI8V zeGqkT)t2+P^`iwr9Zp#PA1WMz>n&t}kiZ-L43-T++r5A0ZqTb-X}ej{9rrUs^Rj?%I4v}Hc->+N+R6(0jPgW5|PGsE~^t( zXbASo$u`!jL*Fbcxq}XTr5+XTM+HNAh)k_=U~vH0z`3Nv#}dmUazD4(?RRlNQ;ZBx zZ+u#J$d5@ffM_7opTk7|AHNF(<3CG!Ks%x9KZF1N^WCGkj~z?`CVJy2phy&V)nm{Y zsAObbb?f_UXL}t2V5+sEPUrxDZUE(d;pJJ)1E-V~n1er*&_{s`by+0^VA$sX0QQ$M z)(TbiaT+^Ta&|Qtv(w#Z`A?Dji8(rnMYVAt=I7!GFPiWGR}t&;$w0b_apt6s0Wjqb zTh9jm#jQKhCokXPJTBp=`j%a5OB6Rs0sa6YuVoIBwPu=i`%nd_r#rN9Wa9`>p$44? zN(u^|qiQ}uUFpi~6rl+K^@a0_!Uh14G-w|v<2SzmFQWH~hw>qPyqXf6`0)iFA{j4B zhjAiR?HYrI+*Pwg#4YDS_Z>P`Q2v{{aE47pIdmo53O=Bp-I?!D5z^c?s8qFkHfG4C%c zxb|Ma2XEKTjW&?zf>pNm`YBP1aJ3C7?V5h&{Wd0gsd6O(ZMwfVzz3S$i)Gi0)s16) zRqmFn)_(yl9Dp@$3zU1E2Ux(k!Fe!pZQUDdQREUq>{fInQMkpJPdVDU&2TIa3$y0h_zz)b++kr>Z|6 zgQ@dek6-z+5CnqIN=X)1$o3Y?ScGRayxh(GYgU+`CoxkQ#oJ#WupCX7wJLkR`uK0E z_E4{ju4sPC3tUG>$H*Zq#D@?3RVBk#&Gh^gXG}xX>E|C9K#}Kg$=TkryPDRuzOLgt zZ|6}GDTh2C%FBq1e50Z#L+?Cb2{&DALVyziJ7|oE zsOn`HHE&M7a*9m=j?wUaORqM~TE+>Vqs0Z4{BW&AgSceF``m#AmA-~aYxi6~l@JR| z&MUnYHLpeLU~AdL6|ss4Ois?n{iOi4MNF2cTU{~?PIKUZR`XfGtt=41&w5E?nP8HL zoN(l~#HwCCJdgZr)6Gt_2IRFObapN7o1v<#>%()UvS@}|E<(6hmKf>IMFdz|Ba1KQ z<2W!`!PNyW=0D8)@CvCS(okM>#5KEqEJUMF9flN^>Kh%+7+(`dnf3rWFG|=BaNfjO zSluU^ebQ1;T9Cx{M|)$xSc_U*MK?$lS{7+5U$gWsOP6VP2X^FBl6}xe8s8jNmo`V(5I7@AB}8U5xR(d9n+7WlkUGe z(HyC3tCGYP_gUxAr{!;cKy&I72eP$!@zbx}N1h6C??2#UH$G6oKbpGYRHu?@FaFo8 z+FL-75Fem!(|}m_TRu45g?jV2UUrSJ_vb4w^ki!G3Q40#1AuVGZA3MUJc4-V)?bvQ z3~mTq^`_4rVE_qnPGw~u@{^{q3R*AXP-UCm-uTA2p~|TW`;vb~^(Li@)hoTcXc1g9 z$4r~2nwrq>$Yw|81A9$?RoI}<81@lOj|k1Yu^q3Cqm#;6W)=54kF~wx!BhN5P#vFQ z1!3NsLyUpHrL4+&1=_MA>wG=z_eS&dOQM8?qO04gX!##k&U`a+cO@1MRn=DVeXjUE z1Z(IN{Bg%<@Fop`xobnc?aG^dz;*l4O=GrBOhuOp;V^@izp+y1`RjzN1q>rU;yB`7 zNY@45kU#LukF`<5!khQinh*-cB^gjCK*$dim7bnQ}1neyjh%F z^%&qyC8O`e3eoaBXD;XYoQ7v1E@^-XA{-`dp%!LEj?Ix-5=B-W)C)lMwz;T)BI|Za z%cLK&PdYNo&3-B{ES)~V>o-K{9?h4S{m##Rn*D(rT4g^kBBhL{go;6fKX?IM^V?^v z{@AnU_`zmC_B`XpK|t_)&;DCww;Y&i+m^~ppXKp!ek8>`H&!=T-^aYS4vBluZ!4LX zIaWbg`QF+X#id+%S~WDp+J;OXkgbT*{Bf|6OMEU0qIQmUSv;6=Ax*{oW=AdPgGL*! zL1Fx`tM^XWJXLhPe`3mC7WUGg=;;GJ4?&+x-Pzz&9)BKK%BZF9#y{k$o77I#hSez^ zYHHOMUggJciEAY8*8}IOmF88jhA`P;pZ5zmjIUvx;#fsjo2FH)8)vsG9{<#Qk6kqDztwb62&}S++0sGJKaI&dyrR5J->Cu<4s4U=L41C z4_)rwH;2(?X2)f$)Q9WPL39!YF7BFJjZj}rO=`=0OkQdOdy`P>VzR0$J(^}Bsn~+h z`E;Lr!6`3K+{Jj?kH1vj)B6;cv{=T4s-})@jLV{y4p|MbM!_)YnhcE}tWV5?v`>(;Fr# zAHHs|sJYw9*R-0SS!B0TJM^&8IwM39MI&FQW5PHc4BfcVxHXmS{9|c#K6rz(&4Km> zfe54DEJ6(4oqu9m;|tG%E^D)<-YN=Dxr?J@+Jq44Y8yS-k{Z%)LSdb)ag!Re0|wTm zEAK&Di@W-@So`U0%Bz%qoVx;Ri#f;h@IlHR`4Sw$p-cd8y^1m?0zDjFx& zl0DORAiW{qm#wR&D#+gh!WV4mO3>669u2j8xD0t=g_9zO#H_8a4#?*+91atzgwKrh+qXZ& z=K1l{7vI@yxV=enPZK&-gF#_kHLI9dx=r6CG9d)H|5PR4PS2qa8iFa+VB(zVK<{b$)BN%(k9gGJydmpox*$IY{HO^suH zBKDg{b>a9L!?&$_L$ZQ=BxCLB1C~#ub@MZRb?`OfgwdK2o_X-L@EH~EY)d1^#s`68 z95y|9E&iu*b1G@LQ?D+;W4UyVR*+)31|`7@nt1x?8cf1;A%r@22WF@721-1T_O=N} z3J_dV|3(_SYef>t)AR~_n!7Tt7a`EHyp;FlmAJ;HtC>KNk!Fdaq@M(|XB{HmUnY+TLyWCn^{mR2KmhLl2@4EIbIF1`RfMNon>}wP{h{YvZsLtuYioz>Rf-S(f)xOOnb=(Aqy-? z!d?@OHp@m+NO+vk0#7Mv!2`8&67{IqX<20?r{VQ*oihTkrDr%Y82zn-==+YN?A(zVoPH z;b&`ZG_^tzb@X4uDg4Ad-x(vS4bAev4F5}*nYcn~$;hiEL@o>Io~YlgQDkqFqD1zt zLqL;9d3Ysf5Ji7jQ+*yj`5IN6VL&yX0cH4PRSNtdQMTW{c`jJ#-aVa0QTr9W_%1;e zp_?9-L)6U=Jx5`KR-#x|o?&GbCG0-&-CX0Tp7)|v{~B#DHF2$vG}Y1A#7w?2oXFbj z4jB#0DDd&Do#+GPE=f4qBXjH#`yu?S4NS`%_wV`if%=ghvHWygdyTh&Xu}OvfLXeZ z2kkgkG@%=Kzkfgb?{LJM2Rz0CB9e{hPThUdz=e^b^wDDAzuHjo?Xo}j>{)cKIL3}# z-Y5R|f&b3KfA_=x|L4Mmo1LQkB~J(J$(|>3Kdk}h1qCbGE#S7ZyyEYR`&Z1J%gV{N z>}O9TXa3A_icvqmB1Uli&Um}oaV?YM zQ4jEvqwD6o2Jx1?f^GN4Wd0>Cn;-*ybau!{!af#upsJElx(7B8R$D znwqi=4h+K(@5ClQ?dExDh*H%9Pz$+)?R5h1%O=i)i$`4z(AF=@Q9=81s}F(M@253o zK6C)HiwMGbP#Pfjv1Iv+bg3L?WY`yL+p`q|bBV6f>m4=asPbw+@o|aVORIu=>!C+l zEV{KteeTXSvgY+>hF5RW21*b%EfC@4U*C<9wL;GOp!KZRuy=lDnFU+YB**?@s>h}? z5tP=HX?nt=pnzF@Yr6JPl~G<(on>n(qlgoX?$}z=l^m*phzQj=40B5u3>gX?*4g_u zK=Ejiitn66DCD%5W_aVvfj(M=87?s{W5utlS$A(DKaAkZg1m5K56EQG64zYhYEdS& z`w%Nr%n9~vusq8}!T6rV_>zOu@NQ`Rpa<2(L-pSdADSODJNrgs_+lzN?QJ>gdK6(4 zC^b`c(2Yif7T)#vo89X2p7Pdbqf$!st!f^o>AzMmo0hd3pO?4BNx7(Q9@OS z;rz8eZueaTi-QL$ZqJ{5XI(1TsFRXp*$j_hG zy9%T6HB59NCjaI$ObT3$O}gK-k^3dY-L;n)61aF*d97w*GG3EMXVV%GcxEqfa)=9G zv916%LHezOtbZI?ipV>9!DS&tO35OuNRbcM-r0itT*=RJUVFWF%&j+)tZ;V|w)v16 zj4wyPT6`;}zrY%SXBQwl2J1Z&VzY|CnQcR23B=SMEO z-a`O5A+q=(hF@>{@gj5MK$5hT^%zdSdhcPuVEfkSH%T=bP6KwzI~#m22hy&)-0xK} zA+_)SwN^`5{pMMJO+qfY2HLx|8KdB{=_iTdv>+5c#P%0d`x87JWKsvtE9osiUBI8c z$-oCzbfhp?Q{|=j{TOldtRTHxUDSXRtS+)yQGfZaZinBvbBE|n8Wij3lB!jeTPIv# zTU`nW<+dpU1p@LT^p2pz1KQFE$UE)O|Gh!c?x4jIy$PCR8v%Ps;Tb%0C1t@U)2+1w zxkS3jk+NIlwrS?1k+eydb}0?!*Gy(0q$YdiJo7x{4cqIhojT?0C5+UD+QOu^Lxg7S zif?sS0EIu%BuD;h+C?`*!xio>MJG*}&;@>PQ1+m8lj|F7O+ku~@n#_ulkFnE$ zS482b85JXbgAB2C{Q=7NU$L6x)K|8UA?>e zk{BNY5INJ2Ixub<6RcG8S9% z?QcS2b{J+;g}LX9J`p|uy7DkyPBz&+AW6B=+B3k+B)|3 zO)8IU0t-MDDvmt$&mwz-_V0)0wZAT@n@o{JcXTF7**~uJ^3L_q?BAvs6~)uV!GCc9 z0{y*6E<|?9gc**H`mPP3vmPM=%3x{E?+TpUCwReV+$V>AYQ1Uyjy$cF3``V=Jv$9zE7 z`oYI^o4|M9zi$#7{&2rhS?QNO<11zt2O~QQ%MXK%xa}o~z9>m5<+Al@vdY*MS>6o? z!ViuS1&1y?^x4%AM}UtNaJR$HJ9Bpdm}(W>1@X7~%<&(yfL#!?V%y^5$!35}bc_0z z4~uk6mXT;M_*HzA%0FjykxSQ;SnGHVK6{nzVV_x)7jRohKjYuC=SHTMiv;`PNkjp+ z*3St4$p3k7z@`5xU}lwL27j5B<615d*HGXCd%RPR7q9RD7h8D8++D!C-`Lp^?O(ds VP!lO01l-=Eqh)xz^p@kZ{{c!~lYamJ literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png new file mode 100644 index 0000000000000000000000000000000000000000..6386998ebcc114a015717a8951a7fd780b86795f GIT binary patch literal 295 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1T+c)B=- zSoFTVV93W9DAM|{KfF6&cGO9&90#M$xi?x~~kyk2X~7>@~G&WRc#&G})_( zRZ^-k<;2t8S1meAe@4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1ULJY5_^ zEPCIb+sNCLAmaLve}+SK`T@-n24SJ+8xlDmOQ=~jvRdB=%Fgz9!D%M)M7$*~zcTJ! z<@EYRiWO;73l1%FX8qJt6m((g`v|t>s}%2kesS1Bq-6eKBOReW#XkaH()d%Y-9PBQ zm2ot@*0KeV42Xr9oJ4vGm}+_ytzO}WkSYk!OGg(iv1i-vy81-gpC)78&q Iol`;+01eGvkN^Mx literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-ed.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-ed.png new file mode 100644 index 0000000000000000000000000000000000000000..0f9d808294ba2729510f17c35f2f5077b765e5b5 GIT binary patch literal 373 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1S(dAc}; zSoB`Kr0d5VD8usM{yQZD36{_q^B7#6RGYYSm_)yN@C7h3^&dB%qE_nA#K=C4`GOXY zib7V`i+9c&e9cZzi<0>M?ChEPd+h(^0;ldsFn-8W{o_ci-}bW^nGT6{hpJq^&${3x z8P5FZ)1nzMrq0TbQ!<+wh1Xo=H0*wHTxR<0Bcc~e8YRP+=JvEJo^@sXzMtjJ1FqgV zmv1%9(Yu#*M&9C-MazM_T!-0#hx`__M>@to&w2m3XUT&waaqSxKN|cVY+B%y5M}ex z(&f&%ul9DAWSnP7_TN~MvsXQ5>7FZQ8s;Z6S%2+&pDJXiG5bH;K897tZS`DN`^o}+ O&*16m=d#Wzp$Pz)7?1A& literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png new file mode 100644 index 0000000000000000000000000000000000000000..2988e9da8cc687d38f1bd2ef25f12bd0bf2bec30 GIT binary patch literal 504 zcmVwE>+o)DkeBC~zVQ8?bI&~| zRgDxca1x)(4r;@p&DoSDF2HYkw>YuS#VGW$OduH{f5J56Xki-fkqR(a| zcfK=wlZ|aO@SKBj9ugc zl_@$8xiiBfNO)huOR~pDsmAH-g8n}`07-yoLy(M5IxXnGWo%`HP9amE+5ZG(s8w+*1PiUSlGJ!rZoH0me_%L9;q@9p5ca@J- u4%P#Zh(&7AAlTY!3K2xG6xX%LL}Jivz*7zLpL^!qd-~dTs)OB> z`$B_Qe24M~f6Z9h=J79s62za&I72m1!v$sodxZ&FykR;}tJ51w^?nI^A)#6YC46RY*KV_+icf O00004nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1T!dAc}; zSoAKvXq(04C~@rL{WtRtd|9dcRfO#l7q_Lm!=9EW5{I(pFWKNX`xSvR6FoOu z$WK^4bLRKQ|IeK{Y%luHEc#t^+=h;Y%-3X(D=C=OKgye8A3xLf<5VUS0rmdbiI+B= zyt| + + + + + + Debugger: Dynamic Listing + + + + + +

Debugger: Dynamic Listing

+ + + + + + + +
+ +

The dynamic listing is analogous to Ghidra's listing for static + analysis, but in the dynamic context. That is, it displays memory contents from a target. More + precisely, it displays recorded memory contents in a trace. In most use cases, that trace is + "at the present", meaning it is the most recent memory from a live target. Multiple listings + can be displayed simultaneously, using the same pattern as many other Ghidra windows. The + "primary" listing is always displayed and generally tracks with the rest of the tool. Any + listing can be "snapshotted", i.e., duplicated. This is where dynamic listings differ from + static listings. Static snapshots remain in place; they do not automatically navigate. Dynamic + snapshots can still be configured to navigate, following the rest of the tool. A common use is + to configure a "snapshot" to follow the stack pointer. Still, you can disable a listing's + automatic navigation, so it behaves like a true snapshot. A current limitation is that you + cannot use snapshots to display different points in time for the same trace.

+ +

Where applicable, the static listing's location is synchronized to the dynamic listing, and + vice versa. The dynamic listing permits most of the same mark-up as the static listing. Any + mark-up added to the dynamic listing is saved to the trace. All mark-up (and nearly all trace + record types, for that matter) have a location in space and time. That is, they occupy an + address range (like mark-up in Ghidra programs), but also a time range (unique to traces). When + mark-up is added to the listing, it exists "from this time on". That is, its time interval is + from the current time to infinity, i.e., it has been created but never destroyed. When mark-up + is removed from the listing, its "destruction time" is set to just before the current time. If + this would cause the mark-up to "never exist", i.e., its destruction time is equal to its + creation time, then the record is deleted altogether. This yields a mostly-intuitive mechanism + for marking things up "in time," but the fact that mark-up is bound in time can still be + surprising. For example, disassembling some instructions and then stepping back in time will + cause that disassembly to disappear.

+ +

Because not all memory is captured, some background coloring is used to indicate the state + of attempted memory reads. Regardless of state, the most-recent contents, as recorded in the + trace, or 0s are displayed in the listing. "Stale" memory, that is ranges of memory which have + not been read at the current time, are displayed with a darker background. Where that memory is + marked "read-only" and has been successfully read previously, that coloring is subdued, since + the contents are unlikely to have changed. Where a read was attempted but failed, the range is + displayed with a pink background. Otherwise, up-to-date contents are displayed with the default + background color.

+ +

Actions

+ +

The listing provides a variety of actions, some for managing and configuring listings, and + others for capturing memory from a target.

+ +

New Dynamic Listing

+ +

This action is always available in the Window → Debugger + menu. It creates a new listing window with the same configuration as the primary dynamic + listing. It is equivalent to "snapshotting" the primary dynamic listing.

+ +

Export Trace View

+ +

This action is available whenever there is a trace open in the listing. It is analogous to + "Export Program," but for the current trace at the current time. This provides a mechanism for + capturing a particular point in time from a trace as a static image. The exported image can be + analyzed in Ghidra or another tool.

+ +

Follows Selected Thread

+ +

This action is only available on snapshot dynamic listings. The primary listing always + follows the tool's current thread. Disabling this toggle causes the snapshot to remain on its + own current thread rather than following the tool's. The current thread is used when computing + a location to navigate to automatically. It is only applicable when "Track Location" is set to + something other than "Do Not Track."

+ +

Track Location

+ +

This action is always available on all dynamic listings. It configures automatic navigation + for the dynamic listing. When location tracking is enabled, the listing is automatically + navigated to an address computed from the trace's or target's machine state. The address (and + its corresponding static address) are also highlighted in green. The computed address is + affected by the tool's current "coordinates," that is the selected thread, frame, and point in + time. The options are pluggable, but currently consist of:

+ +
    +
  • Do Not Track - disables automatic navigation.
  • + +
  • Track Program Counter - (default) navigates this listing to the current program counter. + If the stack contains a record of the program counter, it is preferred to the recorded + register value. Note that debuggers may vary in how they report the program counter, and its + meaning may vary among platforms. While there may be some nuances to the register value, the + value recorded in the stack should indicate the next instruction to be executed.
  • + +
  • Track Stack Pointer - navigates this listing to the current stack pointer. Again, + platforms may vary in how they define the stack pointer.
  • +
+ +

Go To (G)

+ +

This action is available whenever a trace is active in the listing. It prompts the user for + an address, which can be expressed in Sleigh, then attempts to navigate to it. The expression + is evaluated in the context of the current thread and point in time. If the current trace is + live and at the present, the target may be queried to retrieve any machine state required to + evaluate the expression.

+ + + + + + + +
+ +

Sync to Static Listing

+ +

This action is always available, but only on the primary dynamic listing. It configures + location synchronization with the (primary) static listing. When enabled, navigation in either + listing — including automatic navigation — automatically navigates to the + corresponding location, if applicable, in the other. In general, "corresponding location" is + computed using information about loaded modules reported by the debugger. For the finer + details, see the Static Mappings + window.

+ +

Capture Memory

+ +

This action is available when the current trace is "at the present" with a live target, and + there is a selection of addresses in the dynamic listing. It will instruct the recorder to + capture the contents of memory for the selected range(s). Typically, the viewable addresses are + automatically captured — see the Auto-Read action.

+ +

Auto-Read Memory

+ +

This action is always available on all dynamic listings. It configures whether or not the + memory range(s) displayed in the listing are automatically captured. Like the Capture Memory + action, capture can only occur when the current trace is "at the present" with a live target. + It occurs when the user scrolls the listing, or when the listing is otherwise navigated to a + new location. Note that other components may capture memory, regardless of this listing's + configuration. For example, the recorder typically captures the page of memory pointed to by + the program counter. In other words, this action cannot "disable all memory captures." + The options are pluggable, but currently consist of:

+ +
    +
  • Do Not Read Memory - disables automatic memory capture for this listing + only.
  • + +
  • Read Visible Memory - automatically reads stale ranges that enter this listing's + view.
  • + +
  • Read Visible Memory, RO Once - (default) behaves like Read Visible Memory, except it will + neglect to capture read-only ranges that have been captured previously.
  • +
+ +

Auto-Import Current Module

+ +

This toggle is available whenever Sync to Static Listing is enabled. It causes Ghidra to + prompt the user to import unknown modules. Specifically, when Ghidra cannot map the dynamic + listing's location to a static location, but the debugger reports a module containing the + dynamic address, it prompts the user to import that module:

+ + + + + + + +
+ +

This non-modal dialog collects those prompts and appears whenever a new module is suggested. + To import a module, click the import icon to the suggestion's right. To remove an entry, click + the delete icon to the suggestions's left. To ignore an entry, check the box to the left of the + suggestion and dismiss the dialog. Note that a removed suggestion is not ignored. Ghidra may + suggest that module again. To import modules manually, see the Modules window.

+ +

Tool Options: Colors

+ +

The memory-state and tracked-location background colors can all be configured here.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerGoToDialog.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerGoToDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..ae51a07ff43030c94ef8cc9c20a0e2db94bbcf27 GIT binary patch literal 9986 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 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html new file mode 100644 index 0000000000..31db45d57f --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html @@ -0,0 +1,42 @@ + + + + + + + Debugger: Model Service + + + + + +

Debugger: Model Service

+ +

This service plugin backs the Targets window. It maintains + all debugger connections across the entire Ghidra session, that is, they're shared across all + tools.

+ +

Actions

+ +

The plugin also provides some shortcut actions.

+ +

Debug Program

+ +

This action is available whenever a program is opened, and the current program indicates an + "executable path" that exists on the local file system and is marked executable by the host + operating system. It will launch a suitable connection for debugging local applications, and + then run the current program in that debugger. If Record + Automatically is enabled, this will provide a one-click action to debug the current + program. This is similar to the Quick Launch + action in the Commands and Objects window, except this one creates a new connection.

+ +

Disconnect All

+ +

This action is always available from the main application window. It closes all debugger + connections, no matter what tool created them. This is a sort of panic and reset action.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html new file mode 100644 index 0000000000..d7a3eee373 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html @@ -0,0 +1,165 @@ + + + + + + + Debugger: Modules and Sections + + + + + +

Debugger: Modules and Sections

+ + + + + + + +
+ +

The concept of a module may vary from platform to platform, but in most cases, it refers to + a binary image which is loaded or mapped into memory. Likely, these are the same files that + Ghidra can import for static analysis. Similarly, the concept of a section may vary, but in + most cases, it refers to a portion of a module, possibly backed by its binary image. This + window displays information about modules, and sometimes their sections, known to the connected + debugger. Information in this window reflects what has been recorded into the current trace, + which in turn comes from a target. The top table displays module information, and the bottom + table displays section information.

+ +

Table Columns

+ +

The top table, which lists modules, has the following columns:

+ +
    +
  • Base Address - if available, the minimum address where the module is mapped in the + target's memory. Double-clicking this field navigates to the address.
  • + +
  • Max Address - if available, the maximum address where the module is mapped in the + target's memory. Double-clicking this field navigates to the address.
  • + +
  • Module Name - the name of the module, ideally its full path on the target's + filesystem.
  • + +
  • Lifespan - denotes the recorded load and unload times of the module, i.e., the span of + time for which the module record is applicable.
  • + +
  • Length - the length from base address to max address, inclusive. Note that not every page + in the range is necessarily mapped.
  • +
+ +

The bottom table, which lists sections, has the following columns:

+ +
    +
  • Start Address - the minimum virtual memory address of the section. Double-clicking this + field navigates to the address.
  • + +
  • End Address - the maximum virtual memory address of the section. Double-clicking this + field navigates to the address.
  • + +
  • Section Name - the name of the section given by the debugger, usually the same as the + name given in the module's header.
  • + +
  • Module Name - the name of the module containing this section.
  • + +
  • Length - the number of bytes in the section.
  • +
+ +

Actions

+ +

This window provides several actions, some of which are only accessible via pop-up + menus.

+ +

Import From File System

+ +

This action is available from a module's or section's pop-up menu. It prompts the user to + import the module from the local file system into Ghidra as a static image.

+ +

Capture Symbols

+ +

This action is available when the current trace is associated with a live target and "at the + present," and at least one module is selected. It commands the recorder to copy all symbols + (that the debugger knows about) for the selected modules into the trace. These are often the + same symbols that Ghidra imports from the static image, anyway.

+ +

Capture Types

+ +

This action is available when the current trace is associated with a live target and "at the + present," the target presents type information, and at least one module is selected. It + commands the recorder to copy and convert all types (that the debugger knows about) for the + selected modules to Ghidra data types in the trace. These are often the same types that Ghidra + imports from the static image, anyway.

+ +

Map Modules

+ +

This action is available from the modules' or sections' pop-up menu. It searches the tool's + open programs for the selected modules and proposes new mappings. The user can examine and + tweak the proposal before confirming or canceling it. Typically, this is done automatically by + the Map Modules debugger + bot.

+ + + + + + + +
+ +

Map Module to Current Program

+ +

This action is available from a single module's pop-up menu, when there is an open program. + It behaves like Map Modules, except that it will propose the selected module be mapped to the + current program.

+ +

Map Sections

+ +

This action is analogous to the Map Modules action. It searches the tool's open programs for + blocks matching the selected sections and proposes new mappings. Users who prefer this to Map + Modules should also consider using the Map Sections debugger bot.

+ + + + + + + +
+ +

Map Sections to Current Program

+ +

This action is available from the pop-up menu, when the current selection indicates a single + module and there is an open program. It behaves like Map Sections, except that it will attempt + to map sections from the indicated module to blocks in the current program.

+ +

Map Section to Current Block

+ +

This action is available from a single section's pop-up menu, when there is an open program. + It behaves like Map Sections, except that it will propose the selected section be mapped to the + block containing the cursor in the static listing.

+ +

Filter Sections by Module

+ +

This action is always available. By default the bottom table displays all sections in the + current trace. When this toggle is enabled, and at least one module is selected, the bottom + table will only include sections contained by a selected module.

+ +

Select Addresses

+ +

This action is available when at least one module or section is selected. It selects all + addresses in the dynamic listing contained by the selected modules or sections.

+ +

Select Rows

+ +

This action is available when the dynamic listing's cursor is at a valid location. It + selects the module and section, if applicable, containing that cursor. If the dynamic listing + has a selection, it selects all modules and sections intersecting that selection.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModuleMapProposalDialog.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModuleMapProposalDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..5b9873de52aa4c6a727a931333ee327b55145c25 GIT binary patch literal 15294 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 H8hHLM*8%C= literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModulesPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModulesPlugin.png new file mode 100644 index 0000000000000000000000000000000000000000..21da4fa7ceae833adff4c3c5c40dfb49d4c3ce7b GIT binary patch literal 29396 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 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerSectionMapProposalDialog.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerSectionMapProposalDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..831ea6d4a10b308c4b7baa7215a9d87362300eb5 GIT binary patch literal 22173 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 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html new file mode 100644 index 0000000000..ca3ee27dbf --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html @@ -0,0 +1,351 @@ + + + + + + + Debugger: Commands and Objects + + + + + +

Debugger: Commands and Objects

+ + + + + + + +
+ +

The Debugger's Objects window permits the user to interact directly with a connected + debugger and its targets. Along with the command-line, it is the primary mechanism for issuing + commands, e.g., step, to a target. Commands are accessible from the tool bar, the + pull-down, and pop-up menus. Commands are enabled in two ways: (1) the object selected has a + property that marks it as a logical target for that command, or (2) the object has an ancestor + for which the command makes sense. The Act on Selection Only function + determines whether both options are in play. For example, threads and inferiors/processes are + both resumable, so the Resume action + works on both. For many of our targets, processes are interruptible while threads are + not. Nevertheless, if Act On Selection Only is off, you can interrupt a thread because + it descends from an inferior or process. In almost every case, the selection directly or + indirectly determines the set of valid or enabled actions.

+ +

The application of these special properties to each object to determine its behavior and + relevant actions allows all objects to be treated generically. This feature has several + powerful implications. All objects may be represented in many ways, and one representation may + easily be converted into another. The default representation is a tree, and the Debugger's + Objects plugin starts with a tree to represent a debugger session's state. Any portion of the + tree may be reproduced as its own subtree or as a table or as a graph using the Display as... actions. Any portion of display may + equally be exported or imported from the plugin. The generic aspect of all objects also allows + the user to process them in scripts or plugins in common ways. For example, you could write a + script to walk the entire tree and find objects with "File" in the name or with the value + 0xDEADBEEF.

+ +

The hierarchy reflected in the initial display has either been derived from the target, as + in the case of WinDbg v.2's dbgmodel engine, or imposed by the designers of the current + "model". The model is the schema chosen by the authors for a particular debugger target. It + usually mirrors to some extent the underlying relationships in the native debugger, but need + not, and is usually fixed for that target. (In the future, we intend to expose this schema and + allow it to be transformed by the user.) Commands executed within this plugin are passed from + the GUI through the model to the target manager and on to the agent which makes the required + native calls. In general, this plugin does not operate on any object in a trace or controlled + by a recorder, except for actions which initiate a recording or add objects to a recording.

+ +

Console

+ +

Shows the console for the selected context, usually the debugger's command-line interpreter. + Some models may also present a target's standard I/O via a console.

+ +

Target Management

+ +

The following actions manage targets, e.g., processes, within a connected debugger.

+ +

Quick Launch

+ +

Launch a new target using the current program.

+ +

This action is the fastest and probably most common way of starting a debugger target. The + action attempts to launch the program in the currently selected tab of the static listing + window. The success of the action depends on the program being associated with an existing + executable file on the local system. The value associated with the program may, of course, be + checked using the About Program File + function.

+ +

Launch

+ +

Launch a new target. Sometimes you may wish to launch a program which has not been imported, + to launch a program with various configuration options, or to pass arguments to the program. + The launch dialog allows you to do this. Typically, the dialog will present a single empty box + into which you can enter the command with arguments as you would on a command line. For some + targets, the dialog will present pre-configured options corresponding to common launch modes + for the platform.

+ +

For example, the CommandLineLaunch version of the dialog for JDI targets displays a check + box for whether the target shuld be suspended on launch, the default quote character used by + the command line, the program used to start the virtual machine (java), the path to where it's + installed, and fields for the name of the class and any user options you might supply:

+ + + + + + + +
+ +

The options displayed by the Launch command are, for some targets, connected to the + currently selected item under Connectors. For example, the Windows tree offers four possible + connectors: one for command-line launch, one for attach, open for kernel-mode debugging, and + one for opening previously-generated dump or trace files.

+ +

Attach

+ +

Attach to a running target

+ +

If the debugger has available a list of running targets for the local system, the attach + action provides a convenient way to select and attach to an existing process. If the + highlighted object in the provider is considered "attachable", the attach action attempts to + connect that element. The list of "attachable" targets is typically displayed in the tree under + Session/Available. Bare in mind it often takes time to populate the Available + list, which may need to be manually refreshed.

+ +

Detach

+ +

Detach from the selected target, usually allowing it to resume execution. (This action + terminates the current trace for the target, if applicable, but does not close it.)

+ +

Re-attach

+ +

Re-attaches to the selected target. (Some targets require a separate action for re-attaching + vs. attaching for the first time. If so, this action provides that capability.)

+ +

Kill

+ +

Kill this current target. (As with detach, this terminates a current trace, if + applicable, but does not close it.)

+ +

Execution Management

+ +

The following actions manage execution state of a target.

+ +

Interrupt (Pause, Suspend, + Break)

+ +

Interrupt the current target's execution.

+ +

Resume (Continue, Go)

+ +

Allow the current target to resume execution.

+ +

Step Into

+ +

Step the current target to the next instruction.

+ +

Step Over

+ +

Step the current target to the next instruction in the current subroutine.

+ +

Step Finish

+ +

Allow the current target to finish the current subroutine, pausing after.

+ +

Set + Breakpoint

+ +

Set a breakpoint, which will trap target execution under certain conditions.

+ + + + + + + +
+ +

The given expression can follow any form accepted by the connected debugger, although most + often this will be an address. The listing can only set breakpoints on specific addresses, not + symbols or expressions. The listing is also not available until the target is being recorded. + For a recorded target, breakpoints can also be managed in the Breakpoints window + or using the Breakpoint + Marker actions from the disassembly listings.

+ +

Trace Management

+ +

The following actions manage target tracing. Note that many other windows are not usable + until a target is recorded into a trace.

+ +

Record

+ +

Record the current target, if its platform is recognized, and open its trace. If Ghidra + cannot uniquely identify the platform, it may prompt the user to select from a list of + possibilities.

+ +

Record + Automatically

+ +

Automatically record and open recognized targets. If Ghidra cannot uniquely identify the + platform, it will select one by priority without prompting the user.

+ +

Display Management

+ +

The following actions can create additional displays of portions of the debugger model.

+ +

Display As + Tree

+ +

Constructs a new tree using the selection as the new root, synchronized with the current + provider.

+ +

Display As + Table

+ +

Constructs a table from the current object, synchronized with the current provider. If the + object is a container, its elements will be rows in the new table and their attributes will be + columns. If not, the attributes will be rows and their name, kind, value, and type will be + columns.

+ +

Display As + Graph

+ +

Constructs and displays a graph from the selection and its visible descendants. (The graph + does not currently maintain synchronization.)

+ +

Display As + XML

+ +

Encodes the selected object and its visible descendants in XML and prints the result to the + console. (Note: various characters not allowed in XML may be converted, typically to + underscores, in the result.)

+ +

+ Display Filtered Tree

+ +

Constructs a static snapshot of the selection and its visible descendants as a tree, + applying a filter to the selection.

+ +

+ Display Filtered Table

+ +

Constructs a static snapshot of the selection and its visible descendants as a table, + applying a filter to the selection.

+ +

+ Display Filtered Graph

+ +

Constructs a static snapshot of the selection and its visible descendants as a graph, + applying a filter to the selection.

+ +

+ Display Filtered XML

+ +

Constructs a static snapshot of the selection and its visible descendants as XML, applying a + filter to the selection.

+ +

Display Methods

+ +

Displays a list of the methods available for the selection, which may be applied and + combined in a filter.

+ +

Data Management

+ +

The following actions can export and import model information.

+ +

Export as XML

+ +

Convert the selection and its visible descendants to XML and export the result to a file of + the user's choosing.

+ +

Import from + XML

+ +

Import "facts" from a file of the user's choosing and render it as a tree.

+ +

Export as + Facts

+ +

Convert the selection and its visible descendants to "fact" files and export the result to a + directory of the user's choosing. (Currently, "fact" files itemize the path, name, value, type, + and children for each object.)

+ +

Import from + Facts

+ +

Import XML from a directory of the user's choosing and render them as a tree.

+ +

Import from trace

+ +

Import from trace is roughly equivalent to using the open dump/trace connector.

+ +

Miscellaneous

+ +

The following actions are for maintenance or experiments

+ +

Refresh Node

+ +

Queries the model for the current object's children and rebuilds that portion of the + display.

+ +

Subscribe / Unsubscribe to Selection

+ +

Asks the recorder to include or exclude the current object from the trace.

+ +

Act on Selection Only

+ +

Toggles the scope of the other action menus. If "selection only" is chosen, the current + object must have the property used to enable the particular action. For instance, the + step action will only be enabled for objects marked steppable. If "selection + only" is off, the debugger will walk the path up from the current object looking for an + ancestor for which the action is enabled. For example, if threads are steppable and + contain registers as children, step will still be enabled for that thread if the current + selection is one of its registers.

+ +

Hide Intrinsic Attributes

+ +

Toggle which objects are visible (mostly for diagnostic purposes). Attributes for each + object may be split into those that convey information intended for the user and those that + convey information for the debugger. The latter are not normally visible, because they just + clutter the display. However, under certain circumstances, the user may want to see them. For + example, the _update_mode attribute determines when node is refreshed, and the + _state attribute indicates whether a thread is running or stopped. In the latter case, + this information is reflected for the user in the display, but the invisible attribute actually + determines the behavior.

+ +

Ignore State

+ +

Many actions are normally allowed only if the target is stopped or in some other defined + state. Sometimes you may wish to ignore the current state and force the action, as, for + example, when the debugger has lost track of the true state.

+ +

Color Options

+ +

The debugger represents different types of objects with different colors. Bare in mind these + colors are not blended, i.e. one color may override another. Examples include:

+ +
    +
  • Default: normal objects
  • + +
  • Modified: objects recently changed
  • + +
  • Links: objects from a different portion of the display
  • + +
  • Intrinsics: objects with a value, such as strings or numbers
  • + +
  • Invisible: objects used internally by the provider
  • + +
  • Accessors: objects which may used to supply other objects (before they've been + populated)
  • + +
  • Targets: native objects with special properties
  • +

+
+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/DebuggerBreakpointDialog.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/DebuggerBreakpointDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..b605584a9af3827f9559349824c72d27e4bf33d5 GIT binary patch literal 5981 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 Y4dsu`5+A{LiyqQYeQ-Bl<>AZ!0eoVGYXATM literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/attach.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/attach.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0196300df11d04e522d28b53464790426612a2 GIT binary patch literal 562 zcmV-20?qx2P)QVYprdx z+wG44rUDot8Qmd!yqhe0?|Z;rRSZz>-+1qP&bcc!0LsPEZ>s77puqG5)h7TIk%byS zU>;z9ilWo$JOaK(FJAzbBgi1jvWGQ*!Vq9;KT=h}MN9}mqR{Vw#+N^rzUiF11I$FZ zt}*6ybe>6)WMjOAR{%k|uB!b}h{2yDU{L1zbwJ>mfEU5tzVx)Os!iaWh~!Z#xwUpl zRhwCsZP&T?*dRr#)yr9`QsW=#!xm>E<)x*Z0t#RjplEUcbWC7V4aM*~dgLgrV7LZ2 zRn}TpdESVMcgo@~z^s5(SPzDE0DBC=q23^dMF2}m1Ly*0O9khFZ#PeK3;3ZhEpQz8 zsqo1NF9g;WSJ^t`rL2(ho|L%>Q~ygFDfwsn1+j&oxIPWydH?_b07*qoM6N<$f?W>t AJOBUy literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/blank.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/blank.png new file mode 100644 index 0000000000000000000000000000000000000000..fcc18904fe57253da5e1100c9fb38ef01e05b72c GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ zFuQ>;@w}Bh~-_ literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/breakpoint-set.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/breakpoint-set.png new file mode 100644 index 0000000000000000000000000000000000000000..e12b41d30654c654651c93e19924ee49b9093c21 GIT binary patch literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1V4c)B=- zSoFS~XzRu7DA6|m{q2OcnxejnVtFF7BW`udIfUJq5~tWZU10qNUx7Q^84J}tCMs!X zWnR)aVdms9QF3kQY;y09A$Num~|KHb{NmX8p=HYJLCf-u~w+56pFX&f~UjfmnxR zbd$`R^XuHcYTvfbEMl^VOS|ayz{JeqouI?%pq#n37e${s~dN ZZ)a4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1UndAc}; zSoEHqHX?OQu? z-!A%hmuJs$d5MHq91I#^;s03uLN%YgTe~DWM4f4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1ShdAc}; zSoFTVV93W9DB^I@xFar6vMiB1Y>}YiPZ6i3*CJY768WWCT{V`8D7hJ*{KIot=fK*y zytsG2fBw?CoOycTdHSl%!0v2>mtl z;PwOgM%VKjLz0f%elBWv!*TVN4+p|-AGyKW!Zlx*{oAazW#^V=O#MIkGgE+;?kC@B S<269nF?hQAxvXunHmwmU1>GaGqEs2nq_O$k`Zj$&Q70$L@Om0CB4C zciy~tZyx+_gvEtxVKJToV48hYaJnA+(ZOm+8V6vLc>pTRb;|lx-^plUu(cJGC7;EN z0&5=*&b@06)J}Ir6;$ZF$EM<|ftauf0k*f;D?FH5cx3qVr4it1bD*}#Zb|@M16iTu z8OukpW_!hx0}g$nq$E6}|o~OBbHM-bK b`_K3ev$u%_!cMn000000NkvXXu0mjf#WdkL literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/detach.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/detach.png new file mode 100644 index 0000000000000000000000000000000000000000..04e8e975db03191bd465efa5b73030d07c6dfd19 GIT binary patch literal 590 zcmV-U0M?WYNn=jZ3h)Ei|5wv*Kp8j{hT#JT;EYDvfS`Vw&yr>V6_5e;0Wz~$ z00)3Lk>zE8Fbr=2Uk40H$AP(=NH++An?nJE8GxNKaiLLWE=dy0T@4L&8R`L)cT(*8 z{vt4uBRq4?J4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1Uvdb&7< zSoB`KxX|x#g2>U2@wYj7(wY>_nDrY{`44cLG|2cV%+(}}4wt#8Kg-p;s{M@7^Tg4jNgveDKi8Tb)Z0{dtl~(V z>;p+7HsxDOe=jh)d}?P@rPHCh6B5?hlMZ~DaW|f`%14QhJI^4V`Nk@1N1rOE{SVgV zSwGk)wjrRoc+dKZUmTWqIXXOxcd>eM&SK?J<`awmWB!FVyVhK3LY_+|(BllAu6{1- HoD!M>q?GuNnCdgP^*Bj5V_b?dAq2Ppn9^MBB^YUM zad0N-T{Ujg*A6d~mYV4na=hT4Nz+_}SGTgW|Iir!%$ z;@OGkWI6+j0H}~K4RYR%!7y|zM`O@*K>rL{*&}x3lR**HrMXC1->#slU>X|w!U1xQ zqc4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1T+c)B=- zSoFTVV93W9DAM|{KfF6&cGO9&90#M$xi?x~~kyk2X~7>@~G&WRc#&G})_( zRZ^-k<;2t8S1meAe@EP5~x^;@yBmHMp*d-!ep{eG?AZ?;vw%j@p@eSbcm`~LO)ehcFGF&^%|?i32e zgUexW1L@^hRA;b{9xZhP;hYu46;P>E6&yPNTDJ_&o-8mu<5*5PKl8qUp^qqfw`lvx zWRX~ynM4tb#mm!9q-PxwW+W{?nVIsy8174U3x=4P2p206&PfFBOsKu^yvK&e!!RJ}l7-*2Gb zVE~2)U^NWGYPEU@7@jiX@CVRn{%Eustya7DF4(j>oi4FdceVFyRhT#~F<4;=fsy4j(Gv3gJf9nMUd~}N0Y<>foKX!vOk4+&6;)8iBlAFv(43&G^Fkw7SAv8lUcZV0k~xV>-b(plp8xcmOoIFljzMNX%o zu;#q0^Q0Rx5|{8ARIyEWxMw6)TXV`*3cWv64Utw>EBY@ZGD9~U01^|J{T**v&i?aeBG5m?_!JF&jL y%QBszXHXXxJ{n}QoA_?7mvYZ}GrA-`DJ}R6iBjm2?r!<*_{iL7KD##RP~Jbp*6qIl literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/display_filtered_graph.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/display_filtered_graph.png new file mode 100644 index 0000000000000000000000000000000000000000..201d4a298d5ecc34411ce89b499e360ee0786b17 GIT binary patch literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1Uvdb&7< zSoB`KxX|x#g2>U2@wYj7(wY>_nDrY{`44cLG|2cV%+(}}4wt#8Kg-p;s{M@7^Tg4jNgveDKi8Tb)Z0{dtl~(V z>;p+7HsxDOe=jh)d}?P@rPHCh6B5?hlMZ~DaW|f`%14QhJI^4V`Nk@1N1rOE{SVgV zSwGk)wjrRoc+dKZUmTWqIXXOxcd>eM&SK?J<`awmWB!FVyVhK3LY_+|(BllAu6{1- HoD!M>q?GuNnCdgP^*Bj5V_b?dAq2Ppn9^MBB^YUM zad0N-T{Ujg*A6d~mYV4na=hT4Nz+_}SGTgW|Iir!%$ z;@OGkWI6+j0H}~K4RYR%!7y|zM`O@*K>rL{*&}x3lR**HrMXC1->#slU>X|w!U1xQ zqc4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1T+c)B=- zSoFTVV93W9DAM|{KfF6&cGO9&90#M$xi?x~~kyk2X~7>@~G&WRc#&G})_( zRZ^-k<;2t8S1meAe@EP5~x^;@yBmHMp*d-!ep{eG?AZ?;vw%j@p@eSbcm`~LO)ehcFGF&^%|?i32e zgUexW1L@^hRA;b{9xZhP;hYu46;P>E6&yPNTDJ_&o-8mu<5*5PKl8qUp^qqfw`lvx zWRX~ynM4tb#mm!9q-PxwW+W{?nVIsy8174U3x=4P2p206&PfFBOsKu^yvK&e!!RJ}l7-*2Gb zVE~2)U^NWGYPEU@7@jiX@CVRn{%Eustya7DF4(j>oi4FdceVFyRhT#~F<4;=fsy4j(Gv3gJf9nMUd~}N0Y<>foKX!vOk4+&6;)8iBlAFv(43&G^Fkw7SAv8lUcZV0k~xV>-b(plp8xcmOoIFljzMNX%o zu;#q0^Q0Rx5|{8ARIyEWxMw6)TXV`*3cWv64Utw>EBY@ZGD9~U01^|J{T**v&i?aeBG5m?_!JF&jL y%QBszXHXXxJ{n}QoA_?7mvYZ}GrA-`DJ}R6iBjm2?r!<*_{iL7KD##RP~Jbp*6qIl literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/export_as_facts.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/export_as_facts.png new file mode 100644 index 0000000000000000000000000000000000000000..f3f1ee447d54e374015945cb0b0a907d7ffe7487 GIT binary patch literal 545 zcmV++0^a?JP)P000pP1^@s6D7ps@00004b3#c}2nYxW zdr^_0nBVr zm07$wnHTj4_z1Y9wfWv5iK@&L=AYMERb~R70S~%_hQK|*B+<7Gm$3HxhqVM=y*>p- z$F~`pS#QH@h==n|&A|pBXHic&(9IZ~=0&{^oB&%unknJx6G!8NO?v>S%50Js^}d;% zNNRhUoN+WhxFhqsVl>H%dJlNl57JHnXfEES0Y#($YLPd{7oZk!etbD(_wmc+))!kq zzIOdNW`h+#NB{!zHjTwgyqzt#KHB8F0;xNJ3xp$gAS5GS{IVfIuL}ii3&>gO!z@)8 zLf!xo`Mk48Hu46=%S~tB0_igIb_(57v-(XEEPw(rwtojv{PvqsGD(MmNY~^5M5ntt z0Op$@CJ8fpQkB^@wF^LA)MMa{q{BXt9pEYO9C+B1aRs~wjwQ|hw9uAMy_p$sAK2*$ ji9jPMY+7jlEP5~x^;@yBmHMp*d-!ep{eG?AZ?;vw%j@p@eSbcm`~LO)ehcFGF&^%|?i32e zgUexW1L@^hRA;b{9xZhP;hYu46;P>E6&yPNTDJ_&o-8mu<5*5PKl8qUp^qqfw`lvx zWRX~ynM4tb#mm!9q-PxwW+W{?nVIsy8174U3x=4P2p206&PfFBOsKu^yvK&e!!RJ}l7-*2Gb zVE~2)U^NWGYPEU@7@jiX@CVRn{%Eustya7DF4(j>oi4FdceVFyRhT#~F<4;=fsy4j(Gv3gJf9nMUd~}N0Y<>foKX!vOk4+&6;)8iBlAFv(43&G^Fkw7SAv8lUcZV0k~xV>-b(plp8xcmOoIFljzMNX%o zu;#q0^Q0Rx5|{8ARIyEWxMw6)TXV`*3cWv64Utw>EBY@ZGD9~U01^|J{T**v&i?aeBG5m?_!JF&jL y%QBszXHXXxJ{n}QoA_?7mvYZ}GrA-`DJ}R6iBjm2?r!<*_{iL7KD##RP~Jbp*6qIl literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/import_from_facts.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/import_from_facts.png new file mode 100644 index 0000000000000000000000000000000000000000..f3f1ee447d54e374015945cb0b0a907d7ffe7487 GIT binary patch literal 545 zcmV++0^a?JP)P000pP1^@s6D7ps@00004b3#c}2nYxW zdr^_0nBVr zm07$wnHTj4_z1Y9wfWv5iK@&L=AYMERb~R70S~%_hQK|*B+<7Gm$3HxhqVM=y*>p- z$F~`pS#QH@h==n|&A|pBXHic&(9IZ~=0&{^oB&%unknJx6G!8NO?v>S%50Js^}d;% zNNRhUoN+WhxFhqsVl>H%dJlNl57JHnXfEES0Y#($YLPd{7oZk!etbD(_wmc+))!kq zzIOdNW`h+#NB{!zHjTwgyqzt#KHB8F0;xNJ3xp$gAS5GS{IVfIuL}ii3&>gO!z@)8 zLf!xo`Mk48Hu46=%S~tB0_igIb_(57v-(XEEPw(rwtojv{PvqsGD(MmNY~^5M5ntt z0Op$@CJ8fpQkB^@wF^LA)MMa{q{BXt9pEYO9C+B1aRs~wjwQ|hw9uAMy_p$sAK2*$ ji9jPMY+7jlEP5~x^;@yBmHMp*d-!ep{eG?AZ?;vw%j@p@eSbcm`~LO)ehcFGF&^%|?i32e zgUexW1L@^hRA;b{9xZhP;hYu46;P>E6&yPNTDJ_&o-8mu<5*5PKl8qUp^qqfw`lvx zWRX~ynM4tb#mm!9q-PxwW+W{?nVIsy8174U3x=4P2p206&PfFBOsKu^yvK&e!!RJ}l7-*2Gb zVE~2)U^NWGYPEU@7@jiX@CVRn{%Eustya7DF4(j>oi4FdceVFyRhT#~F<4;=fsy4j(Gv3gJf9nMUd~}N0Y<>foKX!vOk4+&6;)8iBlAFv(43&G^Fkw7SAv8lUcZV0k~xV>-b(plp8xcmOoIFljzMNX%o zu;#q0^Q0Rx5|{8ARIyEWxMw6)TXV`*3cWv64Utw>EBY@ZGD9~U01^|J{T**v&i?aeBG5m?_!JF&jL y%QBszXHXXxJ{n}QoA_?7mvYZ}GrA-`DJ}R6iBjm2?r!<*_{iL7KD##RP~Jbp*6qIl literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/kill.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/kill.png new file mode 100644 index 0000000000000000000000000000000000000000..b7f91ad889999a094adb771b1226a97442b4ef96 GIT binary patch literal 533 zcmV+w0_y#VP)ytVyVTgeuIN6RK(b*pn^p>(B8suftmOI^PB&h z8Tg-ZT+S~am-AD}d;l7Nw%a4|i~e%|bPDPA%!Cl@p0H&j2qBw}233?sI9RY9VXki$D--1Og9+Eo6PzdqTz^ z#T_T5k>O-LULVeneR80YAnCKWoNp+yDWW^|f0>J(4|iI(nVhQ33Oa{S7+tGur`q)R z)Rmw`lwu}brygcnCn|sy)j@z)0R}r1;uTOPMYJCfRrw>LzcFm&S5L0qY{7r|?(Y|m X@z9H$0%g;Z00000NkvXXu0mjfuPEXw literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/launch.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/launch.png new file mode 100644 index 0000000000000000000000000000000000000000..948c5efde44473935dce2a05a21e085e00edad21 GIT binary patch literal 623 zcmV-#0+9WQP)hWn+`6z2oAdFq;2vR1%qJ4rD~xOA~x@H4o%*^Z_MD} zE%)%fyAOBw9Q;pfxXgJq&iCu-LPgVsiVmug-ec7zV_*gNtsN4@XAck9ZKp$w+bH>N zwo8XwCho^p7t0?2Ob=j$SwKXXpMJWKwbt=)cpDT0D0v*-S?hS2!lPUSfO4kmtDxh6 z0wV*u-nSDo;}L*B0-!xi!>g9=0$V|ewWn20HUw$amuBun016#|;dZF2UQA-faeN6& z!RR4zz3Y>yV&w)f8gwW2m9_FRlslR!EZpoL;raU3QR*8fu3>5JNVEp*paqB(no)q| z!JDtAbnV;8Y4<{@>G+bwXA;2;N(EQ26OJ!QuR62*7dm_8{`YgUbLzQ#@mwZ)djBE4 zf6Q3GOkS>}jA#xd2P6Rw@7-YX$clgb(8nj)vDf$1(CGX^<3nVup$J@6rJT9MV$2cS zR-8@H%^r2zkN+4M`gt(1|MQeJOaVB1!0t9{#gOjqZr9R literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/record.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/record.png new file mode 100644 index 0000000000000000000000000000000000000000..27d14026482cc79b7726c2262233b4c588776de3 GIT binary patch literal 389 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmSQK*5Dp-y;YjHK@;M7UB8wRq z_>O=u<5X=vX`rBFiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa}?Or9=| zAsQ1)C+_t-Y#`FQ->PVphr*J+-pwTkrJ2{eMn+WdRxqeZ{FpdHGGbx0ap>G#2B{9A zcS}~yIUTS(H>zlf^4;wD#kHk8)+LSE2`p0@l@lc1NqIENOc3j9PP4XTSGgQ@uSYO_ ztwr`z+tW{M`o8<#^^16{cX?Kg!2)sD-sR8l6&ud}xH*oQNBM!Ahy1Tvy~$4sLWPzZ zM16iG7Rvcx#qv&6LzwKamd{1<@jB z_Sy^muR8A=c;&5dPLix}Qmvtz*tJuKqAJbYdanK6cxCwcd-gfcIcgaTlYF?hQAxvXnd_obO>Snp!^^*4gyKS&`XSPMV^ zF@jwHvh+6tClH572uOyA@Q6vXGjK5d`^WI_@2`LV6!;W$<`hih{KE2)G&PhM{&If-I_3}9{C_~(*cdq(zOlSx zc+7f{!HzA2Vc+^23^nsQ`~Lp^T>=n5Oh8*zw54=5&P|(c!fW!2;X3ybhVQH&fnNN{ z@RQ{m!*8}93_n@FGCcbHnBmytvkb5PJ!4Q5Rb*iO&nm<4`wu%n002P%zW)IZXAY^C zU6@wP6TAQg`vd?s_C^4?$iDLQ_4Mup0|f&5`TPGK4IUa^R#pY-0^;UsM0)7}t2ora29Fuim}H%fRvl6d`YbUXkTvk<9N; zYvdI&{=^`|pwI9$?;Sfp0I`76023oO%YWucuaA?5_(S;<43~k9(Y(&daPr)_V*mlf$PmuJ%*eztb!z<7bU8CVhMAAL z81y+U8RR&X8Gir&15Itfq|U;`@cjP+22NHs2K(>f3}rP_-X1!4#0MaNfG%M9_x~@` z=T9F$GyMF*&G7N_FNPn#|1$jk^$+NypFqt(?Tr5znEo>}DE={F_^yfDN z!^?LcUO#zscgLTt#}$A1Pks2-rV00IbN04Q_)0Y=5ApP-mfWMvnvWnkpi z{{82dFT?jQz-R literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/stepinto.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/stepinto.png new file mode 100644 index 0000000000000000000000000000000000000000..bbcb749eb058dd24249abffb39d48d4c02722c47 GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1TMd%8G= zSoB_S zzwzOF5rgI;hVHA%?@m>(-&w((Yl*oq^>Lte=(2ssl~O$Gd4(@&seZ>Nh9k%U*pSX{@jo6D0sg?WmC7QYlfVy z?+1;NFI9? zHt%w?N`=V}c`fg{L{GNO63L6+%_-h=>)4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1ULJzX3_ zEP9_#u;puV5NO@6s#YOk(f5(9n)PLj)wvD5TfHrMozI&3=p1v?p8l^e50+44$rj JF6*2UngA*rT@3&L literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/stop.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerObjectsPlugin/images/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..fa53723b0a2e60ac83c301389c147a79321e55ae GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1SvJzX3_ zEP9g@BtA6=&MZz9JzSr%&riZdZn_0C5C|{TlQ2!V(vZl+;IoQN + + + + + + Debugger: Memory Regions + + + + + +

Debugger: Memory Regions

+ + + + + + + +
+ +

Regions refer to ranges of allocated or reserved memory reported by the target. The precise + meaning of these regions may vary depending on the nature of the target. For user-mode + applications, this is generally pages of memory allocated for image sections, the stack, the + heap, etc. The regions manager allows the user to rename pages and modify the recorded + permissions. Note that such modifications do not affect the target; but only the + recording.

+ +

Table Columns

+ +

The table has the following columns:

+ +
    +
  • Name - the name given to the region by the target. This field is user modifiable.
  • + +
  • Lifespan - the creation and destruction time of the region.
  • + +
  • Start - the minimum address of the region. Double-clicking this field navigates to the + address.
  • + +
  • End - the maximum address of the region. Double-clicking this field navigates to the + address.
  • + +
  • Length - the length of the region in bytes.
  • + +
  • Read,Write,Execute,Volatile - various flags of the region. These flags only affect local + analysis. Toggling these do not affect the target.
  • +
+ +

Actions

+ +

Other than modifications enabled by the table, the Regions window provides the following + actions:

+ +

Select Addresses

+ +

This action is available when at least one region is selected. It selects all addresses in + the dynamic listing contained by the selected regions.

+ +

Select Rows

+ +

This action is available when the dynamic listing's cursor is at a valid location. It + selects the region containing that cursor. If the dynamic listing has a selection, it selects + all regions intersecting that selection.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegionsPlugin/images/DebuggerRegionsPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegionsPlugin/images/DebuggerRegionsPlugin.png new file mode 100644 index 0000000000000000000000000000000000000000..d6340e82531bbe1fb55f0b31323b5540f4b62984 GIT binary patch literal 19430 zcmeIabzD?!*EWobf&wB6QWA=ENq311(%lFM(%mq`2ntAxbb~YqBS_bXbhmUfbTh<| z-v-pn>wfO{`RD!qeA)bTHnV5%^IYeOV;yUq<1a5Oj&+CV4hjkimc+{!iYO>Iyn)w~ z=(m7>P4a7PP*9}uC0>XqyXdSZqQ&4g?(g3e#5>i!roiH5qX5;0UTc-Wy(ceHhY}rx z8b&T*7xLu#r?hlRv}kgk#Ya(?Qbw&+U*AVQ4NS6@v#rX?%FHqb8&{SYFM1>|Hp>p? z(B-=2f)~~cHeBD6qkCWdrhHYX2gxks@I3hS2Xb`DL?RZmgesi1$r?UjU>84cm4ga! z_}vlgSEs=Fhg2MQcx0GPYtn>R$RkZq#dbHOvL&{^F%N1+n7=b>zEOQv=?e)~Kt}s= zP1;baF)_k&PmZUUCf$|lAx)LrWLVZU@Y(e;j%v29U}yH~;{xCDITbr|mrViZo%!C6 z&fbzbSje49J!wmX)}F~kL+LBB zl}8fWA}L;-3^hXX%Ci+6L)3Cr?G^GX16Mc}z!@MJd=B$YdxbGEMywK;?P5Idu49hU zkk|wmR;2)~72Jb4Zd<;)$y95f<>k9c#H4IG{4yC7JYhQiCM$NdSY9$PZ(Kj*4Dl`lmXz(dYf}B{)Iv#R?&U3qC3bj-`Nfx_?w~vD2zseY% zyuiZQkp3`Sz-s#`@I&ilEW4p!+k6gDWg&wFWj5!Wju^Q9f<^2E z-8WBr7Cy!LN6wz{4>Ioa3^gXW5-n^h#L}~DI%8pIcj%U@!JYkY2C_3uW);cTf2Jgg zs{H0M;u#6NU>f>5`T~&?e#j?AIneWr=>%o zPJ1SWmBsLy{@g?S_+pD-)sgDXL`Kb0GL@0a`o_}44U3_o)tI)B2_`3yDbGezfdG@! zrXx;<;nZ!e{Wa&pgSS!PbjNoqhaoMAH~dNx6nR`48dI12T0k`8UO4an4~GLe+Y@}( z`I{I7BLDTM`^xIQQ)35iX`-ckX-jd~LJ|7(&$S~bb?86-b@0}qcO?mo(R;s(AkHOVNuzKU*;8eD#-Rjh#Oeoz3R4d*jz$rY`1-L^K zx@v*8hM4(X$KBVc|NS5_L#399dm1#gHB&DTG+k`k&%ZT+3So#}Ab8N1D~9AY;rX~4}uZKbxB!cnJRHNsMGKSi7`C=5qg^>rKatm%}~n}HMT zx-*YM;|fM9C@A=Xy)Ff}(;nXIjT5|ROMbIt;88*#`R+BsM%)ul@6clKME#a+(vUkDNWOw7 zhHLW$5vPUoC)esn>=Ym9FK?Yhbr1BPM~|WR8;Wf0$Cv}?Av!qp9TSM)LeDzGRqVhW z6F(MK@1Ufa{`UTQYl@anSjkU#rjqHsM>A44tJ#t^sMb*$(TggGxoyxAg_4E2tj5z? zT1;Ml#eHy?T-MZ!SoV9dwl)$!kk$<9&78y&v2yE6zu^FHv#bQ~-IYmlCvQy&R+Aym z^EfVXm~C+E^{a6+$kA_nIf(uMx$#Q}erbX< z1&t6n=r|=tIB0q+BU+iFC9Y(-=rE7QF3;_#t+Ptmtw%Q0i9i?hv6n~)B11k`hJygZ zyR~YQK_9WYwq~k)t|@MsgX8ig;VRpZW6FMi5iTNtW;Y|Tr%iLS-rXu`E z>n#)C<r zF`hM|rBC~5gh<@Jw5e}=y^tNkm&L5~EvIpL%zv}jn}o$-Me(}IdRb>0NfQ}GOALos z&=lw+C9$BGU9FHBYDqwFP2wBO%pA~1Xmuo$fYK`>&GN0MwvO^F-tKr?bD_pLQljmV z@rBFHFc0 z^Y2+)yw;;35`o{D4kBd3grb@e^4O)zQE*Qm*gLSbqIXS@Qdc*foS&*RfWG!d{1`BJ z&2hB3r0sBM@km8-huDrUh$1Lu=cznb)HycKRI`$(hC9br$q-1P^;5s;VwcoFcCXza z`p*4MH0-m7CwoIvh=Zx8XLO*g$r?1oPpvs%-4_Bgj8Q8Xm~W~Ck;gyl>L(IGl8Qv> zqtdIyiRY;$rvj#@Al(t1?#cA-()kTs1Id&9+m9FnpUFv0Sa2UXiDmKXkh3>fYRW$@ z)-!&@IKKCBp?@;VNa>D3y#z9Q<8XYyRfjUJ#vZ-oEdP2W;mH$^LF33vqwJ{7ECQ~BZ5HJmr ztKpF7q3-?LMLT5T3cTv)!(X$sa(GF1k7O+$kwNlRryNN_mBL;8qN8M_(Zns^J&tAgl{u?khBCc0cDZ zMMX+Z_hnG$*e_!JIN4h%(HN&tFI{7J{iXKA?ry%Oqb6Vo;-HENigMeu12)1sn;pg&%BSW)ctob;GIHmGwDnre9+>M=&DFihbM9C zqYSx{^{m?u)Vtcf37$GX-+OBJy{%;aA}W9n5l`bnd?vzaq)`iXAhjeU6rrd$##P#y zLZ_-bKq+!RRcVW7@h&lE{`?LNnk|R5K4KqUuC~>c_=4AlJcNi-JCg{IDycY|F==Kz z*Jf{p{cS|QoE8nsHuEcrmiFga(mXUt1Z~oZV0i)-NU+}S0zEA+ug*A^%Y2JUu9`K@ z)imz@k7@WO;V5^cXaKWF07n#164r4XL(z2$_rK%i3hqy_n>Xvlm9FE*6=#si43BfQ zVsd6G5Q7!pnUeLxDvdt-XyF6$55hZL4V&`T36B1rx=fK)Wk>P?QpCSc`B76e& zx?hw~JxmQ-tx}qxHKaL^>+V-ib?N1f_)F3@~A-VE(5o`hA`v z1bT+6{>|1;T<7cC|HIwi<6yE~h2j5(4C*nVpnJbk4B?f=dh|9coIj`6|Gf1BxXflL zTjJMSD2h0qgd(maw7-*+o(5}*a-{Ng6cH~p9ew>LDA(Q^>@@?WA+?4ZO9fIDt_w3@)dJJBAUcgvwsJaS##NdkO~>R-R7VWP`b2WNO3@AnSrLS!_G-lS$F zfdeKX=N@q!=21ikoIgA6e{m9So3mutcKhO|-G%A(6RoSsgrU{dDdpgN1%7SuHG33p zlIM}>(NbC{5aK&Y;a{FZbt~-PpEIPQbCUMbKFo^Ll4SxFHd zk#yKwQHktL6IY5aY!_&AeMeniVZX>f%|fx*nl2Ns`bc@+{a%s_v6RIL5xa@7+x}WA zg?LC0V!1!Zr1!Jgcyi$acAXhoI{#$TC%nxjHJmy}B~LYi!(7#NAxAMo(qXCRaS7AGrqDSOJ3}rxNylxSgNWbd zt5%hO+kU#5j;mOLLY_+ALlQnG>0qeFH(x>I{DZa#+VJvRuQp&-kTWA!sZb6gR9p@MWFXhQmlb5DF^;IpHtu1)Gs= zlv%FC#mPB#Zml9v-Joh%F)qET^te-cx^YL02&GgMf(D-k>|x&f`Q~80)(}N9)*iOd z3AxVh$pXT@TY!3>47R{uA!`S$>b}NV^DeHOv+orH2Fn^N zm0PHI1?tQ>?pS3-K6C@AR)Mm8(fMe-2FuB~h7pMNbFdoYv1uA9!Yt_^r?dFg0Q|rp zq6#sj{6&!>)brZvv`4Mw7$-KBbc}E$Ul4+Zxjua6aC?q)i~Hm5yn6*^vId=EI(q)A zswhA?#qj8q?}bbH?=}z$x|egm-0{;!?!YHTXDi*e^bZ}s8FdbDGuy|o8r~t{wh<<{ ze#WX+VP{x&1ej;0sO}Z|UaC8sF+`+XzZ40Ny4kqDKt!7fn#8k=GR5^jOFD|x6%Q5C1#1hd= z9%XQ$;T{H1>e5UPiuU&QA{OGU`vUOj`P*&==uA1S4U2U1?*<)qrOt1gca2rpXT9Vy z?R!xjeS%DN_jNx%Js`ED#D@jz4dE45Zgs(7`9>t^LEXj~rS_o}@YPf!GnCqvyc21mIflud`@9^nUo~Xt&!Hy}lvS6ifuUlLV<&7X*@Ty5Kh31} zd>PZC`VYVZO4F_1AISPnIRl7^H|D4u4cMdR`J09oW1uWij9Q$w zn~|Ruwy8oDkmra&K=dF335;aK!h>YV@hicvjF!-Q94%+fazM^@oSDzsO?7P3@|#bi zgp_VHYQg4DGg2CmIyI%1V~WT7>p40#VA8#JUS+l0&XGU!wen0oJ8x-9?4Cpki3Zi? z#Wh+}9L~jYzj6_$gGvpp3va4ft%Xgw4BTl$P9E#iW)XnWzOrI zZX0mK{%o?YpW99ghvi#D=8k2a(~6I6@MluI7wf9xB5j%=IrUlx04`ok0i`2*~Fc1>D$$3WH;bFV^=9?4`gHXQd5nA^X5~OM(oT)iwpZ?fjZ!Y^~3&d>(PjW@x(e z0F7;u z_6fJanLZf~&f}z>bj!faylIY6X#mkQ23JWYVCPn6CI&N-y>YF0r=VL6zqY!?8Ra5y z3b0%a8ap*^2LZ>_hr-YX*HeK|Ld(SY>M8L0+n$Co^**;LkH!*L_sz(C?zC|j<*P}3qHwH_^>v~5$1|4tY~D0LFsXaveJHj+k4w!5t2*JH!yj>4(s9-4aI z!^lDgWRxaMo;LMb^mZ$|=_O(I70eaDPU*00U<@*Z(gM-N|TDHtBgBH19 z(QTIMZMzH+<>P3&ri8HIloKtRJ11l&{d1+d8IUvTP%Y105w>NYHK;=6`2mfg=VN*m z^NArwrn1)&q-E;?+2{HpL(7E-Ir$LCVrJ%z%!){~pFdy}OVB$W+O27Va=wB1%;4{v z--13t8PoFZ&rV!dCaTKHd!v)x_cy9G@6=~CAnC~E%yg)>e`;CxTEk8keyS!5dNjo1 z>F{b+Ia%`BF9Z=S8Wy2>E?L8F$_uQrXNaG{Qbx7bGWJ-s1Vr@#INLp90qwq>yhx)9$Zn6P* zPAtAzSI>MP`*Bps?jqs&x%(rfS#UG0uH|)Oy$gryIZDY~KR;yD904s3D)VGZ8n;wPM)omG64^vI4B zoiTwmN_goaw-J*JrS!Z{L;ReF>^&*1KvcDtXKZth$L<(<*9e5&LMZ=S)}Mol;(cAt_xND*f610wh<|5&Sy?#&l?4q&;{JiJRD<%1yDd4Lx;dFZ9-EK!xk*N2;aT+x^o;wi~ zlr)X69ktrBj!K-h!Q52faf<@|@?*ju`petNiSD!b&CXi)B)T0qnogxs7^6HtM{x z5r&O|Qun4|$v=X{fNG-q8ofTs+ny}5Z=?;gW7|eNmR3Np~z`H>{ zux+ffoa(;WbDU&2kcn3)oHJ04(WqU!5t&NS9I)OI4|^Ne>^Se_Ei-i{O7nQ8l(sx% zT=uK&%9;M!sIdr}w;gG{F&PFf!z|9ZVG9kI+L<(f*G2I=6b<`)kj2*esk2*Orpzj5 zYiA}Q@?1KZzoY#H{DR z{g(VhgWvdWt<^7iJPV+WA(6Us!b5)0*hYYo8X~mYhK@Y;gL3A7xGC9aJ=Pxa9Ue)BQ(?gwiKYnKzKqk6%y~87%1PpX9+5 zC{FyHXDK)ylaTc&hy{y?BaNtJ#eZm&#Xz}xgWxN6+Cd#n(^F#xS^^U2mk-zOtq^qP zO`ys4te&@tEgD$&nnPia`|LbwkgqSbiB>C($8y+NQa;^9qEM!u!YI%oa&#&H(X-7q zz>0afL?l&+&-VDU(sdVF7OZrDZ!jeItpUA2y-I~4b>6WDY^#%Gha61?7AQ+9x{JlO z0_J5Esy%5js-T`3h^-K@HZuz9@o7pCX*zaews%)b_n6~Q# zz{EoZkFqc3X3{JZTLd3(3RVbe7LHofO?hAp4JdZ%AoxeoD4qYA-eH`Y<)||rwab{( z(t<*CZ#>!6YALG3^%Sl5dVg@uTiQk}=Lq*=oAzejh}d54h~R^~<|z1nF&(VUT_qNL znVEHuJ5;h0x z5XV(6JNiI+?=Erc@k4vg{hDfIW6gI-X+GJYl@XE~`>zp#1dX5gq7ZHE>Npoh%>j+j za&ubq-_LGe>cmdFw2^HnQBT6&u}bcsHC+^!r^@ckV7?#)RaGNl0Y%vZ3(eAvm(8*X z95*jl<%Oih9dW&!NxxuW zrce|Tg0B${)eoC9b_&oU9 zJ@$Rd{p7pQE&}pPi969yu|X}w=N3ma)8&GGUmHTE_REk`-^6G-{ZU~yS7mJwRkR#4 z`Z72-C{Lc(Sg_^SKL(n;@ z{@DzA+Q%~wDX9dXy>GYCC@}C_TOkfg!HVOuCN=DgQ#oOHZrAtGUb#?TY^68NF9A#B z9mx}CJobGHo$=v`KgVD>Dm$e|qh(I3;}!OZ4z})|z11Q3 zy?JtgPt+YKGL+iRh&CO}jpVfo(*FYRfJ(ie#V&cq3!R;UPOk_8`DE(`uDk{9t%hLEE=u7Ly@KMxAk#3rEP^#T?+3;OYp;yS4BOFd(&|uGo-#WAcp{ zruXw#XC3quty4nkm%&6C4`wMRuCHJ72_}zInD`(|Jl9pb*^H+|_cwg0b6orQ$^(ds zMG`~+IjEJS>Vtuw2~eHcKY9}!#Mxzyfx zO3=q{@BQ_$XdU-$vBHHi)qG7WsbwIs69!5p3jMaWwzE6sO0#v7q!j|+Os!pW6fOmB z^t3D=50KNuZ{-JyA=88-fCXgou>dT6lgV7D`Q=~ z?fbI5?^kG7HZ*6?!aA0{9e~SBTWadkU=dcJc&rsh%IoQ?={s|2J}bi+_=#2bjQ>pV z*{e#2SqujtiFymugY0&4nI%MLjl&?iGP~vla}h3I%fzj&LQX_=V(orDVXLM2V1^Vv zz`5UV_r$=Z5hior@_Oc8<-GZjCI9SXR}PHm6`#168lP2LB>f_%m3yO$)vg;E8JQdw z5&ON}iDa--6Z!EQgdjk(0(k7t+@P+5CFW|j-ep>4R%S`6;NALc8G3!MT;)irVhguo za{WmvT z)W2b*%1PcWcH>jeNVC280K;hAL3fwI{O%8}R<^#Y>Wo?Yd4{VY4r>h>07B+_qc+9B z;@ud4_rOR32j;m^u{gjE_2I1Cq|cZux;{bbdLAQl0bV|p^$*o@P*hmv-@5yVCTR)a zka+1{RTdu#xnP|~&?>!x}H{PL0xO>|J?_dBz{^g|JI8 z6D296>t97#m7eTLRaL|^L^rEpI$+29{|wE#B=KV zll2$8;sZj+zYW7{Ln}W>|15y)TDS2Y(st{keIu-Y;IkM;;kYky#*gep5|jWUF>UyR zzuUFi1BfasDkT4*M}!w?$%Pu3=xj@p!D(wBP)XZp4c6~I2Zbrbt2SI8bTiBi$TP(# zB?TgPe3b)UMQjQX5Oe*|pi*qcvHEv#{tZkb>?$EbVV^^jGu9QvO;!s;uK?AT zhew7wPJ39Od%xR(X*%Z1aDvX~tgp}w3Q7(Nfyl4d=s+$13iY7ds#@~Bka5FIt6V?2 zd>%lMP_2BwO~hr(8%!Wj$6U_vS4x2StCs)vUY2dN2>Vn0yN!)gDSQVTt9`Yi;%z`h zS|$6?hvpLCb;cBin{M|-Mtrmhy#IiV;D_PAZp9!LG#x=`znl^MEg<>!M}wS1J*%qm zs1f#O-C1yY#benCfgO=07JbBvwiuQN3pdaS=$k%WEFua@s@$NFz)}a2yAnJX#e~nK zo6!-=7HIJOON%Q2rY`QQ~%zfzd42PE+QD@YGW z#+Rmh?aefhYwaNdy$B5-=+bf)o02akVa~X*Zcibbt=TD%faN$=G0j>SU8LlmH~jJn zPj24n|H2bE$PQyt&(5~A)m1N9x6?9G7#*s0x_&ty&H2*s_MjFY<0E z30@Me5l8_QK2G?FUIWTYMgkJHc2Di3TX#tqb+8HeKM9`AvxOdkcYj>1T&=A6Xd)s} zDYT+}NGXopl*ZcYxfN1d+=_bGVRa;$sksS2p#N}`Min>-oaVLV-ulXCLe@2+FlHSY z{8_91WsISmGv_I#&e6R=m#l)C?4@I}zJk-{NY|2lV-p^|u~{@QxuhGW&@VYFF{(b3?l7t&h$iYEdW zU`ha({M_td72*Vgxo?34jb9%$Co)iD=a=NNBf-&WZ`@8f=q9pnBcAu~W9qqmXvNRPcElG!yHA(;PU z*58p`@o$&~559m|j(NXe)&&n4mCx5#_`q1^UsT-y`cB*;B|R6=+XFvGcB`4r$Ds4t z?$b3+yu>|tZPGsPzUK&4YjmFwHF6v$cCxte@Wo}-5id66><(b00CnfG{1AqX5mk_Z zEfw|f91S`{#^F>ncFKpjfrE*4Sog9T)nul~k(32w&0n3sK`fIbD6YL1T;b54kEz=j z2Hm3H5+Jkp)Gf$_AB%VcY?%JyA611K7{PNn8rMnB^HtmbS~fAdfegaD{cLHC(sw#- zQKA-ORmi~dM%+ts+jR6ZqZnBz%fFZ~Rtj$Cx;u38Uq6V~>bTZwBthmvq9?a1Ej?WV zN-?LR>ke9NrEBFvtdBUrx#@#|i4s(mzOrh+;F%iNN70sE(HJ!;zI;-Uy-L198uEIA1nUO$bB1to#N_HU>1be#(SPZz7sR-~h zzd13z0N&MVnYra=C3>qYK>V+eQrkA69v(O^pd}Hojq1`3IKW`if_kk+k>uH2)iR<8 zMeY%Cuh-aXLf#xM3LKIG6}bzrL{3SoUHeeQx;OC25J`GQl`Qce7=uMzmhL4A^3PK( zRD1BBMI?Z8SVAyweSbh*Yc0F>;Nn(q`w0bYN#XQb!ILzqe{fa55i^cjUhd$R#eWyH z9^(G0Xra91IV5e#7G)`Z-Hf9NTmSf2+xMy(_714^71Y+DU%$9J3W{*dOc~BU8^jd- z^iUuF-Yxb+`VX0fJ3Cr>@Qh=XCupT~_41*YpmTUToLx}=59GT@|Na4!&$VSL?|y!a zV^70$c59--SvPv0gjaEYZ6xP;Bl8P@rYcDoh3YoTWHw5vDL((fC@Jm!7xcwt@djE7 z)4ph206lc^n{LN&Ac=W=uJVV)@=1B}_;QTyL;-X&@F zR)m7)O!E^@ja&BxQoR^9BMnF%xz5Ft56{U;Qk>(lUr6ta=gM0a-aD9rD2VUJ%w@)$j4%i-Z3VN@LCqkq^Ts2A-3~T~(<4V7RGybRC*h0atAE%i zN;Syo9)5|%h@yL|k!FcmMu-x<{zs2b&0&KE5|?=fIn6-ixIw0qRXY$h0d36vZ)LSh zuB7bz9#Dp~yh@q0dV*5Io={Na03BUsyq2TIBE*O<7j2SB{H{v?4|m;Dz-di=Y_|5z z-hTUftjss}8b2qA4e^xY$vcBNN`Agr#8k$jcU1!)GLA2h2QG?yWp`MTZ;NIoOBD3b zB*_ArD2o|xWh8tdl?u2BU;w99a0Tw7F0&(*e+p^JFDw?X3TYQ&R)Y%Opg9|r`ITV< zWbe&8xGB4f-GM_pu_`}-Aua=*GWVl>*o-<}>bh^=d{tp5mfvkG2i{u|{e3h5q+pFSPqEY%K4TJcqA7MtKi(kg+v zd>`eS4;A=VG^*Ka>^$lT2?Y9I`l*xMpUd%YqejvwJa?C;pXE^la%S6G2d4*9SwM?I zZlU*0jJ_Rs`yiq(Lkg&~(%s4k`U}{R)0DI<#c0&JyArV(1-S^A4bU8|ed`{u3qUOR zTnPfnn{Rs1YhT&%sy3-FT|(3q1J|y7@rwQV`HlHiNHEtgio}lM@OTr3G50xA`z+CU zGL^&pXHO!Z^HNhVF#;Hm(&^FO;2|^YF zRLmgseWK0X^6k&AxXOR(c$Zuy0ON*jYqn?u&4$JpWlsB{0*@r?OYeAsxYD)3%vb~a6bzdz3A2pX9? z`Tp+4M3tCy9>8FPU$lh+^N+1`-XyI#oDFo?nik=C!%eF0yg8WxsEDG>E*=sCpXtMp zyWQI{m2{FUfbz2^&Qfh4KQGhxa@o`y^f4Cc!jN6Q3 zq>LY8$^uA%hT1#0ryn0$@(L3scS%?5;;YWxxth8qpF`S3o$nV-3m_s^K?oCUbG#y4 zGKzsVO296tSN4vMX1%E|^*!C^KPOjHoShmw@M?tTCf^KQ8ZrZtj1FhTX6uZb?`h1g(KRkAfbz zdEK1t>fKU<~ES4BGuV1`MchRmm?o-^kp=S)o|o4m{#jiB49Eayz&r(2x5ef$?cHWX1=-!Fp`Fk65Ry6I-@7ijd#snFo+X==)PbHzx}X z&^*qNi)IPdCmi6S3$*8^?qmfUPr5|5AIGu39qY)N8{$eyOQ!>k=t@8rW#dkdR5X+S zkjD{10RVitT4e<;EBNyk+X=JS+SYx<*cbF_OjR|16(DIV!$V_fA}%Wi*?}!W!`dum zoKQ3!#SUr<0?#K2`?qcD09L!L>#t=tbm$nba%QeUF2v7%OFFmHjQXv^ZT~`iQ@;`4 zpq&})pQ__Ujl?Jxhe4;ByE9oxx9j1jc4nPw^#n`!alxT;h|4FF_AMngp3u$6xnl;G zzAaI8-0cy~vlJE2@C=}uVkH!+=Lop3sKme4zJ9GXx?hv9@dQukG!i2I5%yiA>tXVT zST^I3gnUVB{w+K`o&EOqVz_Gy^=qKXpS*S&wm)jl?s4S6(PSS1QGFYI@a*Inbr*p6 zM(FvPKRv18#QR0xvxKW1NhfeCR3|-q?LC%DnuO1K8O|PNPtq%?Xv=7Q-HXw+85z$I zkb7}c3y5B)n{j=;4~&;jGF$*OxJOiOMOmZ>(A%sO=sr;iJEXiBWRnd->vjLv^qyaJ zyToR4_|f&tx*smxkz*Twn_4MRjlvgip!MepCrvFd;i&cgMtqkADgf*Ksuaq%EuWpf z2oUs)DitS51(IUIOT1aAxfJa>GRXC(3hABlEPM(i?}~MW{ZPn@y?)m_-#)wX*N&V2 z!ug?)cf9?77yo8J|DQBaQ2ybA|NoZ;unp*!j-n`H3kH+_I(a*A`>(D)QqV`X-&dtu z@duF`|2})^Vd9bOhZDg}@+-|jLAQJ}h4wo+LdkhFa_~{VpB_zk1AObUnkOWjp$|3H(=`UcHIz@#d$?u3ih zdYUtr&kW5mvDj~)Oc1UxrMtfoni-bXB=2{Z#AGyRgw!@q0relt&y9qEzHY~m;5yIU zeO2WcIvXpbtu3Rs(H9nYd%&#3wl$4yx_Baq_! z_*6~N>wc$Dr|TlX*33U$460{Db?0|SC2u=#^#5c#Fm;oDVks2=a;)oV-$Fm6?`ZD8 zWMoFmfjP`X0YS3DD&$KP^33`c+6k+C3=DupIf#|Pc+Zo0Ft)*UBa+;zBbith# z3Q|{EtUr(ONd`8#>S7K{a#^2CRCNoaWZU~oKpvQ8J$nb~^L=n&(l|3F0or07_1L~< z3A0HCGxJDUHg9a1G^2Am8Z^tu%jjiwC7L#Uc^Sv!7NYjka(Lz9QLKijBh#%W2DLg_ zPyfkZJ$HuCRi#`0umsbP2`JF`!la+K79Hc{h8sAzT7nDC5JV}!WW3pDt}zJ zQ_jfs(z-Mfio1^lMdHggKI2e`D^1l3DK*UJ6mL$mj6#;m+mcSjP4-4*ip{%4!USFB z)5ZC`Qd)|EU8o3PN93FsI@oEol63{ zZhxsDuIElO`Uba6oFJh`fTy~h&q_zRv>)@S*c4Ul_fmRjpL3XP@!4>4@g72yt^8=1 zM3d0NuVAWD4t^A)Ra)UXW+nR5a$FKX+BoeXKBu*p?W$k4p9M~Stcm8@%xqDlpn<8H zjMYBCqtlL1{W2*$=CqN~EeL%%HITEZn)N29Pl8WB{xzS`A2FqDg)>udTMe4#3Q$LB zGKHjp`6&cBYDvZ=MVy*I#=EKegw!Xms2rj}4|Kn`9$MeuFA>+M96k)ir*P z0Zd`<_ECDVE3q*g1b3>QX-8!ZkR}!(wCor%_etQ<&1h^_&07wRckYv7u31wx9vqdi z0``Wm-1ti&-3jY7zcDNN8LH*94PX@0@_OB+j{Qy{v87VybG#v6vW%b8>8+m2dRDFe zq)qKs*;AZoo8-2AR%28@EHcxV=htB6gmuZsdq6E}2wZINo_Q+$N+0wmb?DQ9bO1;# zsb9u(=BuBzLP{L(ip9I(<}v@FTxd?_+tEWb>^|knRtg6j>t@CoboVwb3HjU}cR7*E zCER`nvr|0Vq8u_g7(puzhiE?We_!PfBs7eq>x6|KLxQ5E8^fZae!=&swxYe>HPpPX ztCf=vgM;KZNnwyPHtKxsZ@m<{P|u4UyT85>=tA%<7E{gvf@`+#YZL9faxwi=Z{VMO zCYocGuaAw+-p{r#ULH=yjeK+6nX>Q{@B~t<-p-rnVq{Lw4hG~2J?WOb#iSZ*yd)=m zJNX*k=^q+Xczf6eDZHt`=}6#VW7I6_%eU~8eU)Pq_W6mB+2-pw5+q+F4KmEiBaNT! z`|s{4`NLM=YZf9_ihODb!M<(S+@g{A6B&0Xx)uGCyA>OGoR`{toNwwfylgusD?4`= z7Sqb#HEI6ffff?r2cwaWXOUt!pU-=1{?k%%7rBzM6W959{KE49S>?OeNo^nN7(1`2M6ebU@X^9MCk7FclW>OQeF zzj^cG*~eIF@~N9@ijkzmCJokZ{G%-O + + + + + + Debugger: Registers + + + + + +

Debugger: Registers

+ + + + + + + +
+ +

Registers refer to the target processor's register banks. In multi-threaded environments, it + is assumed that each thread has its own register context. The register window presents a subset + of registers for the current thread understood by both the target and Ghidra's language model + for the target processor. It permits for the selection, organization, display, search, + modification, and analysis of the registers and values.

+ +

Table Columns

+ +

The table displays information about registers, including their values and types. It has the + following columns

+ +
    +
  • Favorite - a toggle to mark the register as a favorite. By default this includes the + program counter and stack pointer. Favorites are sorted to the top, by default. The list of + favorite registers is memorized per compiler specification.
  • + +
  • Number - the index of the register in Ghidra's language model. By default, this is the + second sort column.
  • + +
  • Name - the name of the register in Ghidra's language model.
  • + +
  • Value - the value of the register as recorded in the trace. When the value refers to a + valid memory offset, right-clicking the row allows the user to navigate to that offset in a + selected memory space. This field is user modifiable when the target is alive, the trace is + "at the present," and the Enable Edits toggle is on. Changes to the register's value + are sent to the target. Values changed by the last event are displayed in red.
  • + +
  • Type - the type of the register as marked up in the trace. There is generally no default + here. Either the user or some automation, e.g., analysis, may set this value. Changes to this + field do not affect the target. The selected type is saved to the trace "from this + time on."
  • + +
  • Representation - the value of the register as interpreted by its data type. If the value + is an address, double-clicking this field will navigate to it.
  • +
+ +

Actions

+ +

The register window provides the following actions:

+ +

Select Registers

+ +

This displays a dialog for selecting which registers to display in the table.

+ + + + + + + +
+ +

The dialog provides more information about each register, potentially displays a larger set + of registers, and permits the precise selection of registers to include in the window. This + varies from using the table filter in that the register window will not query the target for a + register unless it is selected. Note that de-selecting a register does not prohibit other + components from reading that register. For example, the program counter and stack pointer are + read by the recorder whether or not they're displayed in the table. The actions allow for the + addition and subtraction of selections from the register set. Most columns are self-explanator + or duplicate the same column in the main window. The "Known" column indicates whether Ghidra + was able to find the same register on the target. Unknown registers are never populated by the + recorder, but they can still be populated by the user. Modifying the values of unknown + registers cannot affect the target. Register sets are memorized per compiler specification.

+ +

Enable Edits

+ +

This toggle is a write protector for live registers. To modify live register values, this + toggle must be enabled, and the trace must be live and "at the present." Note that editing + recorded historical values is not permitted via the UI, regardless of this toggle, but can be + accomplished via scripts.

+ +

Snapshot Window

+ +

This button is analogous to the "snapshot" action of other Ghidra windows. It generates a + copy of this window. The copy will no longer follow the current thread, but it will follow the + current time.

+ +

Tool Options: Colors

+ +

The register window uses colors to hint about the state of registers and their values. They + can be configured in the tool's options. By default, changed registers are displayed in red, + and stale registers are displayed in dark grey. A "stale" register is one whose current value + is not known. The value displayed is the last recorded value or the default value 0. Simply, a + "changed" register is one whose value has just changed. For example, if a register is modified + as result of stepping, then that register is changed. However, given the possibility of + rewinding, changing thread focus, etc., "changed" is actually subtly more flexible. The + registers window remembers the user's last coordinates (time, thread, frame, etc.) as well as + the current coordinates. So, "changed" more precisely refers to a register whose value differs + between those two coordinates. This permits the user to switch focus between different + coordinates and quickly identify what is different, so long as those coordinates pertain to the + same processor language.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/images/DebuggerAvailableRegistersDialog.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/images/DebuggerAvailableRegistersDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..05f7946a504a3c4974a757c4aec5955dbf0f08d0 GIT binary patch literal 43243 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 F{vQBMaKHcn literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/images/select-registers.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/images/select-registers.png new file mode 100644 index 0000000000000000000000000000000000000000..19902fa065a350df3ade4093799402aa92c5b651 GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1ULJY5_^ zEP9VlQsirJ5MWN{>|6gqF(BdGLG}+76P5?;>G&`6&1qWdQPq&VS=(-h1YG#$e(9Zm z%yWgQ?HLQ%GIl4s&QQ#~ZmGBPaQGo9hD0W*37iooi)J!h;xkfyTdUx zANE)HoH*7g9+lwcJy~?&U8Pg-fzMr(Ob>Q=wPzcy8%&psN@>UHx3v IIVCg!0E>xUSpWb4 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStackPlugin/DebuggerStackPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStackPlugin/DebuggerStackPlugin.html new file mode 100644 index 0000000000..57cfe6ad08 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStackPlugin/DebuggerStackPlugin.html @@ -0,0 +1,52 @@ + + + + + + + Debugger: Stack + + + + + + + + + + + + + +
+ +

The stack window displays the current trace's execution stack, as unwound and reported by + the target. Not all debuggers will unwind the stack, in which case, this window displays a + synthetic innermost frame. Level 0 always refers to the innermost frame, and each incremental + level refers to the next caller in the chain — most of the time. The current frame + comprises one element of the tool's current "coordinates." Selecting a frame changes those + coordinates, potentially causing other windows to display different information. Namely, the Registers window + will show registers for the current frame, assuming they can be retrieved The Listings may also + navigate to the current frame's program counter.

+ +

Table Columns

+ +

The stack table has the following columns:

+ +
    +
  • Level - the level of the frame, counting 0-up starting at the innermost frame.
  • + +
  • PC - the address of the instruction to execute next, or upon return of the callee for + non-0 frames. Different platforms may have different subtleties in how they report PC. + Double-clicking this field will navigate to the address.
  • + +
  • Function - the name of the containing function, if Ghidra has the corresponding module + image imported and analyzed.
  • + +
  • Comment - a user-modifiable comment.
  • +
+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStackPlugin/images/DebuggerStackPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStackPlugin/images/DebuggerStackPlugin.png new file mode 100644 index 0000000000000000000000000000000000000000..cac68dd738d13f26c22291b554d77a6b9ff53c6e GIT binary patch literal 9994 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 MYjUGO`?vf52QVWhy8r+H literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html new file mode 100644 index 0000000000..2a6a776b16 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html @@ -0,0 +1,76 @@ + + + + + + + Debugger: Static Mappings + + + + + +

Debugger: Static Mappings

+ + + + + + + +
+ +

A static mapping refers to a range of addresses in the dynamic listing and its corresponding + range in the static listing. These mappings provide a flexible means of mapping imported + images, i.e., Ghidra Program Databases, into a trace. Typically, this table is populated by + automation, e.g., the Map + Modules bot, or by higher-level user actions, e.g., the Map Modules + action. This under-the-hood static mapping window dislays the mappings table, allowing users or + developers to diagnose image mapping issues and manually add mappings, regardless of reported + modules and/or sections. For most users, there is no reason to access this window.

+ +

Table Columns

+ +

The table has the following columns:

+ +
    +
  • Dynamic Address - the minimum address in the dynamic address range.
  • + +
  • Static Program - the Ghidra URL of the imported static image.
  • + +
  • Static Address - the minimum address in the static address range.
  • + +
  • Length - the number of bytes in each address range.
  • + +
  • Shift - the offset from static address to dynamic address.
  • + +
  • Lifespan - the span of time for which this mapping is applicable.
  • +
+ +

Actions

+ +

This window provides actions for finding, adding, and removing mappings. Note that entries + cannot be modified.

+ +

Select Rows

+ +

This action is available when the active listing's (dynamic or static) cursor is at a valid + location. It selects the mapping containing that cursor. If the active listing has a selection, + it selects all mappings intersecting that selection.

+ +

Add Mapping

+ +

This action is available when one primary listing (dynamic or static) has a selection, and + the other's cursor is at a valid location. It will add a mapping, using the selection's size + and minimum address, and the cursor as the corresponding minimum address. The lifespan given is + "from now on out", i.e., the current time to infinity.

+ +

Remove Mapping

+ +

This action is available when at least one mapping is selected. It removes those + mappings.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStaticMappingPlugin/images/DebuggerStaticMappingPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStaticMappingPlugin/images/DebuggerStaticMappingPlugin.png new file mode 100644 index 0000000000000000000000000000000000000000..d0df709e62ecac3de83a1829628a36901e9b1ffb GIT binary patch literal 16921 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 Xixl3xW_%=h@4CS)Q{B3o_oM$0GJZ${ literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/DebuggerTargetsPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/DebuggerTargetsPlugin.html new file mode 100644 index 0000000000..c118a3418c --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/DebuggerTargetsPlugin.html @@ -0,0 +1,72 @@ + + + + + + + Debugger: Targets + + + + + +

Debugger: Targets

+ + + + + + + +
+ +

The targets window manages connections to live debuggers. In most cases, a "connection" is a + connection to a local "agent" process which manages a native debugger, communicating + information about its targets. It is possible, however, to create a connection without starting + an agent, e.g., to re-connect to an existing agent, or a remote agent. Additionally, different + connectors may implement alternative protocols, permitting direct communication with a native + target. This window allows the user to establish new connections and disconnect from existing + sessions.

+ +

Actions

+ +

This window provides the following actions for managing connections.

+ +

Connect

+ +

Prompts the user to select a launcher and configure its parameters.

+ + + + + + + +
+ +

The top drop-down displays a list of pluggable connectors or launchers. A description and + the options for the currently-selected launcher are displayed below. Some launchers will start + new sessions; whereas, others may simply connect to existing sessions. Each connector defines + its own options, but common ones include port numbers, host names, the system paths of + debugging components, etc. The launchers should provide help in the form of tool-tips, accessed + by hovering over each option name. Selecting "connect" will launch the connector and dismiss + the dialog.

+ +

Disconnect

+ +

This action is available when a connection is selected. It destroys the connection. Note, + this does not necessarily destroy all processes created by the connector.

+ +

Disconnect All

+ +

This action is always available. It closes all debugger connections, no matter what tool + created them. This is a sort of panic and reset action.

+ +

Flush Caches

+ +

This maintenance command instructs the connector to flush its local caches. It should + rarely, if ever, be needed by the user.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/DebuggerConnectDialog.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/DebuggerConnectDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..0af6301ba21fe9abd1deb02324b1e1fad789c1b3 GIT binary patch literal 12021 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

Wxg;Tnwa8mi(aKJ z+2RR`yeH&2vGxy$MXxc3cq@v9GCCGV>4o_DS+}{cWb+kq<6LqP-%Eg%eo<^%L<1@t z#rb|&;rR9Z``^6sz&;dj2VXuF`TL`B3<)>D^t}n>WC8n}9Uw2QEL9@$>fQeW-RP>Z literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/DebuggerTargetsPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/DebuggerTargetsPlugin.png new file mode 100644 index 0000000000000000000000000000000000000000..a98e95cbb268c008e277fddf5570d5b51a8c4b0d GIT binary patch literal 8595 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 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/connect.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/connect.png new file mode 100644 index 0000000000000000000000000000000000000000..82da18ee0d0871a57180c84ae3b5fdfb9186ce16 GIT binary patch literal 525 zcmV+o0`mQdP)`F$Vq)y9}yjqZ{iM zLNK=H0wOfHACyO=YqUC*TUsbm2>izV0$;HO=(k#h>y~L;lo~&x*cl z`_P4JnCQU}dPS}5E%IVhQNwxM={sTt=M=3aA5)3?G-QT7!}Wm-IM@$EGQ<{%#0T~3 zec8w`KhTT{+xa9nXuwv>Wt8?XC}m?;Af`UJLF|!WJPz(Y95r!smBcBf(h2uN@UA=c z@qj^Q%kxg!X3WWpBNppN+&-{j7Ft8DcHv{F;z5rKusko9>ZpY6PqK-jeR8OFxOTQ+ zwfB&MbjMs5u4jHbE_Ng4HNIgz@Fs98a3Klr=;NHIR`zmvUT&Ys|4)AareSpY&&fv? P00000NkvXXu0mjfL!syo literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/disconnect.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTargetsPlugin/images/disconnect.png new file mode 100644 index 0000000000000000000000000000000000000000..7f78c170d9c78363d4e785432134abbcd41cbddb GIT binary patch literal 429 zcmV;e0aE^nP)-uLXAh0{YzTx$5aP*@39S=HLTAJTJ_z97<;HpYsH?lqufO9;_TJ0J> zz*AbNci%wuDg^Nz#1OXj>+|Eostp9Rv+NVvn9hE;PmW*CE*-5_OGSVb4qnZbM_Tzu zLDq###26agf}wDOB+vIkh!vc5vUDnlD}t~1QM5sTZ_j5cd6MkpNwWO-&0(`Bbf@|Y X;i^kS&zh4700000NkvXXu0mjf+1<9a literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html new file mode 100644 index 0000000000..da02d1eac7 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html @@ -0,0 +1,146 @@ + + + + + + + Debugger: Threads and Traces + + + + + +

Debugger: Threads and Traces

+ + + + + + + +
+ +

In general, a thread refers to a unit of concurrent execution within a target. Typically, + each thread carries its own execution context, so this window provides a means of navigating + those contexts. Furthermore, this window provides a means of navigating and managing open + traces, much like the static listing provides a means of navigating open programs. The window + also provides a timeline showing thread lifespans, and displays a caret which can be used to + navigate the current point in time. This window, the Stack window, and the Dynamic Listing window + provide a complete trace navigation system.

+ +

Trace Tabs

+ +

The trace tab bar is displayed when at least one trace is open. It displays the name of each + open trace in a button, where the "current" or "focused" trace is selected. Clicking a tab will + select its trace and focus the tool onto it. A trace associated with a live target has a red + "recording" icon at its left. If that icon is not present, or has disappeared, the trace is + dead or terminated. A "dead" trace can still be manipulated and marked up, but it will + (obviously) not record any new information from the (dead) target.

+ +

In most cases, a trace is ephemeral, but occasionally, interesting behavior is observed that + is difficult to store as program mark-up. When a trace is no longer needed, it can be closed by + right-clicking the tab and selecting "Close Trace." Warning: closing a + trace that has not been saved cannot be undone. If you have gathered a collection of + unwanted traces, use the "Close Others," "Close Dead," or "Close All" action from the pop-up + menu.

+ +

Navigating Threads

+ +

Selecting a thread in the timeline or the table will navigate to (or "activate" or "focus") + that thread. Many windows which are sensitive to the current thread will update. Notably, the + Registers window + will display the new thread's register values. Listing windows with + configured location tracking will re-compute that location with the new thread's context and + navigate to it. The thread timeline displays all recorded threads in a timeline. Threads which + are alive will appear to extend "to the end of time." The threads table displays more detailed + information, in the following columns:

+ +
    +
  • Name - the name of the thread given by the debugger. Often, this is just an index or + unique id assigned to the thread. This field can be modified, and it has no effect on the + target.
  • + +
  • Created - the point in time when this thread was first observed. If the thread's creation + was observed, then it is its creation time.
  • + +
  • Destroyed - if destroyed, the point in time when this thread was last observed. If the + thread's destruction was observed, then it is its destruction time.
  • + +
  • State - the thread's current state. For a dead trace, this is either ALIVE or TERMINATED, + and depends solely on whether or not a destruction time was recorded. For a live trace, this + may also take RUNNING, STOPPED, or UNKNOWN, depending on what is reported by the debugger. + The state always reflects the present, or latest known, state.
  • + +
  • Comment - a user-modifiable comment about the thread.
  • +
+ +

Navigating Time

+ +

The user can navigate through time within the current trace by using the caret above the + threads timeline. There are also actions for "stepping the trace" forward and backward. See the + Time window for a way to + display and navigate to specific events in the trace's timeline.

+ +

Step Track + Backward

+ +

This action is available when there exists a point in time previous to the current. It steps + the trace backward once, causing most windows to display the recorded data from the new point + in time. Note that stepping backward does not affect the target, and many windows that would + ordinarily interact with a live target, may no longer do so, until the user steps back to the + present. Note also that any component or script that does interact with the target and record + things "into the present" will not cause updates in windows that are not displaying the + present.

+ +

Step Trace + Forward

+ +

This action is available when there exists a point in time ahead of the current. It steps + the trace forward once, causing most windows to display the recorded data from the new point in + time. If the new point in time represents "the present" for a live trace, then many windows + will resume interacting with the target. Note that stepping the trace does not affect the + target; however, stepping back to the present may cause some windows to query the target.

+ +

Seek Trace to + Present

+ +

This toggle is always available and is enabled by default. When first enabled, if the + current trace is live, it immediately steps the trace to the present. Furthermore, as long as + it is enabled and the current trace is live, whenever the recorder steps forward, the tool + steps with it. In most cases, this option should remain on, otherwise stepping the target will + not cause any windows to update to the new machine state. Toggling it off then on is a quick + way to return to the present after browsing the past.

+ +

Actions

+ +

There are additional actions for managing focus and traces.

+ +

Synchronize Trace and Target Focus

+ +

This toggle is always available and is enabled by default. While enabled, any changes in + navigation coordinates are translated, to the extent possible, and sent to the connected + debugger. This may, for example, issue thread and/or frame commands + to GDB so that commands typed into its CLI will refer to the same thread and frame as is + focused in Ghidra. Conversely, any debugger events which indicate a change in focus are + translated, to the extent possible, into navigation coordinates and activated in Ghidra. For + example, if the user issues a frame command to the CLI of a GDB connection, then + Ghidra will navigate to that same frame.

+ +

Save Trace

+ +

This action is available whenever at least one trace is open and active. It saves the + current trace. If the current trace is not in any project, it saves it under "New Traces" of + the current project.

+ +

Save Traces by Default

+ +

This toggle is always available. If the tool is closed with this toggle enabled, all open + traces are immediately saved. Note that if Ghidra is abruptly terminated (a rare occurrence + under normal use), traces may not be saved.

+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/DebuggerThreadsPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/DebuggerThreadsPlugin.png new file mode 100644 index 0000000000000000000000000000000000000000..cbad0796b664907a6d530eb1099b18ccd035901d GIT binary patch literal 23484 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 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/continue.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/continue.png new file mode 100644 index 0000000000000000000000000000000000000000..b79f8b752cbeb42e137e1fc87d5efd163fdab9ee GIT binary patch literal 277 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1ShdAc}; zSoFTVV93W9DB^I@xFar6vMiB1Y>}YiPZ6i3*CJY768WWCT{V`8D7hJ*{KIot=fK*y zytsG2fBw?CoOycTdHSl%!0v2>mtl z;PwOgM%VKjLz0f%elBWv!*TVN4+p|-AGyKW!Zlx*{oAazW#^V=O#MIkGgE+;?kC@B S<269nF?hQAxvX4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1T+d%8G= zSoB_qZG)}fF?LGg5d4`p@Ii`4>GjQRD}z|i z+>H_co1)96py1r_S>t?ntoE&U3^yN(HdL1GIWp<^>e>?S*w|y+7reX^K4Hn~)7!rb ng?`}FVqNKdhGE|1|NeQOxpZfR-`V&U=wt>@S3j3^P64nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1TMd%8G= zSoB_S zzwzOF5rgI;hVHA%?@m>(-&w + + + + + + Debugger: Time + + + + + +

Debugger: Time

+ + + + + + + +
+ +

This window displays all recorded "snapshots" in the current trace. Typically, there is one + snapshot per event recorded. Other tables often display the times of various events or use time + ranges to describe lifespans of various records. Those times refer to the "Snap," which is a + 0-up counter of snapshot records. Thus, a snapshot is a collection of observations of a + target's state, usually while suspended, along with any user mark up. Selecting a snapshot + navigates to the selected point in time. Note that browsing the past may prevent other windows + from interacting with a live target.

+ +

Table Columns

+ +

The table has the following columns:

+ +
    +
  • Snap - the 0-up index of the snapshot (event) recorded.
  • + +
  • Timestamp - the "wall-clock" time of the event. If the debugger doesn't give an event + time, or the snapshot does not correspond to an event, then it is the snapshot creation + time.
  • + +
  • Event Thread - the thread that caused the event, if applicable. In the case of thread + creation, this should probably be the spawned thread, not the parent.
  • + +
  • Ticks - the number of ticks until the next snapshot, if known. If not known, or if + nothing cared to record or compute this, it is 0.
  • + +
  • Description - a user-modifiable description of the snapshot or event. This defaults to + the debugger's description of the event.
  • +
+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTimePlugin/images/DebuggerTimePlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTimePlugin/images/DebuggerTimePlugin.png new file mode 100644 index 0000000000000000000000000000000000000000..d3fc818500df3f061ecf038f05036b04601dd516 GIT binary patch literal 19520 zcmeIacUV(f_b!T}vJn9lkX{r40qMP~ARtwG2PM**H0ebVkgoKO(n|p8y-AZ6Isrlr zCG-|LA>0*kfA`z_{LXXFU-z%$;(3xRmSN2?#~kAw?|A15daWdbf1Bbq78Vx%D_JR3 zEUfFASXkFy{B;XB!*e@|5DQBI=atlRbyvg96r6bC_OXDQM7iIMl^!1?-ldnj&w%^a z{i07_u5qDQZnMZj(!X5)v`BZK;T1&am97J1BhnEvh}_Pbpm4X$uXQ&~>IvBPNY%>j z&nqh0O2|L2tu?I=p!@3+@bg`2J%poRkk9fi<`dmt!3rt0sC$;+M1C90&0%4sKeGal z*?z&k{BUp163lJ3MI{~k^jfKDj1z7oo1X8+1hy#ZXQtIx85-Q3l?+@|PSTtNuIS;o z35}2bkikwH(X~|1p%yi<8_En{>1BL#(v;{Bw9g$FcY1knMPX_?U_V4E-VO?hTfdVt zcN2%Bk&I5O$ncus?H$$Y98DW^E_TniM6c~V{wiRX*0YHBmE$Aogvd3kcrNC89P`)a zg5pEG9bI4OZNu#EnXi4G|M+$7w#XCJ#bT_N*u-99&FhiI*q?}5X}-G#VKKEIIt!JEpz@@UL|$E;;5>w9tQcw6tHMgKFjVpe_o2)E>w=;1OMzG`Nd*QfqLJGQJ#frk%d31#C~31Usu6$3-1Co`)skdeVN+_S?2nyz^S*S)uSXOK1Xdrqc9{ zwVSzm63-e=qi!Bh3YwFBf8NUcDIn~=xo3j3;&onIZjFIVi|c*t!~202pG+f{uX!JH zJwwvw;GFE{+|a#oyowE(e3jd$qG`$1F_8DyEuR}J zgFMj^X#KmHX2mz$agNJeLceRJ_3Up^%B6YZ-Sj$R+JTy!o>#~Y_&oD%y2Z%3!i{~4 z?ZF)?sy737yY9ElHF5B~Gr!(?9VKR+_hx0M=MLq!=HDit=5a&GVwzWaLaEC1Iaxjq zf{4hf9{rBL(Gjlv=7q04k=8BpwC2yRa1{(0;sqI+A2X?8E2LKHax#3(8>a5nrjO_6 zY2LqK+AH_waFUMa0+&;s+breH;ZzTbhohOQ3w7|R29KRb`F`1RPM^2FH$$$s3F_3V z{Cxk)_oX~T(x<0}58ia`VaYSF^5ex8*7xpdM6!J%DZmEnJ0@sYF_e57UB3?2p`|qa zg1X*u`(H;l(;M)=56?%&AdULhIBUDe2wtn#*t*)0~}Q~1CFVwG}bMEIY6Kli8{C;JFd&=3YpyvThosb_d?*1vHtrI0BJPo zQcl)a<57$M4CA*JQIO2(R6)G8%02%&I%4+%g=;q@Z%?^AcuZq7;9-*ZOS|PC2ZIR6 zxsMO}*GM(Ao(ZIJ9790NQ~H2zP^q9na{ zK10T0p-9Y$5zXfaH%c!82@EHbz|7??64f^w?0_#60=54NCHcppp~|Jnh@BO(y7LY! z?Su~hV5j%;oXIA=LL_`J5AR|qNG&IKIIY3lsJ=zm`|v4#5^svBov2*lt)lN5csOe7 zcC!)P+9jDK#^>m>N0U|C#MFZCgYz|0q-cWRhKxP(gU+{k|HvhwLE!r=Q!DKn#_AK+slshbq``4U~=afzlib=}1_uYg&5AUah zaA*}<@T^IA5|3I8g);Vs{Lql;R|`|(Di>o9s2wew3n`)Ce4|(&w5o_t(VIS;!}0n8 zU9iw6Z)Z7T{-Ds%i_RSEeeQg%FRdvgn1qtpPV;mqo+~L=IW6X&mgdyFN6E^ls@Cqx ztqBtLd4+Ri_rtUycJPo0vCuclFloOpcs^bm#;H*jSD1-S`sD$wsPgD!UyhBQsS0#* z$%{*qqiIS{^j2>|SxllI3gF=O+Sjl+?> z_25P_Rs;oWtYa97JVim78OK1D{Kc)4BcuBl|8~)rmSA?wkGlWc3^_GrlMIpY{kT?FVfCnStAYbsRW;|9D)j}!9zx`L#tEVV3iv1fmREDLZvWcl2O1zIj35Fo z-_sHIcUanP>=Iu~+xN#K#lljFR6TtyCq&rP;(U$H-j!c-n4d=e-p+UXXTyK4>SYuSucHi+;YKZ zEqPN;<1VV=jhXh$EkX*JaH?xqQm^DzC7G8*QMaHsecR?Iw9hN{-$&A2P?AW&s*x^9 zB|=Z_{EkH4&tYNF$*IcG)JHO4_T=nPv?&~YzSNOFofqgHM!<# zy9vAQy!l#WP^Vf*6ZNY*VeUDoGn`twx-74x;JK;=4i-zcc=VKbSvK4i3GCj6x0=H_{+1Ca$ za7ObrLTu|c`D?cu&&+q`T3Sl=Dy{FC(0cAXuRR#k%kwv;+e3J^h7*SJJsdZXi z`6fg2lb-=}rX;-AbF$mbCPaJ!_=*ynwWC)zhn)uvA|@-W)CSXE`oSuXhBl{cpk*dK zvBud?6%%<|;(6>S`V|)PkOu+yl(}*-EbwZV8MhqQJ}$F~S?{wj8n1m7xioPxv7;$R zUW=j3H#AsGz+{H3o>jc-NeJAFK8v;7HoeMF-E#AX!V@^Oo_D7p-{@t2S~s{KJh7Xp=LL1#(XDsoieXj@ z5Jw*`yh#@aYo(=*@{Wh z$mPBaV9@?C>oW}^Cf(6Nz`Wh>tvNW$^j)o8DZ)t;p(L-d@!*(-On(pyA!dHzh=7b% zcJimLPHu&I9gJ!j)20T291sviBcF3;SsnhbJZ6y07*?$zfmu&Uj{2P!+9_C^fWa2J z&dNuq>Jts0Xe4k?xzSF$I!bq|tTgRUHg>5Hq ziTU6ol|V<@Vw;7j@e+_94Ac>N&&6Yn3+px4!9%pd>YRRtbXc2p!Bf3@S9>nM)?%CU zMY*@agh54w!k{T5SAID*`kq~(S+ApM-gm#heofYEQobEu6td;MqX?^j8d6SGHpJN? zZ0l03t4HFI6)@Lqt2&;;0ZBstjsDHoM`o=IRVVdsV~T}DSRG|0rxvH)tOtR`S)NNx znb!S__&p~#`?Yt$VYr5l8o3UIKhGbFn(q*o;BGy$<>K3}?4yprp=Pw1EH}QLbP;oM zk6!vq9jCW?9G{iuDc}U<2eJW#wCr1%uE8nDJ5Za+PMahnWN+NmGE`v36*SIu4KvIG zpx@M@=WI1;(&-}#x6*VrTL)Uv>}DFOg9yvFI~i3cZK_Z zciF%^RzGZ2u6IQ&|7UbC5Aszsmf)^6c(PY~F6aT#LR&2>-xg_o$J)RTN$F!DuNJw{ z+>`OJ5#yn;pXY7^a|hIn)n(P~BlR%;*@nJUF%hshiDBG8nVB+|B0ETwE^q0WARm}q zI2WC=z@C9xxy=GJib&Mh=J-j%aG4o{01|*b z&Wwz$qe$`@ES%7nsJ}z*{6os;rfa`vZ&-`D;hl4PV>h5%1onTIxqWg&?+Eq4Ds6VE zt|#$Yz=QPLX9!?vIANQxD$4o8tC7*N`k+z9gF>@5yZgKnIdLaYBOimOGbT|ZbuMfehfr;4U?u9MnYvnqEonKs5O z(3n$?*Z0wVEa=UACxPh~;qrsi(4g0k(?HXeS_+7VgQM%AHn2)X)b5hq)Ls?(SkJ1~p(l=mbe3^pf_mn|`q8Uv z!_MEfnIx7MkV`r;hDVD}L6aqY6i+e ze>xdWmunb{-VWPKJlnuK!=N~;3sM_sztmshBPS*cS^;cO* zV(qSWurE&7aE>xIR{GXq4OkinB;u_%Gqw8z9^W zJHfBe-Q4w163kg&Npj|Hkm$YgaMdw`sGjv4aNHa4}Jm9U$$W4Wxx zmO_+Td#w;>`4OtJccZ2(n)Srf?&~(!0gF{62dOM%tNeJ>{zNh$)TzJWXz+~I`$bYE z^Urs%2J38ylkUQpzYf;d>Vd;t^n4nFxj&t0<$y~l^#YulqE>D0OzqmWJ8H1(u1`7WCIB& zq;D;TId9NtLOL$st+v9_MR9wt!*ubVd_cTysp4A?ir0lmrPdb@82QNzbrhaiNNyOO zt}OJg9vCzDxxx6ljlRZ{*22UlS3gM%dF$JyjDs&(=zu3 zrlmNRyA>UcH3LTTjMrZ4<4{u6G&mor+*9ju^IwNgu+~to@=Z1#x&GeGut{6>q_>GE z6gTY&m)xI|Okg%$6^1(0x*vPm)~+spMQM#%sm9TG`IUT>?0g-g-1rxa)HhS9{JigT z5#jsLzR7b!QCR@b+~a}?)^7iHJ8e{=AGF(UL>QhPRE4)peFTx_d1uf*aa;x)#}*Eq zX_$95620w=*#Ub%%u+L)R)>duXgogE9&FHB#}VnwjPW0MDjbYY)*uFSP8ZAa;er#T zVhV+mABGGizLv@!b_C;pE|awW0%M}l%3HD)l-N*S`pryJygBI;4F5p=Ls{Y?Q9KuA zAv+-;7n5aTW&Dx}PENyt9g`;+!+yp;nh(>a3Nia^IcEC=@XF$STHa5Ftm?eg)LndV zq{RsML0v-5;*%ZlWCGuc$AysYMQsf~?Fo-4$0xKfN?R z2%s*vJpa(^o^u~?Ub!TGwoNfl_UgXk;)cV?pAP&};v1qFEiUYkQqjA=H&f1(f<+jd zB}j(3s)xm*g+hMwpe84N(7)|SW{75x=eL((1Q`~86Gf-EOe=NfnbOf7Cu1&LG zEVbL$I&Z)g7YdLZfv2#cMDZJkQ_+XZ$eGh*XU$W?qECuxe4i zwGD_DVS@`fAr=SBJfxr7=xd}!n|BNz6vHA*%wcB2v$B&5SwL=RamlN6__iDvk@}Q| zV<$1XNk(C(oC633=4VN`2DEeWwRAK5;WP2HNq3=lG9E^VJ8xE0D7uK(x?69m_kS~5U%SQ}^3D#7(v_G){X`l_BWxQikcWQ8 z*Lak7g^{)4KSJ{2yWb;1P4T0mB-{xzJ+|x8vil8*V?*{S@~pzs|FhJ9zA`GIYW{=|S<@$VrM9V-0KvdIf%XuK4jR=i%|uTI-@mo+S706Fx<5$VC45E%uX zV@l!>RJ+wDm?LS?&=~%u4J&M+6LEI+z{;3nKEtO{sv}IjC&AhrLS>VpbZN?u5m2@+ z@*mrc#k7^nGSi*75(q>B3j!QQ1mlDIC!zlOww>+Dk^-d^glbPrn*AEF)$>p|OMtUf zIs|pqEn&$9UroF(yD6m%CI5*ExCEYD+mYYSVbcy4OuBDq$>nSs>dIS8Bl&Cn$0Aur zM=(^sYbwQMhQ!#mzWk3R7u!!zYJ&C2pXN(zYoB*NnxJHDl*~I+ES$4@y8Ggt^KIxY zj`8K$_mMEafN&C*M*tdX;CzbOm>FAj;xbcePcSO?^wVx_omF}pTyOZIZn^WbZq?_e z=}sUd!}+B88@|f-D(vdsH2e$V;$R5NNw1W&X(|-6bgN+JD|TQ&+6rMsKDt`Bm&P_+ zZx^truM7&iOZhBe;UH2!70S80A$Wnpy~qr8zw>JLTI-X};r=gNp|rEeUrq@Nl*;0E zh!Pu{;gpCX8afNd!H#V~|@I<@N+S;uv7x5JdK)QrRQmlLdlIOEMtPYmc97l21 z2NL=Am+4g2x8yqJ%??1bU|zA(fb@LTGoo{8(SCmS2Jmz8n2hzZjYOAr19bE9++x%l z%w^xDf_$tvn4jd8W6Mp;0l`ru@-dg(2HLi?!oDY#O+8pb|lbd|szcESg|4 zliv7mTXoywr;Aa_D|0P=EvTjow0wmX6jL}GyavGWy{JTCXBHB6{dn$ozv&K(^=r5{ zr|SgbBg(JxZcL$g&TZl+Z`csv?QoH;??YMgYSz})vW*@`Tj65YDI~n>E4D`154=hn zTx^AY)4rUXb#iEoVBt<9UK|S-^Eey;s@7*mGakictZuzY+$O=l^AMS-v{oDACHp|p zKZq|dMTcIp$w@wo?;t65o8DbIbcPzp9L@SjXym^B7|W*jpjYyh3=lA02}$@4J}Ei# z__j*swzv4>7CyxQPz>JyDp(?}V)vyOE%T9F6{or8Ym-%Wv|i{FgnH?_t~ZTV(4u`5 zvOh$rcW0eO%>4%PCf&6>;_jhC1iRuIh7PV ztolIArW}wudUg#j6Sz&%f!CPs7*;0XPi5A# zjb5u=Olg@(wzWZ+in>6v@b6E53 zye;VKf(%T2_(Xb=hFQ%E+!{!zw%nUw0q_JxOWKyH{fOlxI|5tBRGc~Q^V6FkPnXkn zQlrnCc>di^UJCZHdIWOy>6>*4A6))pKNZE?%cO4EO;F(&mrfI%e?>yND=o+45ty312_c0+gEcQ z0{je{yuH1>G2Pr12ne%lI_KxJow@`86#Ujtj0Y_je}98-DFiFVIzF3h@@c9b9t`Ra z6e)1*V4b{zaKql&f+1$Bx)qiThIKI@A>I_-@XC^G;@PO}`zz1Iutm}gW> zeEsdwOLGJyr5w~rupY|IL6oamm>9ol4;X>p2UcZA7_|GL(t6?(?8+&eP5@&L1K~6; z;77qzvVt2moBb{iJaZV@O-|ez0Gs7J@jBgav58XXocs7tKKALO{v9g>7I9E4y8(-^ z$n55;EX5>E1zP#tb*thA{%SUc2%n>Am%uyXgO&YGjg^P-hdk{7l}Sf)ZI7C>M_oxS z6{ppuuHG~h=ZRTryA@pk0X0kZMF=SeO{CvGw*jqM@=~ndeGWaJ5cS%l5RfhC5uysH zzotyZ0Q$)|Kqr`XaCh7|QkVewbTp9<$X23~*;Yx!(Ec(Arg)3LR-m32`GIP9)P5mM zU}&PuOwzbM1#p_V$~gOWtC=94;$TIl;+jv9v2Vcl7pHsx3JyLYqO9CQShY#m#Gc)) z9;e{b-A$nb&_QseS2i*%Q%osgZ9#@wH0iMk z3)2!`Kb~Hi$CgkDqY2>t4~1ko{=Igsxx5S6yO-AKn1e;FfZFGMPM@!UjR2lsty(HyPMkv>ATaNX+6MtPTefPzyhPz=`Co z5SGmyYFAI_-oYa3qhQPd0@mY3=5)Q^WUCZ-2NOOR>}i*|`Hg#&3CDmWYRciM89In5 z_3m$p+m&8wbqdf@6E}Imd()uEvpF7 z{Rj()y^@@k%*ozTg%#L!GbY+rBBw|ymGmcalIMv9?&ZQcS+Ay-4!x_^fGWFQ?>odV zyJDQLVB$Kr;4JSToeM1Vdtx4lirb=UOUV`X`SNk>MYFfz;aw4TPr@U%f4Q>xIflg) zdNC$U+E_(cNT}(@6zUuA0YTsM?~q`9&ZY{41U-c~J7`f#{E4Y!TLb|qyZ#fJCi#hu zdFe2UptOYftlM{>St8-Y{ocC**WB(bkc*-xa1C9ujQ>_mLF zo>ir(c`tG3SJRp1kE`#}zqF-{E|1;_Bk>tme?-Djxd?y;3IW^X>ZDrq>AL@0i`m8o z=dT0pnIngJCIYroPsVn39DTB-g6_ZVpP9}aWBWGiJ3kQ%Ebp5>fx)DT!Z}NB_$o#E zHEq2kl7wQ9AACX@q(>Ke<&S*G-K?sRVJjZXTGlY=s@Eg9-6Xztmc493zS7+_Hv1^% zazfk;iMl|n#T9V!_vj!Z^aL3*Ot3c~1gcO{^E*|nj`M5ng6wXF*NKOhb?|sDhJau* z1^dA|X5AMLcbY_Pg-s@vyX6f76A_(1P#%7wddsL9oF}VK0E8sbMm=`o@#vLeB?0d9 zS1$)WH8Un~KG8kNyWPQJ*e`zelntPA@FnjO{}`#WPI|;f`2244$jyIZ1^_mAMw)?~ zSK68`5l8dQzoEzff<-P*e0_4v#4|EJN(X?9Ok1G%py0EmAYoAr1;Byq(Ub>m!^J#P zxdrSaP=yq|3v@yU7bSvX!IKK@!Eu{vF;w$5P3Cam@m zBa*&D;m7r2l59QOE&ym88=B}gnJABJmSO5ImdexQ#XhZOU5JiRwt~y+T>-h_p;5gT zdp06Ycrd%2pY~(5Y9ecA?>x4r9GYpZw?C35JLmqcuk9agE~JL=&w3)+_PY!G&Czha zNzn+rVp7&uA+dG?%cDq_NVYTj=<|7*R=DMaTZram4JsX;Lh-@cS;6=g0kvd=#4hpl z`&N9o(G34+f3cD4t_g^8#PPid?KGNYt%S%iH2PVofTr7C;|m{rnpc!nTRHe89;+D< z5~S2G*vuAuc1@GQzpZD9s%L0o2T!|c@7VOYKf1DmjraBop)@Z;C-nfMs`YWP`2PQu zLLl)|G6w1yBLI5GuSGenO@Htlq>4k>WCFBq<$KNqFp0KAtM(Q&Z{f-6sI^ z*{3nL_S5VAoCkKduJW{`16-4Y|CXp@Mu&~y?6H5(*l?1`OghZ2Uy7V(Mvh_jDU~b!92l}b_o2sTOOOiSCtfz(5*6IB4gnZw4g`d> zGVR^j%)-OYh|BO@E>?LO&n7}J=tpRh9iO$X-?O?lkE~hEPLkX+(kOmq;J%OFalG}j zn+bzUUT^`aX(|>QJr8vr8wFt!v4HZC#Lg8cA)u<0J)k_tbc{%NpfNMWFGLPg{@Maj z1|Crf%cps2YAlwSc*N^gY-ZXdmEl8dXb`pvagcZf7l*;=en)+Hw+1?bg3k)j451&} zzE2SXBK-|V4+OhDNo-UuNOj^u^ShDHI+^M+l%G0xN^jeIeGck*?zS9S990zhU$$!} zFF^ZA4_NsJ2ef0Xm*cQV90C2XDe=<2E_@&-%}0?|9nG7$L7CM(RLWO^fikIWWPU>? zv)Q`Nlp&zq(ARyF_%9XM5F`f_Kf4?VWGUf?K{D=Z-gnXn3GYD2i!p^U#*u&;?hc`G zpEqh$SE;Y3P7-m}>6ZG+1Kvf|A`{);v~!!GDQo!LR1LeAN`jWhM<-^mcwv+H}pA81AC z6=lX>r-(EO4j3jfK@0MRh)?unhvx4w$f5EfN4b8CFSc6Wip5W_$~2?H!`U0wx*e-SY1m z&C;Gf6ezboMQ?}*zVk^fq!e(gw1PQ-hQb79$c%@`X88N6yP$iCd-x@~(roKD52l=< z&$ZInV=F_gv$Rrwg|Z1jIqenL%vzoB zt$C!BTzB9i&kd^>K$kf0TY0Cv7s~vbS(NqCqdr1fu>=QkWU@B);n5e|)I-J1cmT=T z_FTKzSr=YkD*@>4)0-sxhN4B>o@MRXeit!rKvv5(KKeKe7`D(Vund2Xum96-s7@oQ zIAEzJ#S_$3rsIWk2d^jTiS7pe+Nn&_96txcdk6}NMrFmHZTZl(-F43snIvZofJS9Uk%ad=KCh-yncw zA*!;vyPC*&aN_+3J(Y}h+EZH!>50{f`d9^i^C7lA%l_xQjT_gZ=p)1*aMNcRQw9oc zqcmxYb%v;A(wC3FWeFE7_ZDl{#cVYqX6D9omQ!|m2Y~vMsatS99u-hAmDaynRJV>dXlL47?buTr|q4%i2fc4P^In~;Pej(nmtW#Iq7gTTN8L+ z9dEgmMlyFZ`f?QG<~vAFA^QF4ql4Blrra~Z5(B@nP2oCkya-bNJ$k9eg@rmg!=AbN z;nIr!&w0~V2wlT^>3)<;&ov}aZrTXV(Rv%08HiQJv(LigDvg(u`tC^xCkyjPi+aal zW@>hF!LD&v^=802FiPkxeo9Zz)Nf-;4E3?mP3_j*@~(Hhj~XI=!RzUaitHV>W;38z zCAUJFsB@Q{c8+^zuP@qR?}f4XBm`8MK@2FKE_`TkJKWg39rFubTWTBMT}urkmhZw4 z)^_m(=#D_$u^Y2BJ9dIyo6jXXgX{9vi<=C|Vq*)vl}B)7Y?5?ieOUdWJ39hK@sEn* z28vF&evB0wVyD_%5r6ac;W#Gj7T>)n|MO6$( zgP*h|2;rA?1urB?qRFwV_z9?mRHlye&I+KBq>~Y;ob=W{;Y+T7fa)AYB!G&KilfRE zqV}{o){FBLJ2s-JN^z@N?^|HyXwzEwrwNvDrrxH^61=j^tR!TRZInXlgqhM}Vaet< zYqukvAZC)OEzOJ}$E67Px?5RBdMvi-8}KDA^90&BS8Cx;Q~v{m zyB|t|;f9`nRR&}_0H(XofF9FUeGSMvqHAL|!bX8QFz@~Rujq%8w=hzRT!*FZa+htx zig*pJQc2;a()?k7M#)vrQ+0ybRN)@;b=F&+?yq7L72WY%8P&JaJoj*^MO``LFMw(_ z50GCtoo!a(t&J738mm@Xky)HsV`vc;JufAaC$BLWZI;?)>UoxYT=w|*_*c)Pt!ZkH z8@%2qSF7nfgFhhct3+u7j^QF$^5t^Wr>V&jvwoV>IX}Xc8INre5JQBUTDHPHp@U>6 zM2=)P`EVG(l?d2Ui6MJ=qS+0EyT8%n;M0f-K9=~fq3ynwm$MQ;X?yo#uTsrZfny=2 zFbqUm)D<12+P;NilZb}14oM!+DfVUh~06wyuI9$*>_W-FU=`0BHViL-<4MFCTjD&^)X`8s9U)fS>3HpmoHMRm@q zUT6ifFGt%ba9IHLoD)#54}ZBwFO{TOsLRmn&*K9%a2Z?~%y{YjUldX&s~)8y!aTTO zC}y}lIr0RF=pP2e8o4h+9>ABqcH!-EU5i-&k*%I0X#WZ5_Av5U#~`tPzHPnbzFt`M z1egsc;8nH8VW|ygP^Z#vws9Ze41+;SkQ6YpU3&=}qYN{ib7h$WN~y zum9#y&|Ssr;DSzmMu$|pR2o1aBp1aP_QjyqDJW6EPD!WC6aa1-`RYFaGVRt;NmwgD zp=9K16a=GM2_+O#L~zhX9~tdw5hZ<$2Gq6jYLhyjiS!0Loe}h+8KdKQF>Ss zPAxJdvRzL!btr5&F`2uZrC_g7>i}F|i|UuSPyoa%Dy>~|%-!1dIg0}ALV61t5CK6m zNHTnPa&@KP6N=^G*q%z>rbW0hhUF|R(Rargx(AIF>X{XaRP_9z-1d~O!amnGB&sv=|CD74 z|4B}vQLpeFD0z)d+Z1bsow#zWLBX%AS!y;J?Y++qHy?7%K!Eu09=7gkVwJHqGPlvv`pCB%Vhj#5hc{^~GJl_EE_A5Oqi zozffqAB{wCzRQ+7t*Q>L9@{FjU{opj8o@?J9-9>{Hq66(p%{=~QPEkR#@zU(<@)Yl zU$=dAvK>_SM9CUqOMIZIgcP?2#m~uvopR#?I1KAuWeJA#T@n(5rU>%~VWpZu-~sW@mD zGvl=DLYULxh7KQ9G8zOeG?vS+_k)Ax&BbB4k%~t6|s6K;P78$4= z9^UD~Rlod1RcLk&SYJVA+oED@5`R?!wOBlh8hPV7+*uyNr$7~=&STnpH=@4l*8IX`&V=k* z9pJH8*SqSQ9Giyx%}n*R|If@6CU&sdMH=yFK80asl-cf2R2W*C*w_^=T?7-GfVI~g zLB$S@EuKsxIm)!cPAgxv@BSVhsU*z32n&`upC)ZWZfA^1L`$haT;3=mkSrb`)Y9$2yn|sa6l!L}7g875$IZ z>aO~q1*I|a3IMK1D88a{|AZj`Q@|>$m`ocdsCF}20Rd}prw%{{R;y%4!_7=x^RBl# zuZGjNml>fHpV73sb|m{O`$G-#hDLvc2$}RGwauQ^AyPfsYPI>@QG&xYoGG0qsSWc0 ztiosGkVDuYlf>XHGnqAKra&RoopJi!Y41?gRdo@2o9{#a>a*yL8;8A~@ zkdNG>8kRpCSdU$}KHIAJ&%4pYBz+xu%*NURyyL2`w`fZZjSH-An% zz8yU_Y{?W*r-OZs9D%|MUQp?0 z-jb*-?6yorE$SXA-EZ)!y=nVyFQV|2^&p8#vQs(b-hN8;v$l7Qx^*q(=I5HiF+VS= zPYQD1r6{RX^?v~9_}uX|NB9!Y+{j>5j>TcH;=X39?ZcC_Gvzz5V(HGNmr7xNJ+gT) zNW7lgoW!?70czVwH|I1A`6JgJVL|i&obA`C6kHP7S-;jqNU&uVsW;=M&paIumkv{s zv=|Qea@s=OoGe|(;zNPdWgAUB#XlTf6ZV-;KaK-HZfx9cH?sj{ipl7yvqMkG)Sl%u zDThHiwv6(?Nc;L~Ynppt#lxC!#-SQW@db;e*p!EMx{symj2_2_mss|&ukpPVCn?;m z``+Jnf}y`kL}wI6wz!k!M%JF$jcm!%n{wpT6ja_oHrYHE+MZSf(U+$rwgpYrb?Usdvzotm^nU1rY!P?h{4nJ@%rF1w-Q7RWPB(DTEbk7xcE z4m+q;JiD3==e{yVxwCa{ToV@bl;80zoQerO`^n*{(WvH#&AvqW*_;Yj6#s`AIhK7z z*qk^J-kw$P^IDIoew(ei_^X0KPzT13dt^@o8>tR1A~5J>D-LI7%x2S3ZX$@MdJy-s z%aZTCoGkd@J2S7zZtWa@PY)*BBv!R+U?Q4cBl5S7F~4FM6O-!a5;%DxL96K(Xstju z9^{;VbBcqexIyz=fJ+j26w1JB2?_pMS{7@UdC@U@zWc11N7V2ZiyADO!$4WAvkxEg z*8zoua@&Z5;rs+^qev9Yx-=PEHAt7b{aMyl=g!Fv4oql)SmP|2JM-n@m z{WidP?a+b{L%*E$@|+qV>e+^I0H7iw0i1Pn=u8!&Kz3u-2`WQmP;k`-a`{;*5$V~e1?sRIXL2%Q{#XQZL zzG8r<6tFg8PEGtk0Ce^uvO37FIKXe-x02y?28KKxDLP^V7Y}oxUYYR$X-$k{YSVet zm=2hY&{tPX-^OX1w8uu_U5wQL-@CLz4}u_a)0)1B6<2GO_1N46&VE&m(;sHcZvlT@#a1YNo~-R1Dv5X=Rxh?QW#xE}soxjf^)^(dj=Pp9+P8oeoi_(stW?}SSg&?$z7+xr3GK&j zMBaVcyG@n5-DA>fGsIehz&NZ=nnOqPe!4nfge*}b4*bbyJ1GN%4V@|_+pv(EA4L^6 zd)L>X?dpG%R#DPEp$}&B2iQ(SoWlH1H^eZdqrlMN_!`eEHnRemxCofzYr2akaGgIg?<>!@iJ1N#R{y8 z=3{=k)w@2XEks%^60wPavE~54V8K*4Z2frSZel4E%1Qunla-F~x2cGx@SfSBIc6=i z&NahltG(-2YNjuC(_v|($@kX8-Zti#&E7XGXy(y?=uk4%3l44SJx4uUmigk@+36x# zAKOyX(LE+kl>GsA$*XFWT%oDnOWO1qyyT!tcCf(+LsWhE&C7-CTmC{z?LAd$_dDGL zCWt~*yZslMnxEi6Gtx!874_?s5NTO28gT$>l@i%K#$!F7KzM%?w7qn2p)&#EzbeX( z+`#vt{Ay?JrXmb<2_u^~$05mjLRIz5Md_?LA**{QO_pqz4MUKexLt*{!}X4}p5{+B zXndxczDw*x%5Hc+V5zD_O>%9NT9VXpSzQQ8lDL1jzinw-s!xx4eRZNr@BHZrK3Sv3 zs)P`uw!|fLLh_tc=pB7Y$ZH6U2=Sluj9aPjVi6WX0*QOcSv4XrRtx04H0?l#I$e1O z!#NJQiZDRq_P_gtU0D2Ne;BGnX0&Aedh@-vbk3dMb~3mzeL|)2+!&29FRof)1qWCs zt99(+wzkboMHsW zKY*R4mvO5(eDrEFgYWOZr4K=zp!Tbst9Pm2b_8ENkE2;ciI&6^Jd($D`QN_F9H{&W zf-32!Pp;GxzE4!Vh_7bxS#+*AOFLx&e^bRx%%~!AeXP`Y@+0JSJM!hBw&mu7#+dIp z#=0=jxERYeRi}-ChmHDP!v=P7Ph)PCSlkZ2o*X^h(9SzHVbI~_0T2)89y_qHtblIvET3Gz`iq%>}nGP5}H zIqbWxotOa|->bPs3-#g>d2QsdaqzQ#w91eth%qsOQ`_kNG$&rS?OB3nEJV`6)5MjL zRjE1gHl$BX&u;4eJhjs_k~H-DjKd`eytgC5N`tE@x8gLBQz3t)xsE9~3#S20jIU=10H4 zq3;h{Lwl3OBrqc&`Bq}wnhEcnFf>xUvd?b`H$F|1g|N{rdNT{1`V!qn5cZD+bZc7^xeoOD?vCb8lc0n-R*7EUWTA~>>@QtDKDVw+xXH=bLx zuZ5L6^kviaXclEy@GK9&O$yo%*PQi!t-ZRV3Opi!FB`k#HfDlVD}nJ16@qe4bG_8v zjtzAi&K`O3qv^baPU-rB#ANU)Q%TD-)y7iO?F*C#;$u2jR=Ud6_i}399=Lw&8~vgJ z@(*(8n2-uf;`Fa|+Ek*K2_wpupB5;QJuwA4nRfHmwKHj-CnfGTV-u=&gaX`tRrhe@ zlGTgDi}}u2%;p=_L|%(u;UxI~u{T+Pe*rvK3aXvYg~rk#uZ|nrs*y@-Ps*QEBN#$R z9u0%T5a!unG(=;?Mrn*nsWhgI z3b}?Ii~gBKxh1Mb@|IAWXEn&hjILqR;ZHAnR{MrbRx?=A0yflg-wz(+l{8nR0wU?l zM%cwW9!(#Gu#E23!$FsY!(k@u!=WifgvI%ckHuCjp8C@EGt{|j+34H7dnG^5)7U=p zX$1l+i&EmHDdtH#%W?}HiZ32i3RsW7_KbR6*!j6noeN`r5;Mr5Zlu^*E3YlQeC|-f zkrdbmEOEEAe}vre#o^#65$@mCu1Thh88Rg6VOcc47MHqw zI)IbVu+=NfR_95pVvDbq0{d!bcPSbXY5gLbxY45vBA@fzaWki@YFNA>VKY2ebK4^6?1|Y zr*cM+@;MKF(fP2rea|B$W`smQvzAYFqSPdZdH4c1Wc~06?@DaGr_2v(Zv%hHf+iMb z^q+2~2RGSeXw;Wu>1N5Nr8+FS3ZtvV{`vQ{KpH<=tS9YP?r5ZW1-Tb9ojOvh zL{vx_EL?0-UD)Ng3Kuwo1CPn1HuGq@~unW)J#3aF6%R+*dlqM14c>Oo>|D z*W`DTy#p0fK`&Vzfnt(B{&DHWQEif3u@E>xayo~petLh70rB$c@G9v|Aawa~Z(i3( zaQ`R{-^?RzFaC)*b#MTNhZ?J-t~jnS@qHi>a$)qHSJS%mlMqg;3}ePcmZIv7zYPc6 zcs=Clq{}{UJGH1}367H5270($=p!~01n)S>mkFYu8D+*XoZ}S=bcviHIKaH5jO-H5WUOi$>W#g|`jb^$h#z1V`41Ah#l-U16D_=QBUn56< zxd)5GTS%yxG>Z8Ms+Mb9HZiigZSwX&>iR_!HYKmQY*W?t-979@t^3S>HZwc|3Zod^ z|MyEGc9Son7*{gKv}5;#64$zt6V10*Ismv5iJPkicehNkj8=oc|3UcFS3 KDthte)Bgje*aZ~; literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/AbstractDebuggerPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/AbstractDebuggerPlugin.java new file mode 100644 index 0000000000..c41debbe7e --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/AbstractDebuggerPlugin.java @@ -0,0 +1,33 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug; + +import ghidra.framework.plugintool.*; + +public abstract class AbstractDebuggerPlugin extends Plugin { + @SuppressWarnings("unused") + private AutoService.Wiring autoServiceWiring; + + public AbstractDebuggerPlugin(PluginTool tool) { + super(tool); + } + + @Override + protected void init() { + super.init(); + autoServiceWiring = AutoService.wireServicesProvidedAndConsumed(this); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java new file mode 100644 index 0000000000..d0bf327a4b --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java @@ -0,0 +1,359 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug; + +import java.io.IOException; +import java.util.Objects; + +import org.jdom.Element; + +import ghidra.app.services.DebuggerTraceManagerService; +import ghidra.app.services.TraceRecorder; +import ghidra.framework.data.ProjectFileManager; +import ghidra.framework.model.*; +import ghidra.framework.options.SaveState; +import ghidra.framework.plugintool.PluginTool; +import ghidra.trace.database.DBTraceContentHandler; +import ghidra.trace.model.Trace; +import ghidra.trace.model.program.TraceProgramView; +import ghidra.trace.model.thread.TraceThread; +import ghidra.util.Msg; +import ghidra.util.NotOwnerException; + +public class DebuggerCoordinates { + public static final DebuggerCoordinates NOWHERE = + new DebuggerCoordinates(null, null, null, null, 0L, "", 0) { + @Override + public void writeDataState(PluginTool tool, SaveState saveState, String key) { + // Write nothing + } + }; + + private static final String KEY_TRACE_PROJ_LOC = "TraceProjLoc"; + private static final String KEY_TRACE_PROJ_NAME = "TraceProjName"; + private static final String KEY_TRACE_PATH = "TracePath"; + private static final String KEY_TRACE_VERSION = "TraceVersion"; + private static final String KEY_THREAD_KEY = "ThreadKey"; + private static final String KEY_SNAP = "Snap"; + private static final String KEY_TICKS = "Ticks"; + private static final String KEY_FRAME = "Frame"; + + public static DebuggerCoordinates all(Trace trace, TraceRecorder recorder, TraceThread thread, + TraceProgramView view, Long snap, String ticks, Integer frame) { + if (trace == NOWHERE.trace && recorder == NOWHERE.recorder && thread == NOWHERE.thread && + view == NOWHERE.view && snap == NOWHERE.snap && ticks == NOWHERE.ticks && + frame == NOWHERE.frame) { + return NOWHERE; + } + return new DebuggerCoordinates(trace, recorder, thread, view, snap, ticks, frame); + } + + public static DebuggerCoordinates trace(Trace trace) { + if (trace == null) { + return NOWHERE; + } + return all(trace, null, null, null, null, null, null); + } + + public static DebuggerCoordinates recorder(TraceRecorder recorder) { + return all(recorder == null ? null : recorder.getTrace(), recorder, + null, null, recorder == null ? null : recorder.getSnap(), null, null); + } + + public static DebuggerCoordinates thread(TraceThread thread) { + return all(thread == null ? null : thread.getTrace(), null, thread, + null, null, null, null); + } + + public static DebuggerCoordinates view(TraceProgramView view) { + return all(view == null ? null : view.getTrace(), null, null, view, + view == null ? null : view.getSnap(), null, null); + } + + public static DebuggerCoordinates snap(long snap) { + return all(null, null, null, null, snap, null, null); + } + + public static DebuggerCoordinates frame(int frame) { + return all(null, null, null, null, null, null, frame); + } + + public static DebuggerCoordinates threadSnap(TraceThread thread, long snap) { + return all(thread == null ? null : thread.getTrace(), null, thread, null, snap, null, null); + } + + public static boolean equalsIgnoreRecorderAndView(DebuggerCoordinates a, + DebuggerCoordinates b) { + if (!Objects.equals(a.trace, b.trace)) { + return false; + } + if (!Objects.equals(a.thread, b.thread)) { + return false; + } + if (!Objects.equals(a.snap, b.snap)) { + return false; + } + if (!Objects.equals(a.ticks, b.ticks)) { + return false; + } + if (!Objects.equals(a.frame, b.frame)) { + return false; + } + return true; + } + + private final Trace trace; + private final TraceRecorder recorder; + private final TraceThread thread; + private final TraceProgramView view; + private final Long snap; + private final String ticks; + private final Integer frame; + + private final int hash; + + protected DebuggerCoordinates(Trace trace, TraceRecorder recorder, TraceThread thread, + TraceProgramView view, Long snap, String ticks, Integer frame) { + this.trace = trace; + this.recorder = recorder; + this.thread = thread; + this.view = view; + this.snap = snap; + this.ticks = ticks; + this.frame = frame; + + this.hash = Objects.hash(trace, recorder, thread, view, snap, ticks, frame); + } + + @Override + public String toString() { + return String.format( + "Coords(trace=%s,recorder=%s,thread=%s,view=%s,snap=%d,ticks=%s,frame=%d)", + trace, recorder, thread, view, snap, ticks, frame); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DebuggerCoordinates)) { + return false; + } + DebuggerCoordinates that = (DebuggerCoordinates) obj; + if (!Objects.equals(this.trace, that.trace)) { + return false; + } + if (!Objects.equals(this.recorder, that.recorder)) { + return false; + } + if (!Objects.equals(this.thread, that.thread)) { + return false; + } + if (!Objects.equals(this.view, that.view)) { + return false; + } + if (!Objects.equals(this.snap, that.snap)) { + return false; + } + if (!Objects.equals(this.ticks, that.ticks)) { + return false; + } + if (!Objects.equals(this.frame, that.frame)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return hash; + } + + public Trace getTrace() { + return trace; + } + + public TraceRecorder getRecorder() { + return recorder; + } + + public DebuggerCoordinates withRecorder(TraceRecorder newRecorder) { + return all(trace, newRecorder, thread, view, snap, ticks, frame); + } + + public TraceThread getThread() { + return thread; + } + + public DebuggerCoordinates withReFoundThread() { + if (trace == null || thread == null) { + return this; + } + TraceThread newThread = trace.getThreadManager().getThread(thread.getKey()); + if (thread == newThread) { + return this; + } + return withThread(newThread); + } + + public DebuggerCoordinates withThread(TraceThread newThread) { + return all(trace, recorder, newThread, view, snap, ticks, frame); + } + + public TraceProgramView getView() { + return view; + } + + public Long getSnap() { + return snap; + } + + public DebuggerCoordinates withSnap(Long newSnap) { + return all(trace, recorder, thread, view, newSnap, ticks, frame); + } + + public String getTicks() { + return ticks; + } + + public Integer getFrame() { + return frame; + } + + public void writeDataState(PluginTool tool, SaveState saveState, String key) { + SaveState coordState = new SaveState(); + // for NOWHERE, key should be completely omitted + if (trace != null) { + DomainFile df = trace.getDomainFile(); + if (df.getParent() == null) { + return; // not contained within any project + } + ProjectLocator projLoc = df.getProjectLocator(); + if (projLoc != null && !projLoc.isTransient()) { + coordState.putString(KEY_TRACE_PROJ_LOC, projLoc.getLocation()); + coordState.putString(KEY_TRACE_PROJ_NAME, projLoc.getName()); + coordState.putString(KEY_TRACE_PATH, df.getPathname()); + if (!df.isLatestVersion()) { + coordState.putInt(KEY_TRACE_VERSION, df.getVersion()); + } + } + } + if (thread != null) { + coordState.putLong(KEY_THREAD_KEY, thread.getKey()); + } + if (snap != null) { + coordState.putLong(KEY_SNAP, snap); + } + if (ticks != null) { + coordState.putString(KEY_TICKS, ticks); + } + if (frame != null) { + coordState.putInt(KEY_FRAME, frame); + } + + saveState.putXmlElement(key, coordState.saveToXml()); + } + + protected static DomainFile getDomainFile(PluginTool tool, SaveState coordState) { + String pathname = coordState.getString(KEY_TRACE_PATH, null); + String location = coordState.getString(KEY_TRACE_PROJ_LOC, null); + String projName = coordState.getString(KEY_TRACE_PROJ_NAME, null); + if (location == null || projName == null) { + return null; + } + ProjectLocator projLoc = new ProjectLocator(location, projName); + + ProjectData projData = tool.getProject().getProjectData(projLoc); + if (projData == null) { + try { + projData = new ProjectFileManager(projLoc, false, false); + } + catch (NotOwnerException e) { + Msg.showError(DebuggerCoordinates.class, tool.getToolFrame(), "Trace Open Failed", + "Not project owner: " + projLoc + "(" + pathname + ")"); + return null; + } + catch (IOException e) { + Msg.showError(DebuggerCoordinates.class, tool.getToolFrame(), "Trace Open Failed", + "Project error: " + e.getMessage()); + return null; + } + } + + DomainFile df = projData.getFile(pathname); + if (df == null || !DBTraceContentHandler.TRACE_CONTENT_TYPE.equals(df.getContentType())) { + String message = "Can't open trace - \"" + pathname + "\""; + int version = coordState.getInt(KEY_TRACE_VERSION, DomainFile.DEFAULT_VERSION); + if (version != DomainFile.DEFAULT_VERSION) { + message += " version " + version; + } + String title = df == null ? "Trace Not Found" : "Wrong File Type"; + Msg.showError(DebuggerCoordinates.class, tool.getToolFrame(), title, message); + return null; + } + return df; + } + + public static DebuggerCoordinates readDataState(PluginTool tool, SaveState saveState, + String key, boolean resolve) { + if (!saveState.hasValue(key)) { + return NOWHERE; + } + DebuggerTraceManagerService traceManager = + tool.getService(DebuggerTraceManagerService.class); + Trace trace = null; + Element coordElement = saveState.getXmlElement(key); + SaveState coordState = new SaveState(coordElement); + if (traceManager != null) { + DomainFile df = getDomainFile(tool, coordState); + int version = coordState.getInt(KEY_TRACE_VERSION, DomainFile.DEFAULT_VERSION); + if (df != null) { + trace = traceManager.openTrace(df, version); + } + } + TraceThread thread = null; + if (trace != null && coordState.hasValue(KEY_THREAD_KEY)) { + long threadKey = coordState.getLong(KEY_THREAD_KEY, 0); + thread = trace.getThreadManager().getThread(threadKey); + } + Long snap = null; + if (coordState.hasValue(KEY_SNAP)) { + snap = coordState.getLong(KEY_SNAP, 0); + } + String ticks = coordState.getString(KEY_TICKS, null); + Integer frame = null; + if (coordState.hasValue(KEY_FRAME)) { + frame = coordState.getInt(KEY_FRAME, 0); + } + + DebuggerCoordinates coords = + DebuggerCoordinates.all(trace, null, thread, null, snap, ticks, frame); + if (!resolve) { + return coords; + } + return traceManager.resolveCoordinates(coords); + } + + public boolean isDeadOrPresent() { + return recorder == null || isPresent(); + } + + public boolean isAlive() { + return recorder != null; + } + + public boolean isPresent() { + return recorder.getSnap() == snap; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerPluginPackage.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerPluginPackage.java new file mode 100644 index 0000000000..ee835dee8a --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerPluginPackage.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug; + +import ghidra.app.plugin.core.debug.gui.DebuggerResources; +import ghidra.framework.plugintool.util.PluginPackage; + +public class DebuggerPluginPackage extends PluginPackage { + public static final String NAME = "Debugger"; + + public DebuggerPluginPackage() { + super(NAME, DebuggerResources.ICON_PACKAGE, "Plugins for debugging and tracing"); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/ModelActivatedPluginEvent.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/ModelActivatedPluginEvent.java new file mode 100644 index 0000000000..478c9a8889 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/ModelActivatedPluginEvent.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.event; + +import java.lang.ref.WeakReference; + +import ghidra.dbg.DebuggerObjectModel; +import ghidra.framework.plugintool.PluginEvent; + +public class ModelActivatedPluginEvent extends PluginEvent { + static final String NAME = "Model Focus"; + + private final WeakReference newModelRef; + + public ModelActivatedPluginEvent(String source, DebuggerObjectModel model) { + super(source, NAME); + this.newModelRef = new WeakReference<>(model); + } + + public DebuggerObjectModel getActiveModel() { + return newModelRef.get(); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/ModelObjectFocusedPluginEvent.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/ModelObjectFocusedPluginEvent.java new file mode 100644 index 0000000000..07834fc911 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/ModelObjectFocusedPluginEvent.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.event; + +import ghidra.dbg.attributes.TargetObjectRef; +import ghidra.framework.plugintool.PluginEvent; + +/** + * Plugin event class for notification of objects being focused in a connected debugger. + */ +public class ModelObjectFocusedPluginEvent extends PluginEvent { + static final String NAME = "Object Focused"; + + private final TargetObjectRef focusRef; + + /** + * Construct a new plugin event. + * + * @param source name of the plugin that created this event + * @param focusRef the object (ref) associated with this event + */ + public ModelObjectFocusedPluginEvent(String source, TargetObjectRef focusRef) { + super(source, NAME); + this.focusRef = focusRef; + } + + /** + * Return the new focused object ref. Should never be null. + * + * @return the focused object ref + */ + public TargetObjectRef getFocusRef() { + return focusRef; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceActivatedPluginEvent.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceActivatedPluginEvent.java new file mode 100644 index 0000000000..b09100f4fe --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceActivatedPluginEvent.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.event; + +import ghidra.app.plugin.core.debug.DebuggerCoordinates; +import ghidra.framework.plugintool.PluginEvent; + +public class TraceActivatedPluginEvent extends PluginEvent { + static final String NAME = "Trace Location"; + + private final DebuggerCoordinates coordinates; + + public TraceActivatedPluginEvent(String source, DebuggerCoordinates coordinates) { + super(source, NAME); + this.coordinates = coordinates; + } + + public DebuggerCoordinates getActiveCoordinates() { + return coordinates; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceClosedPluginEvent.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceClosedPluginEvent.java new file mode 100644 index 0000000000..935e60d472 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceClosedPluginEvent.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.event; + +import java.lang.ref.WeakReference; + +import ghidra.framework.plugintool.PluginEvent; +import ghidra.trace.model.Trace; + +public class TraceClosedPluginEvent extends PluginEvent { + static final String NAME = "Trace Closed"; + + private final WeakReference traceRef; + + public TraceClosedPluginEvent(String source, Trace trace) { + super(source, NAME); + this.traceRef = new WeakReference<>(trace); + } + + public Trace getTrace() { + return traceRef.get(); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceLocationPluginEvent.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceLocationPluginEvent.java new file mode 100644 index 0000000000..58bede035a --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceLocationPluginEvent.java @@ -0,0 +1,48 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.event; + +import java.util.Objects; + +import ghidra.framework.plugintool.PluginEvent; +import ghidra.program.util.ProgramLocation; +import ghidra.trace.model.program.TraceProgramView; + +public class TraceLocationPluginEvent extends PluginEvent { + public static final String NAME = "TraceLocation"; + + private final ProgramLocation loc; + private final TraceProgramView view; + + public TraceLocationPluginEvent(String src, ProgramLocation loc) { + super(src, NAME); + this.loc = Objects.requireNonNull(loc); + this.view = (TraceProgramView) loc.getProgram(); + } + + public ProgramLocation getLocation() { + return loc; + } + + public TraceProgramView getTraceProgramView() { + return view; + } + + @Override + protected String getDetails() { + return loc.getClass() + " addr==> " + loc.getAddress(); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceOpenedPluginEvent.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceOpenedPluginEvent.java new file mode 100644 index 0000000000..466998e625 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceOpenedPluginEvent.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.event; + +import java.lang.ref.WeakReference; + +import ghidra.framework.plugintool.PluginEvent; +import ghidra.trace.model.Trace; + +public class TraceOpenedPluginEvent extends PluginEvent { + static final String NAME = "Trace Opened"; + + private final WeakReference newTraceRef; + + public TraceOpenedPluginEvent(String source, Trace trace) { + super(source, NAME); + this.newTraceRef = new WeakReference<>(trace); + } + + public Trace getTrace() { + return newTraceRef.get(); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceRecorderAdvancedPluginEvent.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceRecorderAdvancedPluginEvent.java new file mode 100644 index 0000000000..d572737956 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceRecorderAdvancedPluginEvent.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.event; + +import ghidra.app.services.TraceRecorder; +import ghidra.framework.plugintool.PluginEvent; + +public class TraceRecorderAdvancedPluginEvent extends PluginEvent { + static final String NAME = "Recorder Advanced"; + + private final TraceRecorder recorder; + private final long snap; + + /** + * Construct a new plugin event. + * + * @param source name of the plugin that created this event + * @param recorder the recorder that has advanced to its next snap + * @param snap the snap to which the recorder advanced + */ + public TraceRecorderAdvancedPluginEvent(String source, TraceRecorder recorder, long snap) { + super(source, NAME); + this.recorder = recorder; + this.snap = snap; + } + + /** + * Get the recorder that has advanced to its next snap + * + * @return the recorder + */ + public TraceRecorder getRecorder() { + return recorder; + } + + /** + * Get the snap to which the recorder advanced + * + * @return the snap + */ + public long getSnap() { + return snap; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceSelectionPluginEvent.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceSelectionPluginEvent.java new file mode 100644 index 0000000000..d9572a9a04 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/event/TraceSelectionPluginEvent.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.event; + +import java.util.Objects; + +import ghidra.framework.plugintool.PluginEvent; +import ghidra.program.util.ProgramSelection; +import ghidra.trace.model.program.TraceProgramView; + +public class TraceSelectionPluginEvent extends PluginEvent { + public static final String NAME = "TraceSelection"; + + private ProgramSelection selection; + private TraceProgramView view; + + public TraceSelectionPluginEvent(String src, ProgramSelection sel, TraceProgramView view) { + super(src, NAME); + + this.selection = Objects.requireNonNull(sel); + this.view = Objects.requireNonNull(view); + } + + public ProgramSelection getSelection() { + return selection; + } + + public TraceProgramView getTraceProgramView() { + return view; + } + + @Override + protected String getDetails() { + return getClass() + " ==> " + selection; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewAsciiExporter.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewAsciiExporter.java new file mode 100644 index 0000000000..f31c1ffe8a --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewAsciiExporter.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.export; + +import ghidra.app.util.exporter.AsciiExporter; +import ghidra.framework.model.DomainObject; +import ghidra.trace.model.Trace; + +public class TraceViewAsciiExporter extends AsciiExporter { + @Override + public boolean canExportDomainObject(Class domainObjectClass) { + return Trace.class.isAssignableFrom(domainObjectClass); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewBinaryExporter.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewBinaryExporter.java new file mode 100644 index 0000000000..2949322a91 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewBinaryExporter.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.export; + +import ghidra.app.util.exporter.BinaryExporter; +import ghidra.framework.model.DomainObject; +import ghidra.trace.model.Trace; + +public class TraceViewBinaryExporter extends BinaryExporter { + @Override + public boolean canExportDomainObject(Class domainObjectClass) { + return Trace.class.isAssignableFrom(domainObjectClass); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewHtmlExporter.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewHtmlExporter.java new file mode 100644 index 0000000000..b9c581e2c9 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewHtmlExporter.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.export; + +import ghidra.app.util.exporter.HtmlExporter; +import ghidra.framework.model.DomainObject; +import ghidra.trace.model.Trace; + +public class TraceViewHtmlExporter extends HtmlExporter { + @Override + public boolean canExportDomainObject(Class domainObjectClass) { + return Trace.class.isAssignableFrom(domainObjectClass); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewIntelHexExporter.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewIntelHexExporter.java new file mode 100644 index 0000000000..96c85d60fd --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewIntelHexExporter.java @@ -0,0 +1,27 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.export; + +import ghidra.app.util.exporter.IntelHexExporter; +import ghidra.framework.model.DomainObject; +import ghidra.trace.model.Trace; + +public class TraceViewIntelHexExporter extends IntelHexExporter { + @Override + public boolean canExportDomainObject(Class domainObjectClass) { + return Trace.class.isAssignableFrom(domainObjectClass); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewXmlExporter.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewXmlExporter.java new file mode 100644 index 0000000000..46ab514eb6 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/export/TraceViewXmlExporter.java @@ -0,0 +1,56 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.export; + +import java.util.*; +import java.util.stream.Collectors; + +import ghidra.app.util.*; +import ghidra.app.util.exporter.XmlExporter; +import ghidra.framework.model.DomainObject; +import ghidra.trace.model.Trace; + +// TODO: perhaps getApplicableExporters should use domainObject's class, not file's object class. +// TODO: Where un-supported, be less abrasive, e.g., present empty managers. +public class TraceViewXmlExporter extends XmlExporter { + private final Map hideOpts = Map.of( + "Properties", false, + "Relocation Table", false, + "External Libraries", false); + + @Override + public boolean canExportDomainObject(Class domainObjectClass) { + return Trace.class.isAssignableFrom(domainObjectClass); + } + + @Override + public List

Debugger: Stack