bool IDkmRuntimeStepper.OwnsCurrentExecutionLocation(DkmRuntimeInstance runtimeInstance, DkmStepper stepper, DkmStepArbitrationReason reason) { var processData = DebugHelpers.GetOrCreateDataItem <NullcRemoteProcessDataItem>(runtimeInstance.Process); // Can't handle steps without an address if (stepper.StartingAddress == null) { return(false); } var instructionAddress = stepper.StartingAddress.CPUInstructionPart.InstructionPointer; if (DebugHelpers.useNativeInterfaces) { if (processData.nativeModuleInstance != null) { if (instructionAddress >= processData.nativeModuleInstance.BaseAddress && instructionAddress < processData.nativeModuleInstance.BaseAddress + processData.nativeModuleInstance.Size) { return(processData.bytecode.ConvertNativeAddressToInstruction(instructionAddress) != 0); } } } else { if (processData.moduleInstance != null) { if (instructionAddress >= processData.moduleInstance.BaseAddress && instructionAddress < processData.moduleInstance.BaseAddress + processData.moduleInstance.Size) { return(processData.bytecode.ConvertNativeAddressToInstruction(instructionAddress) != 0); } } } return(false); }
void IDkmRuntimeMonitorBreakpointHandler.DisableRuntimeBreakpoint(DkmRuntimeBreakpoint runtimeBreakpoint) { var breakpointData = runtimeBreakpoint.GetDataItem <NullcBreakpointDataItem>(); if (breakpointData != null) { var processData = DebugHelpers.GetOrCreateDataItem <NullcRemoteProcessDataItem>(runtimeBreakpoint.Process); var address = breakpointData.instructionAddress.CPUInstructionPart.InstructionPointer; runtimeBreakpoint.Process.InvisibleWriteMemory(breakpointData.instructionAddress.CPUInstructionPart.InstructionPointer, breakpointData.prevValue); // Skip internal breakpoints used in stepper if (runtimeBreakpoint.SourceId != DebugHelpers.NullcStepperBreakpointSourceId) { if (processData.activeBreakpointLocations.Contains(address)) { processData.activeBreakpointLocations.Remove(address); } // If we were planning to restore the breakpoint, forget about it if (processData.lastHitBreakpointLocation == address) { processData.lastHitBreakpointLocation = 0; } } } }
void IDkmProcessExecutionNotification.OnProcessResume(DkmProcess process, DkmProcessExecutionCounters processCounters) { var processData = DebugHelpers.GetOrCreateDataItem <NullcRemoteProcessDataItem>(process); // Third stage of breakpoint handling - schedule a single step so we will move from the instruction location where debug break instruction must be restored (int 3) if (processData.lastHitBreakpointLocation != 0) { var singleStepRequest = DkmSingleStepRequest.Create(DebugHelpers.NullcStepperBreakpointSourceId, processData.singleStepThread); singleStepRequest.EnableSingleStep(); } }
void IDkmRuntimeMonitorBreakpointHandler.EnableRuntimeBreakpoint(DkmRuntimeBreakpoint runtimeBreakpoint) { var instructionBreakpoint = (runtimeBreakpoint as DkmRuntimeInstructionBreakpoint); if (instructionBreakpoint != null) { var nativeInstructionAddress = instructionBreakpoint.InstructionAddress as DkmNativeInstructionAddress; if (nativeInstructionAddress == null) { Debug.WriteLine("Missing breakpoint address"); return; } ulong address = instructionBreakpoint.InstructionAddress.CPUInstructionPart.InstructionPointer; byte[] prevValue = new byte[1]; if (runtimeBreakpoint.Process.ReadMemory(address, DkmReadMemoryFlags.ExecutableOnly, prevValue) == 0) { Debug.WriteLine("Failed to read current instruction"); return; } var breakpointData = DebugHelpers.GetOrCreateDataItem <NullcBreakpointDataItem>(runtimeBreakpoint); breakpointData.instructionAddress = nativeInstructionAddress; breakpointData.prevValue = prevValue; // Write an 'int 3' instruction runtimeBreakpoint.Process.InvisibleWriteMemory(address, new byte[1] { 0xcc }); // Remember locations of the active user breakpoint instructions, skip internal breakpoints used in stepper if (runtimeBreakpoint.SourceId != DebugHelpers.NullcStepperBreakpointSourceId) { var processData = DebugHelpers.GetOrCreateDataItem <NullcRemoteProcessDataItem>(instructionBreakpoint.Process); if (!processData.activeBreakpointLocations.Contains(address)) { processData.activeBreakpointLocations.Add(address); } } return; } Debug.WriteLine("Failed to enable the breakpoint"); }
bool IDkmModuleUserCodeDeterminer.IsUserCode(DkmModuleInstance moduleInstance) { var processData = DebugHelpers.GetOrCreateDataItem <NullcRemoteProcessDataItem>(moduleInstance.Process); if (processData != null) { if (moduleInstance.LoadContext == "nullc embedded code") { return(true); } } return(moduleInstance.IsUserCode()); }
void IDkmSingleStepCompleteReceived.OnSingleStepCompleteReceived(DkmSingleStepRequest singleStepRequest, DkmEventDescriptorS eventDescriptor) { var processData = DebugHelpers.GetOrCreateDataItem <NullcRemoteProcessDataItem>(singleStepRequest.Thread.Process); // Last stage of breakpoint handling - restore debug break instruction now that we have moved from the breakpoint location if (processData.lastHitBreakpointLocation != 0) { // Restore breakpoint instruction singleStepRequest.Thread.Process.InvisibleWriteMemory(processData.lastHitBreakpointLocation, new byte[1] { 0xcc }); processData.lastHitBreakpointLocation = 0; eventDescriptor.Suppress(); } }
void IDkmDebugMonitorExceptionNotification.OnDebugMonitorException(DkmExceptionInformation exception, DkmWorkList workList, DkmEventDescriptorS eventDescriptor) { var processData = DebugHelpers.GetOrCreateDataItem <NullcRemoteProcessDataItem>(exception.Process); if (exception.InstructionAddress == null) { return; } ulong address = exception.InstructionAddress.CPUInstructionPart.InstructionPointer; // Breakpoint if (exception.Code == 0x80000003) { // Internal stepper breakpoints are silenced but te debugger is notified to break the process if (processData.stepBreakpoint != null && processData.stepBreakpoint.InstructionAddress.CPUInstructionPart.InstructionPointer == address) { eventDescriptor.Suppress(); exception.Thread.OnEmbeddedBreakpointHit(exception.InstructionAddress, false); } if (processData.secondaryStepBreakpoint != null && processData.secondaryStepBreakpoint.InstructionAddress.CPUInstructionPart.InstructionPointer == address) { eventDescriptor.Suppress(); exception.Thread.OnEmbeddedBreakpointHit(exception.InstructionAddress, false); } // Second stage of breakpoint handling - supress exception, notify debugger to break the process and remember what breakpoint has to be resotred later if (processData.activeBreakpointLocations.Contains(address)) { eventDescriptor.Suppress(); exception.Thread.OnEmbeddedBreakpointHit(exception.InstructionAddress, false); // Schedule restore of this breakpoint when process continues processData.lastHitBreakpointLocation = address; processData.singleStepThread = exception.Thread; } } }
void IDkmInstructionAddressProvider.GetInstructionAddress(DkmProcess process, DkmWorkList workList, ulong instructionPointer, DkmCompletionRoutine <DkmGetInstructionAddressAsyncResult> completionRoutine) { var processData = DebugHelpers.GetOrCreateDataItem <NullcModuleDataItem>(process); if (!processData.nullcIsMissing && processData.moduleBase == 0) { processData.moduleBase = DebugHelpers.ReadPointerVariable(process, "nullcModuleStartAddress").GetValueOrDefault(0); processData.moduleSize = (uint)(DebugHelpers.ReadPointerVariable(process, "nullcModuleEndAddress").GetValueOrDefault(0) - processData.moduleBase); processData.nullcIsMissing = processData.moduleBase == 0; } if (processData.moduleBase != 0) { if (instructionPointer >= processData.moduleBase && instructionPointer < processData.moduleBase + processData.moduleSize) { DkmInstructionAddress address; if (DebugHelpers.useNativeInterfaces) { var nullcNativeRuntime = DebugHelpers.useDefaultRuntimeInstance ? process.GetNativeRuntimeInstance() : process.GetRuntimeInstances().OfType <DkmNativeRuntimeInstance>().FirstOrDefault(el => el.Id.RuntimeType == DebugHelpers.NullcRuntimeGuid); var nullcModuleInstance = nullcNativeRuntime.GetModuleInstances().OfType <DkmNativeModuleInstance>().FirstOrDefault(el => el.Module != null && el.Module.CompilerId.VendorId == DebugHelpers.NullcCompilerGuid); address = DkmNativeInstructionAddress.Create(nullcNativeRuntime, nullcModuleInstance, (uint)(instructionPointer - processData.moduleBase), new DkmInstructionAddress.CPUInstruction(instructionPointer)); } else { var nullcNativeRuntime = process.GetRuntimeInstances().OfType <DkmCustomRuntimeInstance>().FirstOrDefault(el => el.Id.RuntimeType == DebugHelpers.NullcRuntimeGuid); var nullcModuleInstance = nullcNativeRuntime.GetModuleInstances().OfType <DkmCustomModuleInstance>().FirstOrDefault(el => el.Module != null && el.Module.CompilerId.VendorId == DebugHelpers.NullcCompilerGuid); address = DkmCustomInstructionAddress.Create(nullcNativeRuntime, nullcModuleInstance, null, instructionPointer, null, new DkmInstructionAddress.CPUInstruction(instructionPointer)); } completionRoutine(new DkmGetInstructionAddressAsyncResult(address, true)); return; } } process.GetInstructionAddress(workList, instructionPointer, completionRoutine); }
DkmStackWalkFrame[] IDkmCallStackFilter.FilterNextFrame(DkmStackContext stackContext, DkmStackWalkFrame input) { if (input == null) // null input frame indicates the end of the call stack. This sample does nothing on end-of-stack. { var processData = DebugHelpers.GetOrCreateDataItem <NullcStackFilterDataItem>(stackContext.InspectionSession.Process); processData.nullcFramePosition = 1; return(null); } if (input.InstructionAddress == null) { return(new DkmStackWalkFrame[1] { input }); } if (input.InstructionAddress.ModuleInstance != null) { if (input.BasicSymbolInfo != null && input.BasicSymbolInfo.MethodName == "ExecutorRegVm::RunCode") { var processData = DebugHelpers.GetOrCreateDataItem <NullcStackFilterDataItem>(input.Thread.Process); InitNullcDebugFunctions(processData, input.RuntimeInstance); if (processData.nullcIsMissing) { return(new DkmStackWalkFrame[1] { input }); } string vmInstructionStr = ExecuteExpression("instruction - codeBase", stackContext, input, true); string ptrValue = DebugHelpers.Is64Bit(input.Thread.Process) ? "longValue" : "intValue"; string vmDataOffsetStr = ExecuteExpression($"(unsigned long long)regFilePtr[1].{ptrValue} - (unsigned long long)rvm->dataStack.data", stackContext, input, true); if (vmInstructionStr != null && vmDataOffsetStr != null) { ulong vmInstruction = ulong.Parse(vmInstructionStr); string stackFrameDesc = ExecuteExpression($"((char*(*)(unsigned, unsigned)){processData.nullcDebugGetVmAddressLocation})({vmInstruction}, 0),sb", stackContext, input, false); var nullcCustomRuntime = input.Thread.Process.GetRuntimeInstances().OfType <DkmCustomRuntimeInstance>().FirstOrDefault(el => el.Id.RuntimeType == DebugHelpers.NullcVmRuntimeGuid); if (stackFrameDesc != null && nullcCustomRuntime != null) { var flags = input.Flags; flags = flags & ~(DkmStackWalkFrameFlags.NonuserCode | DkmStackWalkFrameFlags.UserStatusNotDetermined); flags = flags | DkmStackWalkFrameFlags.InlineOptimized; DkmCustomModuleInstance nullcModuleInstance = nullcCustomRuntime.GetModuleInstances().OfType <DkmCustomModuleInstance>().FirstOrDefault(el => el.Module != null && el.Module.CompilerId.VendorId == DebugHelpers.NullcCompilerGuid); if (nullcModuleInstance != null) { DkmInstructionAddress instructionAddress = DkmCustomInstructionAddress.Create(nullcCustomRuntime, nullcModuleInstance, null, vmInstruction, null, null); var rawAnnotations = new List <DkmStackWalkFrameAnnotation>(); // Additional unique request id rawAnnotations.Add(DkmStackWalkFrameAnnotation.Create(DebugHelpers.NullcCallStackDataBaseGuid, ulong.Parse(vmDataOffsetStr))); var annotations = new ReadOnlyCollection <DkmStackWalkFrameAnnotation>(rawAnnotations); DkmStackWalkFrame frame = DkmStackWalkFrame.Create(stackContext.Thread, instructionAddress, input.FrameBase, input.FrameSize, flags, stackFrameDesc, input.Registers, annotations, nullcModuleInstance, null, null); return(new DkmStackWalkFrame[2] { frame, input }); } } } } return(new DkmStackWalkFrame[1] { input }); } // Currently we want to provide info only for JiT frames if (!input.Flags.HasFlag(DkmStackWalkFrameFlags.UserStatusNotDetermined)) { return(new DkmStackWalkFrame[1] { input }); } try { var processData = DebugHelpers.GetOrCreateDataItem <NullcStackFilterDataItem>(input.Thread.Process); InitNullcDebugFunctions(processData, input.RuntimeInstance); if (processData.nullcIsMissing) { return new DkmStackWalkFrame[1] { input } } ; string stackFrameDesc = ExecuteExpression($"((char*(*)(void*, unsigned)){processData.nullcDebugGetNativeAddressLocation})((void*)0x{input.InstructionAddress.CPUInstructionPart.InstructionPointer:X}, 0),sb", stackContext, input, false); if (stackFrameDesc != null) { var flags = input.Flags; flags = flags & ~(DkmStackWalkFrameFlags.NonuserCode | DkmStackWalkFrameFlags.UserStatusNotDetermined); if (stackFrameDesc == "[Transition to nullc]") { return(new DkmStackWalkFrame[1] { DkmStackWalkFrame.Create(stackContext.Thread, input.InstructionAddress, input.FrameBase, input.FrameSize, flags, stackFrameDesc, input.Registers, input.Annotations) }); } DkmStackWalkFrame frame = null; var nullcCustomRuntime = input.Thread.Process.GetRuntimeInstances().OfType <DkmCustomRuntimeInstance>().FirstOrDefault(el => el.Id.RuntimeType == DebugHelpers.NullcRuntimeGuid); var nullcNativeRuntime = DebugHelpers.useDefaultRuntimeInstance ? input.Thread.Process.GetNativeRuntimeInstance() : input.Thread.Process.GetRuntimeInstances().OfType <DkmNativeRuntimeInstance>().FirstOrDefault(el => el.Id.RuntimeType == DebugHelpers.NullcRuntimeGuid); if (DebugHelpers.useNativeInterfaces ? nullcNativeRuntime != null : nullcCustomRuntime != null) { DkmModuleInstance nullcModuleInstance; if (DebugHelpers.useNativeInterfaces) { nullcModuleInstance = nullcNativeRuntime.GetModuleInstances().OfType <DkmNativeModuleInstance>().FirstOrDefault(el => el.Module != null && el.Module.CompilerId.VendorId == DebugHelpers.NullcCompilerGuid); } else { nullcModuleInstance = nullcCustomRuntime.GetModuleInstances().OfType <DkmCustomModuleInstance>().FirstOrDefault(el => el.Module != null && el.Module.CompilerId.VendorId == DebugHelpers.NullcCompilerGuid); } if (nullcModuleInstance != null) { // If the top of the call stack is a nullc frame, nullc call stack wont have an entry for it and we start from 0, otherwise we start from default value of 1 if (input.Flags.HasFlag(DkmStackWalkFrameFlags.TopFrame)) { processData.nullcFramePosition = 0; } string stackFrameBase = ExecuteExpression($"((unsigned(*)(unsigned)){processData.nullcDebugGetReversedStackDataBase})({processData.nullcFramePosition})", stackContext, input, true); processData.nullcFramePosition++; if (int.TryParse(stackFrameBase, out int stackFrameBaseValue)) { DkmInstructionAddress instructionAddress; if (DebugHelpers.useNativeInterfaces) { var rva = (uint)(input.InstructionAddress.CPUInstructionPart.InstructionPointer - nullcModuleInstance.BaseAddress); instructionAddress = DkmNativeInstructionAddress.Create(nullcNativeRuntime, nullcModuleInstance as DkmNativeModuleInstance, rva, input.InstructionAddress.CPUInstructionPart); } else { instructionAddress = DkmCustomInstructionAddress.Create(nullcCustomRuntime, nullcModuleInstance as DkmCustomModuleInstance, null, input.InstructionAddress.CPUInstructionPart.InstructionPointer, null, input.InstructionAddress.CPUInstructionPart); } var rawAnnotations = new List <DkmStackWalkFrameAnnotation>(); // Additional unique request id rawAnnotations.Add(DkmStackWalkFrameAnnotation.Create(DebugHelpers.NullcCallStackDataBaseGuid, (ulong)(stackFrameBaseValue))); var annotations = new ReadOnlyCollection <DkmStackWalkFrameAnnotation>(rawAnnotations); frame = DkmStackWalkFrame.Create(stackContext.Thread, instructionAddress, input.FrameBase, input.FrameSize, flags, stackFrameDesc, input.Registers, annotations, nullcModuleInstance, null, null); } } } if (frame == null) { frame = DkmStackWalkFrame.Create(stackContext.Thread, input.InstructionAddress, input.FrameBase, input.FrameSize, flags, stackFrameDesc, input.Registers, input.Annotations); } return(new DkmStackWalkFrame[1] { frame }); } } catch (Exception ex) { Console.WriteLine("Failed to evaluate: " + ex.ToString()); } return(new DkmStackWalkFrame[1] { input }); }
void IDkmProcessExecutionNotification.OnProcessPause(DkmProcess process, DkmProcessExecutionCounters processCounters) { try { ulong?moduleBaseOpt = DebugHelpers.ReadPointerVariable(process, "nullcModuleStartAddress"); // Check if nullc is available if (moduleBaseOpt == null) { return; } var processData = DebugHelpers.GetOrCreateDataItem <NullcRemoteProcessDataItem>(process); if (processData.language == null) { processData.compilerId = new DkmCompilerId(DebugHelpers.NullcCompilerGuid, DebugHelpers.NullcLanguageGuid); processData.language = DkmLanguage.Create("nullc", processData.compilerId); } // Create VM runtime and module if (processData.vmRuntimeInstance == null) { DkmRuntimeInstanceId runtimeId = new DkmRuntimeInstanceId(DebugHelpers.NullcVmRuntimeGuid, 0); processData.vmRuntimeInstance = DkmCustomRuntimeInstance.Create(process, runtimeId, null); } if (processData.vmModule == null) { DkmModuleId moduleId = new DkmModuleId(Guid.NewGuid(), DebugHelpers.NullcSymbolProviderGuid); processData.vmModule = DkmModule.Create(moduleId, "nullc.vm.code", processData.compilerId, process.Connection, null); } if (processData.vmModuleInstance == null) { DkmDynamicSymbolFileId symbolFileId = DkmDynamicSymbolFileId.Create(DebugHelpers.NullcSymbolProviderGuid); processData.vmModuleInstance = DkmCustomModuleInstance.Create("nullc_vm", "nullc.vm.code", 0, processData.vmRuntimeInstance, null, symbolFileId, DkmModuleFlags.None, DkmModuleMemoryLayout.Unknown, 0, 1, 0, "nullc vm code", false, null, null, null); processData.vmModuleInstance.SetModule(processData.vmModule, true); // Can use reload? } ulong moduleBase = moduleBaseOpt.GetValueOrDefault(0); uint moduleSize = (uint)(DebugHelpers.ReadPointerVariable(process, "nullcModuleEndAddress").GetValueOrDefault(0) - moduleBase); // Create JiT runtime and module if (moduleBase != 0 && moduleSize != 0) { if (processData.runtimeInstance == null && processData.nativeRuntimeInstance == null) { DkmRuntimeInstanceId runtimeId = new DkmRuntimeInstanceId(DebugHelpers.NullcRuntimeGuid, 0); if (DebugHelpers.useNativeInterfaces) { processData.nativeRuntimeInstance = DebugHelpers.useDefaultRuntimeInstance ? process.GetNativeRuntimeInstance() : DkmNativeRuntimeInstance.Create(process, runtimeId, DkmRuntimeCapabilities.None, process.GetNativeRuntimeInstance(), null); } else { processData.runtimeInstance = DkmCustomRuntimeInstance.Create(process, runtimeId, null); } } if (processData.module == null) { DkmModuleId moduleId = new DkmModuleId(Guid.NewGuid(), DebugHelpers.NullcSymbolProviderGuid); processData.module = DkmModule.Create(moduleId, "nullc.embedded.code", processData.compilerId, process.Connection, null); } if (processData.moduleInstance == null && processData.nativeModuleInstance == null) { DkmDynamicSymbolFileId symbolFileId = DkmDynamicSymbolFileId.Create(DebugHelpers.NullcSymbolProviderGuid); if (DebugHelpers.useNativeInterfaces) { processData.nativeModuleInstance = DkmNativeModuleInstance.Create("nullc", "nullc.embedded.code", 0, null, symbolFileId, DkmModuleFlags.None, DkmModuleMemoryLayout.Unknown, 1, "nullc embedded code", processData.nativeRuntimeInstance, moduleBase, moduleSize, Microsoft.VisualStudio.Debugger.Clr.DkmClrHeaderStatus.NativeBinary, false, null, null, null); processData.nativeModuleInstance.SetModule(processData.module, true); // Can use reload? } else { processData.moduleInstance = DkmCustomModuleInstance.Create("nullc", "nullc.embedded.code", 0, processData.runtimeInstance, null, symbolFileId, DkmModuleFlags.None, DkmModuleMemoryLayout.Unknown, moduleBase, 1, moduleSize, "nullc embedded code", false, null, null, null); processData.moduleInstance.SetModule(processData.module, true); // Can use reload? } } } // Update bytecode var moduleBytecodeVersion = DebugHelpers.ReadPointerVariable(process, "nullcModuleBytecodeVersion").GetValueOrDefault(0); if (processData.moduleBytecodeLocation != 0 && moduleBytecodeVersion != processData.moduleBytecodeVersion) { processData.moduleBytecodeLocation = 0; processData.moduleBytecodeSize = 0; processData.moduleBytecodeRaw = null; processData.bytecode = null; } if (processData.moduleBytecodeLocation == 0) { processData.moduleBytecodeLocation = DebugHelpers.ReadPointerVariable(process, "nullcModuleBytecodeLocation").GetValueOrDefault(0); processData.moduleBytecodeSize = DebugHelpers.ReadPointerVariable(process, "nullcModuleBytecodeSize").GetValueOrDefault(0); processData.moduleBytecodeVersion = moduleBytecodeVersion; if (processData.moduleBytecodeLocation != 0) { processData.moduleBytecodeRaw = new byte[processData.moduleBytecodeSize]; process.ReadMemory(processData.moduleBytecodeLocation, DkmReadMemoryFlags.None, processData.moduleBytecodeRaw); processData.bytecode = new NullcBytecode(); processData.bytecode.ReadFrom(processData.moduleBytecodeRaw, DebugHelpers.Is64Bit(process)); // Notify local component about bytecode update var message = DkmCustomMessage.Create(process.Connection, process, DebugHelpers.NullcReloadSymbolsMessageGuid, 1, null, null); message.SendHigher(); } } } catch (Exception ex) { Console.WriteLine("OnProcessPause failed with: " + ex.ToString()); } }
void IDkmRuntimeStepper.NotifyStepComplete(DkmRuntimeInstance runtimeInstance, DkmStepper stepper) { var processData = DebugHelpers.GetOrCreateDataItem <NullcRemoteProcessDataItem>(runtimeInstance.Process); ClearStepBreakpoints(processData); }
void IDkmRuntimeStepper.Step(DkmRuntimeInstance runtimeInstance, DkmStepper stepper, DkmStepArbitrationReason reason) { if (!(this as IDkmRuntimeStepper).OwnsCurrentExecutionLocation(runtimeInstance, stepper, reason)) { runtimeInstance.Step(stepper, reason); return; } var processData = DebugHelpers.GetOrCreateDataItem <NullcRemoteProcessDataItem>(runtimeInstance.Process); if (stepper.StepKind == DkmStepKind.StepIntoSpecific) { throw new NotSupportedException(); } if (stepper.StepKind == DkmStepKind.Over || stepper.StepKind == DkmStepKind.Into) { ClearStepBreakpoints(processData); int nullcInstruction = processData.bytecode.ConvertNativeAddressToInstruction(stepper.StartingAddress.CPUInstructionPart.InstructionPointer); int line = processData.bytecode.GetInstructionSourceLocationLine(nullcInstruction, out int moduleIndex); // Instruction based stepping int nextNullcInstruction = nullcInstruction; int secondaryNullcInstruction = 0; while (nextNullcInstruction < processData.bytecode.instructions.Count) { // Walk forward until an instruction with a different line is hit if (processData.bytecode.GetInstructionSourceLocationLine(nextNullcInstruction, out _) != line) { break; } var nextInstruction = processData.bytecode.instructions[nextNullcInstruction]; // If there is a jump, place a breakpoint at the target (even if it's on the same line) if (nextInstruction.code == NullcInstructionCode.rviJmp) { nextNullcInstruction = (int)nextInstruction.argument; break; } // If there is a conditional jump, place two breakpoints - one after it and one at the target (even if either of them is on the same line) if (nextInstruction.code == NullcInstructionCode.rviJmpz || nextInstruction.code == NullcInstructionCode.rviJmpnz) { secondaryNullcInstruction = nextNullcInstruction + 1; nextNullcInstruction = (int)nextInstruction.argument; break; } // If there is a return, I wish I could just fallback to parent runtime to handle it, may be required to place a breakpoint at the call site if (nextInstruction.code == NullcInstructionCode.rviReturn) { ulong address = GetReturnAddress(stepper.Thread); // Address must be within nullc module or we will have to cancel the step if (address >= processData.nativeModuleInstance.BaseAddress && address < processData.nativeModuleInstance.BaseAddress + processData.nativeModuleInstance.Size) { processData.stepBreakpoint = PlaceBreakpointAtAddress(processData, stepper.Thread, address); } else { stepper.OnStepComplete(stepper.Thread, false); } return; } if (stepper.StepKind == DkmStepKind.Into) { if (nextInstruction.code == NullcInstructionCode.rviCall && stepper.StepKind == DkmStepKind.Into) { NullcFuncInfo function = processData.bytecode.functions[(int)nextInstruction.argument]; if (function.regVmAddress != ~0u) { secondaryNullcInstruction = nextNullcInstruction + 1; nextNullcInstruction = (int)function.regVmAddress; break; } else if (function.funcPtrWrap != 0 || function.funcPtrRaw != 0) { // TODO: Step into an external function // I've tryied placing a breakpoint at the external call target but it's not getting handled correctly // Maybe the solution is to single step the process until the stack frame changes } } else if (nextInstruction.code == NullcInstructionCode.rviCallPtr) { // TODO: Step into a function pointer call } } nextNullcInstruction++; } if (nextNullcInstruction != 0) { ulong instructionAddress = processData.bytecode.ConvertInstructionToNativeAddress(nextNullcInstruction); processData.stepBreakpoint = PlaceBreakpointAtAddress(processData, stepper.Thread, instructionAddress); } if (secondaryNullcInstruction != 0) { ulong instructionAddress = processData.bytecode.ConvertInstructionToNativeAddress(secondaryNullcInstruction); processData.secondaryStepBreakpoint = PlaceBreakpointAtAddress(processData, stepper.Thread, instructionAddress); } } else if (stepper.StepKind == DkmStepKind.Out) { ulong address = GetReturnAddress(stepper.Thread); processData.stepBreakpoint = PlaceBreakpointAtAddress(processData, stepper.Thread, address); } else { throw new NotImplementedException(); } }