diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java index aa5a38ce71..812a851428 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java @@ -70,6 +70,7 @@ public class SleighLanguage implements Language { private int numSections = 0; // Number of named sections for this language private int alignment = 1; private int defaultPointerWordSize = 1; // Default wordsize to send down with pointer data-types + private OptionalInt maxInstructionLength = OptionalInt.empty(); private SleighLanguageDescription description; private ParallelInstructionLanguageHelper parallelHelper; private SourceFileIndexer indexer; //used to provide source file info for constructors @@ -129,7 +130,8 @@ public class SleighLanguage implements Language { SleighLanguageValidator.validatePspecFile(langDescription.getSpecFile()); - readInitialDescription(); + readInitialDescription(); // process pspec file + // should addressFactory and registers initialization be done at // construction time? // for now we'll assume yes. @@ -154,6 +156,13 @@ public class SleighLanguage implements Language { instructProtoMap = new ConcurrentHashMap<>(); initParallelHelper(); + + int maxLength = + getPropertyAsInt(GhidraLanguagePropertyKeys.MAXIMUM_INSTRUCTION_LENGTH, -1); + if (maxLength > 0) { + maxInstructionLength = OptionalInt.of(maxLength); + } + } private void buildVolatileSymbolAddresses() { @@ -1181,6 +1190,11 @@ public class SleighLanguage implements Language { } } + @Override + public OptionalInt getMaximumInstructionLength() { + return maxInstructionLength; + } + @Override public String getProperty(String key) { return properties.get(key); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoInstruction.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoInstruction.java index 378739306e..5b9cb921e1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoInstruction.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoInstruction.java @@ -22,6 +22,7 @@ package ghidra.app.util; import java.math.BigInteger; import java.util.List; +import java.util.OptionalInt; import ghidra.program.model.address.*; import ghidra.program.model.lang.*; @@ -113,27 +114,31 @@ public class PseudoInstruction extends PseudoCodeUnit implements Instruction, In // NOTE: in certain cases this may not cache enough if slot size was // specified as a minimum and not actual int length = prototype.getLength(); - int extraByteCount = prototype.getDelaySlotByteCount(); - if (prototype.hasNext2Dependency()) { - /** - * NOTE: The notes about problems below apply here, too. We're assuming that the next - * instruction is at most 3 bytes longer than this instruction. Maybe that suffices, but - * be prepared to 1) Add more slack bytes, or 2) Actually parse the next instruction(s). - * The difficulty with option 2 (and why I don't just do it now) is that I need the - * initial context register value for that next instruction. - */ - extraByteCount = Math.max(length, extraByteCount); - } - if (extraByteCount == 1) { - // Assume this is a minimum size and cache enough for one + + int delaySlotByteCount = prototype.getDelaySlotByteCount(); + if (delaySlotByteCount == 1) { + // Assume this is a minimum delay slot size and cache enough for one // more instruction of the same size. - length += length; + delaySlotByteCount = length; } - else { - // NOTE: This may have a problem if delaySlotByteCount is a - // minimum byte count and more bytes are needed for delay slots. - length += extraByteCount; + length += delaySlotByteCount; + + // Factor in optional inst_next2 use + int next2Length = 0; + if (prototype.hasNext2Dependency()) { + Language language = prototype.getLanguage(); + OptionalInt maxNextInstructionLength = language.getMaximumInstructionLength(); + if (maxNextInstructionLength.isPresent()) { + // next instruction length based upon language specified property + next2Length = maxNextInstructionLength.getAsInt(); + } + else { + // next instruction length assumed to be the same as the current instruction + next2Length = length; + } } + length += next2Length; + // Sleigh utilizes 4-byte (int) chunks when evaluating patterns // make sure we have enough bytes to give out for any valid offset // within the instruction diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/GhidraLanguagePropertyKeys.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/GhidraLanguagePropertyKeys.java index 43fde46050..90c64e710c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/GhidraLanguagePropertyKeys.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/GhidraLanguagePropertyKeys.java @@ -15,12 +15,30 @@ */ package ghidra.program.model.lang; +import ghidra.app.util.PseudoInstruction; import ghidra.program.disassemble.Disassembler; public final class GhidraLanguagePropertyKeys { private GhidraLanguagePropertyKeys() { } + /** + * MAXIMUM_INSTRUCTION_LENGTH specifies the maximum instruction length that a language + * may produce, including any delay slots which may be present. This value is used + * by {@link PseudoInstruction} in support of Sleigh {@code inst_next2} processing. If + * not specified a computed estimate is used by each instruction parse. If the + * Sleigh {@code inst_next2} instruction is used with variable length instructions, it + * is highly recommended that this property be specified. + * + * NOTE: It is the language (i.e., {@code slaspec/pspec}) author who needs to manually + * compute this value carefully based upon the largest possible instruction size including + * any delay slot. Ghidra will not validate this value. + * + * NOTE: This property is currently only used by Ghidra when {@code inst_next2} is used + * within the correspond {@code slaspec}. + */ + public static final String MAXIMUM_INSTRUCTION_LENGTH = "maximumInstructionLength"; + /** * CUSTOM_DISASSEMBLER_CLASS is a full class name for a language-specific * disassembler implementation. The specified class must extend the generic diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/Language.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/Language.java index 7bbf4b3734..6c99736d90 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/Language.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/Language.java @@ -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,8 +16,7 @@ package ghidra.program.model.lang; import java.io.IOException; -import java.util.List; -import java.util.Set; +import java.util.*; import ghidra.app.plugin.processors.generic.MemoryBlockDefinition; import ghidra.program.model.address.*; @@ -423,4 +422,15 @@ public interface Language { */ public AddressSetView getRegisterAddresses(); + /** + * Returns an optional value which represents the maximum instruction length that a language + * may produce, including any delay slots which may be present. This value originates from + * an optional language property. This value is primarily intended when considering the + * maximum number of bytes beyond the current instruction and its delay slots which may be + * needed when determining an inst_next2 location for a given instruction. + * + * @return maximum instruction length in bytes if specified. + */ + public OptionalInt getMaximumInstructionLength(); + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/OldLanguage.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/OldLanguage.java index 3de115c640..a6c1961b50 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/OldLanguage.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/OldLanguage.java @@ -767,4 +767,9 @@ class OldLanguage implements Language { public AddressSetView getRegisterAddresses() { return registerMgr.getRegisterAddresses(); } + + @Override + public OptionalInt getMaximumInstructionLength() { + return OptionalInt.empty(); + } }