Merge remote-tracking branch 'origin/GP-6902_Dan_multiLineAssembly--SQUASHED'

This commit is contained in:
Ryan Kurtz
2026-07-02 12:36:21 -04:00
52 changed files with 2700 additions and 928 deletions

View File

@@ -27,7 +27,14 @@
Other languages may also be presented if the only thing that varies is the default disassembly
context, e.g., configuring ARM will also allow THUMB disassembly.</P>
<H3><A name="patch_instruction"></A> Patch Instruction / Assemble</H3>
<H3><A name="assemble_patch"></A>Assemble</H3>
<P>This action assembles a block of instructions starting at the cursor. One action is
presented per language configured at the current location. Other languages may also be
presented if the only thing that varies is the default disassembly context, e.g., configuring
ARM will also allow THUMB assembly.</P>
<H3><A name="patch_instruction"></A> Patch Instruction</H3>
<P>This action assembles an instruction at the cursor. One action is presented per language
configured at the current location. Other languages may also be presented if the only thing

View File

@@ -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.app.plugin.core.debug.disassemble;
import ghidra.app.context.ListingActionContext;
import ghidra.app.plugin.core.assembler.AbstractAssemblePatchDialog;
import ghidra.app.plugin.core.assembler.AssemblePatchAction;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.Instruction;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.program.TraceProgramView;
public abstract class AbstractTraceAssemblePatchAction extends AssemblePatchAction {
public AbstractTraceAssemblePatchAction(DebuggerDisassemblerPlugin plugin, String name) {
super(plugin, name);
}
protected abstract TracePlatform getPlatform(TraceProgramView view, Address entry);
@Override
protected AbstractAssemblePatchDialog<?> newDialog(ListingActionContext ctx) {
if (!(ctx.getProgram() instanceof TraceProgramView view)) {
return null;
}
Address entry = ctx.getAddress();
Instruction ins = view.getListing().getInstructionContaining(entry);
RegisterValue initialContext = getInitialContext(ins, view, entry);
return new TraceAssemblePatchDialog(plugin.getTool(), ctx.getNavigatable(),
getPlatform(view, entry), view, entry, initialContext);
}
}

View File

@@ -37,6 +37,31 @@ import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor;
public abstract class AbstractTracePatchInstructionAction extends PatchInstructionAction {
protected class TraceAssemblyDualTextField extends AssemblyDualTextField {
protected class TraceAssemblyDualTextAutocompletionModel
extends AssemblyDualTextAutocompletionModel {
AssemblyPatternBlock ctx = null;
@Override
protected AssemblyPatternBlock getContext() {
return ctx;
}
@Override
public void setAddress(Address address) {
super.setAddress(address);
RegisterValue rv = getContextValue(getCodeUnit());
ctx = rv == null ? AssemblyPatternBlock.nop()
: AssemblyPatternBlock.fromRegisterValue(rv).fillMask();
}
}
@Override
protected AssemblyDualTextAutocompletionModel newAutocompletionModel() {
return new TraceAssemblyDualTextAutocompletionModel();
}
}
protected final DebuggerDisassemblerPlugin plugin;
public AbstractTracePatchInstructionAction(DebuggerDisassemblerPlugin plugin, String name) {
@@ -57,22 +82,7 @@ public abstract class AbstractTracePatchInstructionAction extends PatchInstructi
@Override
protected AssemblyDualTextField newAssemblyDualTextField() {
return new AssemblyDualTextField() {
AssemblyPatternBlock ctx = null;
@Override
protected AssemblyPatternBlock getContext() {
return ctx;
}
@Override
public void setAddress(Address address) {
super.setAddress(address);
RegisterValue rv = getContextValue(getCodeUnit());
ctx = rv == null ? AssemblyPatternBlock.nop()
: AssemblyPatternBlock.fromRegisterValue(rv).fillMask();
}
};
return new TraceAssemblyDualTextField();
}
@Override
@@ -150,7 +160,14 @@ public abstract class AbstractTracePatchInstructionAction extends PatchInstructi
}
AddressSetView set = new AddressSet(address, address.add(data.length - 1));
TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, address, set);
TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, address, set) {
@Override
public boolean applyTo(TraceProgramView view, TaskMonitor monitor) {
boolean result = super.applyTo(view, monitor);
doGoToNext(data.length);
return result;
}
};
if (contextValue != null) {
dis.setInitialContext(contextValue);
}

View File

@@ -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.app.plugin.core.debug.disassemble;
import docking.ActionContext;
import ghidra.app.context.ListingActionContext;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Instruction;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.TraceInstruction;
import ghidra.trace.model.program.TraceProgramView;
public class CurrentPlatformTraceAssemblePatchAction extends AbstractTraceAssemblePatchAction {
public CurrentPlatformTraceAssemblePatchAction(DebuggerDisassemblerPlugin plugin) {
super(plugin, "Assemble");
}
@Override
protected boolean isApplicableToContext(ActionContext context) {
return super.isApplicableToContext(context) &&
context instanceof ListingActionContext lac && lac.getNavigatable().isDynamic();
}
@Override
protected TracePlatform getPlatform(TraceProgramView view, Address entry) {
Instruction ins = view.getListing().getInstructionContaining(entry);
if (!(ins instanceof TraceInstruction tins)) { // includes null
return view.getTrace().getPlatformManager().getHostPlatform();
}
return tins.getPlatform();
}
}

View File

@@ -171,6 +171,7 @@ public class DebuggerDisassemblerPlugin extends Plugin implements PopupActionPro
CurrentPlatformTraceDisassembleAction actionDisassemble;
CurrentPlatformTracePatchInstructionAction actionPatchInstruction;
TracePatchDataAction actionPatchData;
CurrentPlatformTraceAssemblePatchAction actionAssemblePatch;
public DebuggerDisassemblerPlugin(PluginTool tool) {
super(tool);
@@ -187,10 +188,12 @@ public class DebuggerDisassemblerPlugin extends Plugin implements PopupActionPro
actionDisassemble = new CurrentPlatformTraceDisassembleAction(this);
actionPatchInstruction = new CurrentPlatformTracePatchInstructionAction(this);
actionPatchData = new TracePatchDataAction(this);
actionAssemblePatch = new CurrentPlatformTraceAssemblePatchAction(this);
tool.addAction(actionDisassemble);
tool.addAction(actionPatchInstruction);
tool.addAction(actionPatchData);
tool.addAction(actionAssemblePatch);
}
/**
@@ -222,6 +225,7 @@ public class DebuggerDisassemblerPlugin extends Plugin implements PopupActionPro
for (LanguageID langID : getAlternativeLanguageIDs(platform.getLanguage())) {
result.add(new FixedPlatformTraceDisassembleAction(this, langID, platform));
result.add(new FixedPlatformTracePatchInstructionAction(this, langID, platform));
result.add(new FixedPlatformTraceAssemblePatchAction(this, langID, platform));
}
}

View File

@@ -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.app.plugin.core.debug.disassemble;
import docking.action.MenuData;
import ghidra.app.plugin.core.assembler.AbstractPatchAction;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.TraceInstruction;
import ghidra.trace.model.program.TraceProgramView;
public class FixedPlatformTraceAssemblePatchAction extends AbstractTraceAssemblePatchAction {
private final LanguageID altLangID;
private final TracePlatform platform;
public FixedPlatformTraceAssemblePatchAction(DebuggerDisassemblerPlugin plugin,
LanguageID altLangID, TracePlatform platform) {
this.altLangID = altLangID;
this.platform = platform;
super(plugin, "Assemble using " + altLangID);
}
@Override
protected MenuData createMenuData(String name) {
MenuData menuData =
new MenuData(new String[] { "Assemble using...", altLangID.toString() });
menuData.setParentMenuGroup(AbstractPatchAction.MENU_GROUP);
return menuData;
}
@Override
protected TracePlatform getPlatform(TraceProgramView view, Address entry) {
return platform;
}
@Override
protected RegisterValue getInitialContext(Instruction ins, Program program, Address entry) {
Language language = ins instanceof TraceInstruction tins
? tins.getLanguage()
: program.getLanguage();
return DebuggerDisassemblerPlugin.deriveAlternativeDefaultContext(language, altLangID,
entry);
}
}

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,6 +16,7 @@
package ghidra.app.plugin.core.debug.disassemble;
import docking.action.MenuData;
import ghidra.app.plugin.core.assembler.AbstractPatchAction;
import ghidra.program.model.lang.LanguageID;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.program.TraceProgramView;
@@ -26,13 +27,13 @@ public class FixedPlatformTraceDisassembleAction extends AbstractTraceDisassembl
public FixedPlatformTraceDisassembleAction(DebuggerDisassemblerPlugin plugin,
LanguageID altLangID, TracePlatform platform) {
super(plugin, "Disassemble Trace as " + altLangID);
this.altLangID = altLangID;
this.platform = platform;
super(plugin, "Disassemble Trace as " + altLangID);
// TODO: Human-readable description?
setPopupMenuData(
new MenuData(new String[] { "Disassemble as " + altLangID }, "Disassembly"));
MenuData menuData = new MenuData(new String[] { "Disassemble as", altLangID.toString() });
menuData.setParentMenuGroup(AbstractPatchAction.MENU_GROUP);
setPopupMenuData(menuData);
}
@Override

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,6 +15,8 @@
*/
package ghidra.app.plugin.core.debug.disassemble;
import docking.action.MenuData;
import ghidra.app.plugin.core.assembler.AbstractPatchAction;
import ghidra.program.model.lang.LanguageID;
import ghidra.program.model.listing.CodeUnit;
import ghidra.trace.model.guest.TracePlatform;
@@ -25,11 +27,18 @@ public class FixedPlatformTracePatchInstructionAction extends AbstractTracePatch
public FixedPlatformTracePatchInstructionAction(DebuggerDisassemblerPlugin plugin,
LanguageID altLangID, TracePlatform platform) {
super(plugin, "Patch Instruction using " + altLangID);
setKeyBindingData(null);
this.altLangID = altLangID;
this.platform = platform;
super(plugin, "Patch Instruction using " + altLangID);
setKeyBindingData(null);
}
@Override
protected MenuData createMenuData(String name) {
MenuData menuData =
new MenuData(new String[] { "Patch Instruction using", altLangID.toString() });
menuData.setParentMenuGroup(AbstractPatchAction.MENU_GROUP);
return menuData;
}
@Override

View File

@@ -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.disassemble;
import java.util.List;
import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.assembler.Assembler;
import ghidra.app.plugin.assembler.Assemblers;
import ghidra.app.plugin.core.assembler.AbstractAssemblePatchDialog;
import ghidra.app.plugin.core.assembler.AbstractPatchAssemblyCommand;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.RegisterValue;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.program.TraceProgramView;
public class TraceAssemblePatchDialog extends AbstractAssemblePatchDialog<TraceProgramView> {
private final TracePlatform platform;
protected TraceAssemblePatchDialog(PluginTool tool, Navigatable navigatable,
TracePlatform platform, TraceProgramView program, Address entry,
RegisterValue initialContext) {
this.platform = platform;
super(tool, navigatable, program, entry, initialContext);
}
@Override
protected AbstractPatchAssemblyCommand<TraceProgramView> newPatchCommand(List<String> lines) {
return new TracePatchAssemblyCommand(platform, assembler, lines, entry, initialContext);
}
@Override
protected Language getLanguage() {
return platform.getLanguage();
}
@Override
protected Assembler getAssembler() {
return Assemblers.getAssembler(getLanguage());
}
}

View File

@@ -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.app.plugin.core.debug.disassemble;
import java.util.List;
import ghidra.app.plugin.assembler.Assembler;
import ghidra.app.plugin.core.assembler.AbstractPatchAssemblyCommand;
import ghidra.framework.cmd.Command;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.RegisterValue;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.program.TraceProgramView;
public class TracePatchAssemblyCommand extends AbstractPatchAssemblyCommand<TraceProgramView> {
private final TracePlatform platform;
public TracePatchAssemblyCommand(TracePlatform platform, Assembler asm, List<String> lines,
Address entry, RegisterValue initialContext) {
this.platform = platform;
super(asm, lines, entry, initialContext);
}
public TracePatchAssemblyCommand(TracePlatform platform, Assembler asm, String string,
Address entry, RegisterValue initialContext) {
this.platform = platform;
super(asm, string, entry, initialContext);
}
@Override
protected Command<TraceProgramView> newDisassembleCommand(AddressSetView set,
TraceProgramView program) {
TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, entry, set);
dis.setInitialContext(initialContext);
return dis;
}
}

View File

@@ -62,6 +62,7 @@ public class TracePatchDataAction extends PatchDataAction {
try {
editor.setVariable(address, encoded).get(1, TimeUnit.SECONDS);
doGoToNext(encoded.length);
// Let the trace do everything regarding existing units
return true;
}

View File

@@ -654,8 +654,7 @@ public class StackUnwinderTest extends AbstractGhidraHeadedDebuggerTest {
Language language = asm.getLanguage();
Register regCtx = language.getContextBaseRegister();
Register regT = language.getRegister("T");
RegisterValue rvDefault = new RegisterValue(regCtx,
asm.getContextAt(entry).toBigInteger(regCtx.getNumBytes()));
RegisterValue rvDefault = asm.getContextAt(entry).toRegisterValue(regCtx);
RegisterValue rvThumb = rvDefault.assign(regT, BigInteger.ONE);
AssemblyPatternBlock ctxThumb = AssemblyPatternBlock.fromRegisterValue(rvThumb);