diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CallDepthChangeInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CallDepthChangeInfo.java index eb42edc861..4e4436eea3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CallDepthChangeInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CallDepthChangeInfo.java @@ -273,8 +273,7 @@ public class CallDepthChangeInfo { PcodeOp[] pcode = instr.getPcode(); Varnode outVarNode = null; - for (int i = 0; i < pcode.length; i++) { - PcodeOp op = pcode[i]; + for (PcodeOp op : pcode) { Varnode input0 = op.getInput(0); Varnode input1 = op.getInput(1); Varnode output = op.getOutput(); @@ -317,9 +316,10 @@ public class CallDepthChangeInfo { break; case PcodeOp.INT_AND: // Assume this is a stack alignment and do the and if (isStackPointer(input0)) { - if (currentStackDepth != Function.UNKNOWN_STACK_DEPTH_CHANGE) + if (currentStackDepth != Function.UNKNOWN_STACK_DEPTH_CHANGE) { possibleDepthChange = (int) (currentStackDepth & input1.getOffset()) - currentStackDepth; + } outVarNode = output; } else if (input0.equals(outVarNode)) { @@ -327,9 +327,10 @@ public class CallDepthChangeInfo { outVarNode = output; } else if (isStackPointer(input1)) { - if (currentStackDepth != Function.UNKNOWN_STACK_DEPTH_CHANGE) + if (currentStackDepth != Function.UNKNOWN_STACK_DEPTH_CHANGE) { possibleDepthChange = (int) (currentStackDepth & input0.getOffset()) - currentStackDepth; + } outVarNode = output; } else if (input1.equals(outVarNode)) { @@ -385,7 +386,7 @@ public class CallDepthChangeInfo { // TODO: Modify return by normal stack shift.... if (flowType.isTerminal()) { - depthChange -= program.getCompilerSpec().getCallStackShift(); + depthChange -= program.getCompilerSpec().getDefaultCallingConvention().getStackshift(); } // if the current stack depth is still bad, don't return a depth change. @@ -419,8 +420,9 @@ public class CallDepthChangeInfo { * @return */ private int getDefaultStackDepthChange(int depth) { - int callStackMod = program.getCompilerSpec().getCallStackMod(); - int callStackShift = program.getCompilerSpec().getCallStackShift(); + PrototypeModel defaultModel = program.getCompilerSpec().getDefaultCallingConvention(); + int callStackMod = defaultModel.getExtrapop(); + int callStackShift = defaultModel.getStackshift(); if (callStackMod != PrototypeModel.UNKNOWN_EXTRAPOP && callStackShift >= 0) { return callStackShift - callStackMod; } @@ -578,8 +580,8 @@ public class CallDepthChangeInfo { FlowType flow = instr.getFlowType(); if (!flow.isCall()) { Address[] flows = instr.getFlows(); - for (int i = 0; i < flows.length; i++) { - st.push(flows[i]); + for (Address flow2 : flows) { + st.push(flow2); st.push(new Integer(stackPointerDepth)); st.push(stackOK); } @@ -653,7 +655,7 @@ public class CallDepthChangeInfo { return; } - int purge = (short) program.getCompilerSpec().getCallStackMod(); + int purge = (short) program.getCompilerSpec().getDefaultCallingConvention().getExtrapop(); final boolean possiblePurge = purge == -1 || purge > 3200 || purge < -3200; // follow all flows building up context @@ -948,8 +950,8 @@ public class CallDepthChangeInfo { FlowType flow = instr.getFlowType(); if (!flow.isCall()) { Address[] flows = instr.getFlows(); - for (int i = 0; i < flows.length; i++) { - st.push(flows[i]); + for (Address flow2 : flows) { + st.push(flow2); st.push(new Integer(stackPointerDepth)); st.push(stackOK); } @@ -1027,11 +1029,11 @@ public class CallDepthChangeInfo { } // try to find a call destination that the stack frame is known - for (int i = 0; i < flows.length; i++) { - if (flows[i] == null) { + for (Address flow : flows) { + if (flow == null) { continue; } - Function func = program.getListing().getFunctionAt(flows[i]); + Function func = program.getListing().getFunctionAt(flow); if (func != null) { int purge = func.getStackPurgeSize(); if (func.isStackPurgeSizeValid() && purge != Function.UNKNOWN_STACK_DEPTH_CHANGE && diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/FunctionPurgeAnalysisCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/FunctionPurgeAnalysisCmd.java index 605a427fcd..58bb7a2ba7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/FunctionPurgeAnalysisCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/FunctionPurgeAnalysisCmd.java @@ -17,15 +17,16 @@ package ghidra.app.cmd.function; import ghidra.framework.cmd.BackgroundCommand; import ghidra.framework.model.DomainObject; -import ghidra.program.model.address.AddressSet; -import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.address.*; import ghidra.program.model.lang.Processor; +import ghidra.program.model.lang.PrototypeModel; import ghidra.program.model.listing.*; +import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.scalar.Scalar; -import ghidra.program.model.symbol.FlowType; -import ghidra.program.model.symbol.Reference; +import ghidra.program.model.symbol.*; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; +import ghidra.util.exception.InvalidInputException; import ghidra.util.task.TaskMonitor; /** @@ -34,13 +35,17 @@ import ghidra.util.task.TaskMonitor; public class FunctionPurgeAnalysisCmd extends BackgroundCommand { private AddressSetView entryPoints; private Program program; + private PrototypeModel[] nearFarModels = null; + + private static final int STDCALL_FAR = 0; + private static final int CDECL_FAR = 1; + private static final int STDCALL_NEAR = 2; + private static final int CDECL_NEAR = 3; /** * Constructs a new command for analyzing the Stack. * @param entries and address set indicating the entry points of functions that have * stacks to be analyzed. - * @param forceProcessing flag to force processing of stack references even if the stack - * has already been defined. */ public FunctionPurgeAnalysisCmd(AddressSetView entries) { super("Compute Function Purge", true, true, false); @@ -56,12 +61,16 @@ public class FunctionPurgeAnalysisCmd extends BackgroundCommand { program = (Program) obj; Processor processor = program.getLanguage().getProcessor(); - if (program.getLanguage().getDefaultSpace().getSize() > 32 || + AddressSpace defaultSpace = program.getLanguage().getDefaultSpace(); + if (defaultSpace.getSize() > 32 || !processor.equals(Processor.findOrPossiblyCreateProcessor("x86"))) { Msg.error(this, "Unsupported operation for language " + program.getLanguage().getLanguageID()); return false; } + if (defaultSpace instanceof SegmentedAddressSpace) { // For 16-bit x86, prepare to establish near/fear calling convention models + setupNearFarModels(); + } AddressSetView set = entryPoints; @@ -95,6 +104,52 @@ public class FunctionPurgeAnalysisCmd extends BackgroundCommand { return true; } + /** + * For x86 16-bit find the models stdcallnear, stdcallfar, cdeclnear, and cdeclfar so they can + * be applied at the same time function purge is set. + */ + private void setupNearFarModels() { + int countModels = 0; + nearFarModels = new PrototypeModel[4]; + nearFarModels[0] = null; + nearFarModels[1] = null; + nearFarModels[2] = null; + nearFarModels[3] = null; + PrototypeModel[] models = program.getCompilerSpec().getCallingConventions(); + for (PrototypeModel model : models) { + if (model.isMerged()) { + continue; + } + int pos = -1; + if (model.getStackshift() == 4) { + if (model.getExtrapop() == PrototypeModel.UNKNOWN_EXTRAPOP) { + pos = STDCALL_FAR; + } + else if (model.getExtrapop() == 4) { + pos = CDECL_FAR; + } + } + else if (model.getStackshift() == 2) { + if (model.getExtrapop() == PrototypeModel.UNKNOWN_EXTRAPOP) { + pos = STDCALL_NEAR; + } + else if (model.getExtrapop() == 2) { + pos = CDECL_NEAR; + } + } + if (pos >= 0) { + if (nearFarModels[pos] == null) { + nearFarModels[pos] = model; + countModels += 1; + } + } + } + if (countModels < 4) { + Msg.warn(this, + "FunctionPurgeAnalysis is missing full range of near/far prototype models"); + } + } + /** * Analyze a function to build a stack frame based on stack references. * @param function function to be analyzed @@ -104,39 +159,120 @@ public class FunctionPurgeAnalysisCmd extends BackgroundCommand { */ private void analyzeFunction(Function function, TaskMonitor monitor) throws CancelledException { - int purge = -1; - - if (function != null) { - purge = function.getStackPurgeSize(); + if (function == null) { + return; } + int purge = function.getStackPurgeSize(); if (purge == -1 || purge > 128 || purge < -128) { - purge = locatePurgeReturn(program, function, monitor); - // if couldn't find it, don't set it! - if (purge != -1) { - function.setStackPurgeSize(purge); + Instruction purgeInstruction = locatePurgeInstruction(function, monitor); + if (purgeInstruction != null) { + purge = getPurgeValue(purgeInstruction); + // if couldn't find it, don't set it! + if (purge != -1) { + function.setStackPurgeSize(purge); + } + setPrototypeModel(function, purgeInstruction); } } } - private int locatePurgeReturn(Program program, Function func, TaskMonitor monitor) { - AddressSetView body = func.getBody(); + private void setPrototypeModel(Function function, Instruction purgeInstruction) { + if (nearFarModels == null) { + return; + } + if (purgeInstruction.getFlowType().isCall()) { + return; + } + if (function.getSignatureSource() != SourceType.DEFAULT) { + return; + } + PrototypeModel model = null; + try { + byte val = purgeInstruction.getBytes()[0]; + if (val == (byte) 0xc3) { + model = nearFarModels[CDECL_NEAR]; + } + else if (val == (byte) 0xcb) { + model = nearFarModels[CDECL_FAR]; + } + else if (val == (byte) 0xc2) { + model = nearFarModels[STDCALL_NEAR]; + } + else if (val == (byte) 0xca) { + model = nearFarModels[STDCALL_FAR]; + } + } + catch (MemoryAccessException e) { + return; + } + if (model == null) { + return; + } + try { + function.setCallingConvention(model.getName()); + } + catch (InvalidInputException e) { + // Ignore if we can't change it + } + } - int returnPurge = findReturnPurge(program, body); - if (returnPurge != -1) { - return returnPurge; + private Instruction locatePurgeInstruction(Function func, TaskMonitor monitor) { + AddressSetView body = func.getBody(); + Instruction purgeInstruction; + + purgeInstruction = findPurgeInstruction(body); + if (purgeInstruction != null) { + return purgeInstruction; } // look harder, maybe something wrong with body, compute with flow. body = CreateFunctionCmd.getFunctionBody(program, func.getEntryPoint(), monitor); - returnPurge = findReturnPurge(program, body); - - return returnPurge; + return findPurgeInstruction(body); } - private int findReturnPurge(Program program, AddressSetView body) { - int tempPurge; + /** + * Given a terminating instruction, discover the purge value encoded in it + * @param instr is the terminating instruction + * @return the purge value (or -1 if a value can't be found) + */ + private int getPurgeValue(Instruction instr) { + if (instr.getFlowType().isCall()) { + // is an override call-return, terminal/call + // find a reference to a function, and take it's purge + Reference[] referencesFrom = instr.getReferencesFrom(); + for (Reference reference : referencesFrom) { + if (reference.getReferenceType().isFlow()) { + Function functionAt = + program.getFunctionManager().getFunctionAt(reference.getToAddress()); + // don't take the purge of a non-returning function + if (functionAt != null && !functionAt.hasNoReturn()) { + return functionAt.getStackPurgeSize(); + } + } + } + } + else { + int tempPurge = 0; + Scalar scalar = instr.getScalar(0); + if (scalar != null) { + tempPurge = (int) scalar.getSignedValue(); + } + return tempPurge; + } + return -1; + } + + /** + * Find a terminating instruction in the given set of addresses with a purge encoded in it. + * This routine prefers a RET instruction, but if none is available, it will use a + * terminating CALL. + * @param body is the set of addresses to look through + * @return a terminating instruction or null + */ + private Instruction findPurgeInstruction(AddressSetView body) { InstructionIterator iter = program.getListing().getInstructions(body, true); int count = 2048; + Instruction backupPurge = null; while (iter.hasNext() && count > 0) { count--; Instruction instr = iter.next(); @@ -144,33 +280,15 @@ public class FunctionPurgeAnalysisCmd extends BackgroundCommand { FlowType ftype = instr.getFlowType(); if (ftype.isTerminal()) { if (instr.getMnemonicString().compareToIgnoreCase("ret") == 0) { - tempPurge = 0; - Scalar scalar = instr.getScalar(0); - if (scalar != null) { - tempPurge = (int) scalar.getSignedValue(); - return tempPurge; - } - return 0; + return instr; } else if (ftype.isCall()) { - // is an override call-return, terminal/call - // find a reference to a function, and take it's purge - Reference[] referencesFrom = instr.getReferencesFrom(); - for (Reference reference : referencesFrom) { - if (reference.getReferenceType().isFlow()) { - Function functionAt = program.getFunctionManager().getFunctionAt( - reference.getToAddress()); - // don't take the purge of a non-returning function - if (functionAt != null && !functionAt.hasNoReturn()) { - return functionAt.getStackPurgeSize(); - } - } - } + backupPurge = instr; // Use as last resort, if we can't find RET } } } - return -1; + return backupPurge; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/FunctionResultStateStackAnalysisCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/FunctionResultStateStackAnalysisCmd.java index 73e998a5b5..045fe5f9a3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/FunctionResultStateStackAnalysisCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/FunctionResultStateStackAnalysisCmd.java @@ -169,8 +169,9 @@ public class FunctionResultStateStackAnalysisCmd extends BackgroundCommand { // Process all the functions identified as needing stack analysis. // The list will have the lowest level functions analyzed first. - int default_extraPop = program.getCompilerSpec().getCallStackMod(); - int default_stackshift = program.getCompilerSpec().getCallStackShift(); + PrototypeModel defaultModel = program.getCompilerSpec().getDefaultCallingConvention(); + int default_extraPop = defaultModel.getExtrapop(); + int default_stackshift = defaultModel.getStackshift(); while (!funcList.isEmpty()) { monitor.checkCanceled(); @@ -256,19 +257,22 @@ public class FunctionResultStateStackAnalysisCmd extends BackgroundCommand { int storageSpaceID, RefType refType, TaskMonitor monitor1) throws CancelledException { - if (instrOpIndex < 0) + if (instrOpIndex < 0) { return; + } Address fromAddr = op.getSeqnum().getTarget(); Instruction instr = listing.getInstructionAt(fromAddr); - if (instr == null) + if (instr == null) { return; + } Address stackAddr = addrFactory.getStackSpace().getAddress(stackOffset); RefType rt = refType; Reference ref = refMgr.getReference(fromAddr, stackAddr, instrOpIndex); if (ref != null) { RefType existingRefType = ref.getReferenceType(); - if (existingRefType == rt) + if (existingRefType == rt) { return; + } if (existingRefType == RefType.READ || existingRefType == RefType.WRITE) { rt = RefType.READ_WRITE; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/NewExecutable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/NewExecutable.java index 5cea4c6384..e95d196494 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/NewExecutable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/NewExecutable.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,13 @@ */ package ghidra.app.util.bin.format.ne; -import generic.continues.*; -import ghidra.app.util.bin.*; -import ghidra.app.util.bin.format.*; -import ghidra.app.util.bin.format.mz.*; +import java.io.IOException; -import java.io.*; +import generic.continues.GenericFactory; +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader; +import ghidra.app.util.bin.format.mz.DOSHeader; +import ghidra.program.model.address.SegmentedAddress; /** * A class to manage loading New Executables (NE). @@ -34,17 +34,20 @@ public class NewExecutable { private WindowsHeader winHeader; /** - * Constructs a new instance of an new executable. - * @param bp the byte provider - * @throws IOException if an I/O error occurs. - */ - public NewExecutable(GenericFactory factory, ByteProvider bp) throws IOException { + * Constructs a new instance of an new executable. + * @param factory is the object factory to bundle with the reader + * @param bp the byte provider + * @param baseAddr the image base of the executable + * @throws IOException if an I/O error occurs. + */ + public NewExecutable(GenericFactory factory, ByteProvider bp, SegmentedAddress baseAddr) + throws IOException { reader = new FactoryBundledWithBinaryReader(factory, bp, true); dosHeader = DOSHeader.createDOSHeader(reader); if (dosHeader.isDosSignature()) { try { - winHeader = new WindowsHeader(reader, (short)dosHeader.e_lfanew()); + winHeader = new WindowsHeader(reader, baseAddr, (short) dosHeader.e_lfanew()); } catch (InvalidWindowsHeaderException e) { } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/SegmentTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/SegmentTable.java index 15f0a278bc..41ad4b8822 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/SegmentTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/SegmentTable.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +17,9 @@ package ghidra.app.util.bin.format.ne; import java.io.IOException; -import ghidra.app.util.bin.format.*; +import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader; +import ghidra.program.model.address.SegmentedAddress; +import ghidra.program.model.address.SegmentedAddressSpace; import ghidra.util.Conv; /** @@ -28,7 +29,8 @@ import ghidra.util.Conv; public class SegmentTable { private Segment [] segments; - SegmentTable(FactoryBundledWithBinaryReader reader, short index, short segmentCount, short shiftAlignCount) throws IOException { + SegmentTable(FactoryBundledWithBinaryReader reader, SegmentedAddress baseAddr, short index, + short segmentCount, short shiftAlignCount) throws IOException { long oldIndex = reader.getPointerIndex(); reader.setPointerIndex(Conv.shortToInt(index)); @@ -39,14 +41,29 @@ public class SegmentTable { segments = new Segment[segmentCountInt]; - int startOffset = 0; + SegmentedAddressSpace space; + int curSegment; + if (baseAddr != null) { + space = (SegmentedAddressSpace) baseAddr.getAddressSpace(); + curSegment = baseAddr.getSegment(); + } + else { + space = null; + curSegment = 0; + } for (int i = 0 ; i < segmentCountInt ; ++i) { - segments[i] = new Segment(reader, shiftAlignCount, startOffset >> 4); + segments[i] = new Segment(reader, shiftAlignCount, curSegment); int size = segments[i].getMinAllocSize() & 0xffff; if (size == 0) { size = 0x10000; } - startOffset = (startOffset + size + 0xf) & ~0xf; + if (space != null) { + SegmentedAddress endAddr = space.getAddress(curSegment, size - 1); + curSegment = space.getNextOpenSegment(endAddr); + } + else { + curSegment += 1; + } } reader.setPointerIndex(oldIndex); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/WindowsHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/WindowsHeader.java index a66f040597..825de14d0f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/WindowsHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/ne/WindowsHeader.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +15,11 @@ */ package ghidra.app.util.bin.format.ne; -import ghidra.app.util.bin.format.*; import java.io.IOException; +import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader; +import ghidra.program.model.address.SegmentedAddress; + /** * A class to represent and parse the * Windows new-style executable (NE) header. @@ -39,20 +40,22 @@ public class WindowsHeader { private NonResidentNameTable nonResNameTable; /** - * Constructor - * @param reader the binary reader - * @param index the index where the windows headers begins - * @throws InvalidWindowsHeaderException if the bytes defined in the binary reader at - * the specified index do not constitute a valid windows header. - */ - public WindowsHeader(FactoryBundledWithBinaryReader reader, short index) throws InvalidWindowsHeaderException, IOException { + * Constructor + * @param reader the binary reader + * @param baseAddr the image base address + * @param index the index where the windows headers begins + * @throws InvalidWindowsHeaderException if the bytes defined in the binary reader at + * the specified index do not constitute a valid windows header. + * @throws IOException for problems reading the header bytes + */ + public WindowsHeader(FactoryBundledWithBinaryReader reader, SegmentedAddress baseAddr, + short index) throws InvalidWindowsHeaderException, IOException { this.infoBlock = new InformationBlock(reader, index); short segTableIndex = (short)(infoBlock.getSegmentTableOffset() + index); this.segTable = new SegmentTable(reader, - segTableIndex, - infoBlock.getSegmentCount(), - infoBlock.getSegmentAlignmentShiftCount()); + baseAddr, segTableIndex, infoBlock.getSegmentCount(), + infoBlock.getSegmentAlignmentShiftCount()); //if resource table offset == resident name table offset, then //we do not have any resources... diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractLibrarySupportLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractLibrarySupportLoader.java index 4dbf12095e..7a62948957 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractLibrarySupportLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractLibrarySupportLoader.java @@ -892,14 +892,14 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader if (expSym.hasNoReturn()) { extFunc.setNoReturn(true); } - int stackShift = program.getCompilerSpec().getCallStackShift(); - if (stackShift == -1) { - stackShift = 0; - } +// TODO: This should not be done at time of import and should be done +// by a late running analyzer (e.g., stack analyzer) if no signature +// has been established +// int stackShift = program.getCompilerSpec().getDefaultCallingConvention().getStackshift(); +// if (stackShift == -1) { +// stackShift = 0; +// } - // TODO: This should not be done at time of import and should be done - // by a late running analyzer (e.g., stack analyzer) if no signature - // has been established // int numParams = expSym.getPurge() / 4; // if (numParams > 0) { // // HACK: assumes specific stack-based x86 convention diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MzLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MzLoader.java index 0712c72c28..65896e1cd9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MzLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MzLoader.java @@ -258,6 +258,11 @@ public class MzLoader extends AbstractLibrarySupportLoader { int dataStart = dos.e_cparhdr() << 4; HashMap segMap = new HashMap(); + SegmentedAddress codeAddress = + space.getAddress(Conv.shortToInt(dos.e_cs()) + csStart, 0); + segMap.put(codeAddress, codeAddress); + codeAddress = space.getAddress(csStart, 0); + segMap.put(codeAddress, codeAddress); // This is there data starts loading int numRelocationEntries = dos.e_crlc(); reader.setPointerIndex(relocationTableOffset); for (int i = 0; i < numRelocationEntries; i++) { @@ -290,17 +295,17 @@ public class MzLoader extends AbstractLibrarySupportLoader { for (int i = 0; i < segStartList.size(); i++) { SegmentedAddress start = (SegmentedAddress) segStartList.get(i); - int readLoc = (int) (start.getOffset() - csStartEffective) + dataStart; + int readLoc = ((start.getSegment() << 4) - csStartEffective) + dataStart; if (readLoc < 0) { Msg.error(this, "Invalid read location " + readLoc); continue; } - byte bytes[] = null; int numBytes = 0; if ((i + 1) < segStartList.size()) { SegmentedAddress end = (SegmentedAddress) segStartList.get(i + 1); - numBytes = (int) end.subtract(start); + int nextLoc = ((end.getSegment() << 4) - csStartEffective) + dataStart; + numBytes = nextLoc - readLoc; } else { // last segment length @@ -317,7 +322,6 @@ public class MzLoader extends AbstractLibrarySupportLoader { numUninitBytes = calcNumBytes - numBytes; } if (numBytes > 0) { - bytes = reader.readByteArray(readLoc, numBytes); MemoryBlockUtils.createInitializedBlock(program, false, "Seg_" + i, start, fileBytes, readLoc, numBytes, "", "mz", true, true, true, log); } @@ -399,6 +403,7 @@ public class MzLoader extends AbstractLibrarySupportLoader { symbolTable.createLabel(addr, ENTRY_NAME, SourceType.IMPORTED); } catch (InvalidInputException e) { + // Just skip if we can't create } symbolTable.addExternalEntryPoint(addr); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/NeLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/NeLoader.java index 60703c818e..59e648fabb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/NeLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/NeLoader.java @@ -51,6 +51,7 @@ public class NeLoader extends AbstractLibrarySupportLoader { private static final String TAB = " "; private static final long MIN_BYTE_LENGTH = 4; + private static final int SEGMENT_START = 0x1000; private ArrayList
entryPointList = new ArrayList<>(); private Comparator comparator = new CallNameComparator(); @@ -65,7 +66,7 @@ public class NeLoader extends AbstractLibrarySupportLoader { if (provider.length() < MIN_BYTE_LENGTH) { return loadSpecs; } - NewExecutable ne = new NewExecutable(RethrowContinuesFactory.INSTANCE, provider); + NewExecutable ne = new NewExecutable(RethrowContinuesFactory.INSTANCE, provider, null); WindowsHeader wh = ne.getWindowsHeader(); if (wh != null) { List results = QueryOpinionService.query(getName(), @@ -99,7 +100,9 @@ public class NeLoader extends AbstractLibrarySupportLoader { // the original bytes. MemoryBlockUtils.createFileBytes(prog, provider, monitor); - NewExecutable ne = new NewExecutable(factory, provider); + SegmentedAddressSpace space = + (SegmentedAddressSpace) prog.getAddressFactory().getDefaultAddressSpace(); + NewExecutable ne = new NewExecutable(factory, provider, space.getAddress(SEGMENT_START, 0)); WindowsHeader wh = ne.getWindowsHeader(); InformationBlock ib = wh.getInformationBlock(); SegmentTable st = wh.getSegmentTable(); @@ -113,8 +116,6 @@ public class NeLoader extends AbstractLibrarySupportLoader { Listing listing = prog.getListing(); SymbolTable symbolTable = prog.getSymbolTable(); Memory memory = prog.getMemory(); - SegmentedAddressSpace space = - (SegmentedAddressSpace) prog.getAddressFactory().getDefaultAddressSpace(); ProgramContext context = prog.getProgramContext(); RelocationTable relocTable = prog.getRelocationTable(); @@ -306,11 +307,6 @@ public class NeLoader extends AbstractLibrarySupportLoader { } } - private int getNextAvailableSegment(Program program) { - Address addr = program.getMemory().getMaxAddress(); - return ((int) addr.getOffset() >> 4) + 1; - } - private void processResourceTable(MessageLog log, Program program, ResourceTable rt, SegmentedAddressSpace space, TaskMonitor monitor) throws IOException { Listing listing = program.getListing(); @@ -326,7 +322,7 @@ public class NeLoader extends AbstractLibrarySupportLoader { Resource[] resources = type.getResources(); for (Resource resource : resources) { - int segidx = getNextAvailableSegment(program); + int segidx = space.getNextOpenSegment(program.getMemory().getMaxAddress()); Address addr = space.getAddress(segidx, 0); try { @@ -417,7 +413,7 @@ public class NeLoader extends AbstractLibrarySupportLoader { for (LengthStringSet name : names) { String[] callnames = getCallNamesForModule(name.getString(), mrt, st, imp); int length = callnames.length * pointerSize; - int segment = getNextAvailableSegment(program); + int segment = space.getNextOpenSegment(program.getMemory().getMaxAddress()); Address start = space.getAddress(segment, 0); if (length > 0) { // This isn't a real block, just place holder addresses, so don't create an initialized block diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java index 2cb1e058ab..0c89c48bec 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java @@ -176,10 +176,10 @@ public class ProgramMemoryUtil { MemoryBlock[] blocks = mem.getBlocks(); MemoryBlock[] tmpBlocks = new MemoryBlock[blocks.length]; int j = 0; - for (int i = 0; i < blocks.length; i++) { - if ((blocks[i].isInitialized() && withBytes) || - (!blocks[i].isInitialized() && !withBytes)) { - tmpBlocks[j++] = blocks[i]; + for (MemoryBlock block : blocks) { + if ((block.isInitialized() && withBytes) || + (!block.isInitialized() && !withBytes)) { + tmpBlocks[j++] = block; } } MemoryBlock[] typeBlocks = new MemoryBlock[j]; @@ -297,7 +297,7 @@ public class ProgramMemoryUtil { } // Just looking for the offset into the segment now, not the whole segment/offset pair - if (addrSize == 20) { + if (toAddress instanceof SegmentedAddress) { SegmentedAddress segAddr = (SegmentedAddress) toAddress; currentSegment = (short) segAddr.getSegment(); } @@ -322,10 +322,10 @@ public class ProgramMemoryUtil { if (toAddress instanceof SegmentedAddress) { short offsetShort = memory.getShort(a); offsetShort &= offsetShort & 0xffff; - SegmentedAddress sega = ((SegmentedAddress) a); - short shortSega = (short) (sega.getSegment()); - shortSega &= shortSega & 0xffff; // this is checking to see if the ref is in the same segment as the toAddr - not sure this is needed anymore + // SegmentedAddress sega = ((SegmentedAddress) a); + // short shortSega = (short) (sega.getSegment()); + // shortSega &= shortSega & 0xffff; // if (offsetShort == shortCurrentOffset) { //*** commenting this out is making it find the instances of 46 01's not the 0a 00's - closer though // check for the case where the reference includes both the segment and offset @@ -441,8 +441,9 @@ public class ProgramMemoryUtil { } for (ReferenceAddressPair rap : directReferenceList) { - if (monitor.isCancelled()) + if (monitor.isCancelled()) { return null; + } Address fromAddr = rap.getSource(); if (!results.contains(fromAddr)) { results.add(fromAddr); @@ -624,18 +625,18 @@ public class ProgramMemoryUtil { byte maskBytes[] = null; MemoryBlock[] blocks = memory.getBlocks(); - for (int i = 0; i < blocks.length; i++) { - if (!blocks[i].isInitialized()) { + for (MemoryBlock block : blocks) { + if (!block.isInitialized()) { continue; } if (memoryRange != null && - !memoryRange.intersects(blocks[i].getStart(), blocks[i].getEnd())) { + !memoryRange.intersects(block.getStart(), block.getEnd())) { // skip blocks which do not correspond to currentSeg continue; } - Address start = blocks[i].getStart(); - Address end = blocks[i].getEnd(); + Address start = block.getStart(); + Address end = block.getEnd(); Address found = null; while (true) { monitor.checkCanceled(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java index 172bf0f20f..39157d281d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java @@ -1705,8 +1705,9 @@ public class SymbolicPropogator { * @return */ private int getDefaultStackDepthChange(Program prog, int depth) { - int callStackMod = prog.getCompilerSpec().getCallStackMod(); - int callStackShift = prog.getCompilerSpec().getCallStackShift(); + PrototypeModel defaultModel = prog.getCompilerSpec().getDefaultCallingConvention(); + int callStackMod = defaultModel.getExtrapop(); + int callStackShift = defaultModel.getStackshift(); if (callStackMod != PrototypeModel.UNKNOWN_EXTRAPOP) { return callStackShift; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/state/ResultsState.java b/Ghidra/Features/Base/src/main/java/ghidra/util/state/ResultsState.java index 999f4042c1..257d4855b8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/state/ResultsState.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/state/ResultsState.java @@ -46,14 +46,17 @@ public class ResultsState { private static final Iterator emptyContextStateIterator = new Iterator() { + @Override public boolean hasNext() { return false; } + @Override public ContextState next() { return null; } + @Override public void remove() { } }; @@ -168,10 +171,12 @@ public class ResultsState { stackGrowsNegative = program.getCompilerSpec().stackGrowsNegative(); Long stackOffset = currentPrototype.getStackParameterOffset(); - if (stackOffset != null) + if (stackOffset != null) { paramBaseStackOffset = stackOffset - currentPrototype.getStackshift(); - else + } + else { paramBaseStackOffset = null; + } todoList.add(new BranchDestination(null, entryPt, entryState)); } @@ -253,9 +258,10 @@ public class ResultsState { currentState = null; } - if (DEBUG) + if (DEBUG) { Msg.debug(this, ">>> At " + nextSeq.getTarget() + "/" + nextSeq.getTime() + " " + instr); + } SequenceNumber lastSeq = flowFrom; @@ -272,9 +278,10 @@ public class ResultsState { if (existingStates.containsKey(flowFrom)) { // TODO: We have processed this flow before // TODO: Should we compare existingState with dest.initialState ? - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Flow ignored - already processed: " + flowFrom + " -> " + pcodeOp.getSeqnum()); + } instr = null; // signal - abort current flow break; } @@ -284,9 +291,10 @@ public class ResultsState { // Re-use existing state where register values match addState(flowFrom, otherEntryState); instr = null; // signal - abort current flow - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Flow combined - similar state: " + flowFrom + " -> " + pcodeOp.getSeqnum()); + } break; } } @@ -385,6 +393,7 @@ public class ResultsState { private static Comparator CONTEXT_STATE_SET_SEQUENCE_COMPARATOR = new Comparator() { + @Override public int compare(Object o1, Object o2) { ContextStateSet set = (ContextStateSet) o1; SequenceNumber seq = (SequenceNumber) o2; @@ -468,14 +477,16 @@ public class ResultsState { private ContextState performInlineCall(Address inlineCallAddress, ContextState currentState, TaskMonitor monitor) throws CancelledException { - if (DEBUG) + if (DEBUG) { Msg.debug(this, "*** Start Inline Call to " + inlineCallAddress + " ***"); + } ResultsState inlineState = new ResultsState(new SequenceNumber(inlineCallAddress, 0), null, currentState, maintainInstructionResults); inlineState.processFunction(monitor); - if (DEBUG) + if (DEBUG) { Msg.debug(this, "*** End Inline Call to " + inlineCallAddress + " ***"); + } // TODO: How should multiple return states be handled ?? @@ -746,9 +757,10 @@ public class ResultsState { if (values[1].isConstant() && values[1].getOffset() == 0) { // TODO: This is a problem, since branch case may never be evaluated! - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Conditional Branch to " + inputs[0].getAddress() + " - Not taken due to false condition value"); + } break; // Fall-through case - assume that a pre-condition is steering the execution } @@ -762,16 +774,18 @@ public class ResultsState { SequenceNumber dest = new SequenceNumber(seq.getTarget(), seq.getTime() + (int) inputs[0].getOffset()); - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Internal " + (pcodeOp.getOpcode() == PcodeOp.CBRANCH ? "Conditional " : "") + "Branch to " + dest); + } todoList.add(new BranchDestination(pcodeOp.getSeqnum(), dest, currentState)); } else if (inputs[0].isAddress()) { - if (DEBUG) + if (DEBUG) { Msg.debug(this, (pcodeOp.getOpcode() == PcodeOp.CBRANCH ? "Conditional " : "") + "Branch to " + inputs[0].getAddress()); + } handleDirectFlow(pcodeOp, inputs[0].getAddress(), currentState, monitor); } else { @@ -786,8 +800,9 @@ public class ResultsState { AddressSpace space = currentState.getEntryPoint().getTarget().getAddressSpace(); Address destAddr = space.getAddress(getUnsignedOffset(values[0], space.getPointerSize())); - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Branch to " + destAddr); + } handleDirectFlow(pcodeOp, destAddr, currentState, monitor); } else if (values[0].isAddress()) { @@ -798,32 +813,36 @@ public class ResultsState { currentState.getEntryPoint().getTarget().getAddressSpace(); Address destAddr = space.getAddress(getUnsignedOffset(brOffset, space.getPointerSize())); - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Indirect Branch to [" + values[0].getAddress() + "] -> " + destAddr); + } handleDirectFlow(pcodeOp, destAddr, currentState, monitor); } else { - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Indirect Branch to [" + values[0].toString(program.getLanguage()) + "]"); + } handleIndirectFlow(pcodeOp, values[0], currentState, monitor); } } else { - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Indirect Branch to [" + values[0].toString(program.getLanguage()) + "]"); + } handleIndirectFlow(pcodeOp, values[0], currentState, monitor); } return false; case PcodeOp.CALL: // A call with absolute address if (inputs[0].isAddress()) { - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Call to " + inputs[0].getAddress()); + } handleCall(pcodeOp, null, inputs[0].getAddress(), currentState, monitor); } else { @@ -839,8 +858,9 @@ public class ResultsState { AddressSpace space = currentState.getEntryPoint().getTarget().getAddressSpace(); Address destAddr = space.getAddress(getUnsignedOffset(values[0], space.getPointerSize())); - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Call to " + destAddr); + } handleCall(pcodeOp, indirectPtr, destAddr, currentState, monitor); } else if (values[0].isAddress()) { @@ -852,23 +872,26 @@ public class ResultsState { currentState.getEntryPoint().getTarget().getAddressSpace(); Address destAddr = space.getAddress(getUnsignedOffset(callOffset, space.getPointerSize())); - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Indirect Call to [" + values[0].getAddress() + "] -> " + destAddr); + } handleCall(pcodeOp, indirectPtr, destAddr, currentState, monitor); } else { - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Indirect Call to [" + values[0].toString(program.getLanguage()) + "]"); + } handleIndirectCall(pcodeOp, indirectPtr, values[0], currentState, monitor); } } else { - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Indirect Call to [" + values[0].toString(program.getLanguage()) + "]"); + } handleIndirectCall(pcodeOp, indirectPtr, values[0], currentState, monitor); } return false; @@ -1737,8 +1760,9 @@ public class ResultsState { BookmarkType.ERROR, "Instruction Expected", "Expected instruction at " + address); return; } - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Disassemble at " + address); + } DisassembleCommand cmd = new DisassembleCommand(address, null, true); cmd.applyTo(program, monitor); monitor.checkCanceled(); @@ -1772,8 +1796,9 @@ public class ResultsState { return; } if (addRegister(reg, registersModified)) { - if (DEBUG) + if (DEBUG) { Msg.debug(this, "MODIFIED: " + reg + " = " + value); + } } else { Msg.debug(this, "SET: " + output + " = " + value); @@ -2068,10 +2093,11 @@ public class ResultsState { } if (func == null) { - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Function not found at " + indirectPtr + " indirectly called from " + pcodeOp.getSeqnum().getTarget() + " - call affects unknown"); + } return; } @@ -2104,9 +2130,10 @@ public class ResultsState { } if (func == null) { - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Function not found at " + destAddr + " called from " + pcodeOp.getSeqnum().getTarget() + " - call affects unknown"); + } return; } @@ -2133,10 +2160,11 @@ public class ResultsState { // Must invalidate return varnode DataType returnType = null; if (func == null) { - if (DEBUG) + if (DEBUG) { Msg.debug(this, "No function at " + destAddr + " called from " + calledFrom.getTarget() + " - default return/affects assumed"); + } } else { returnType = func.getReturnType(); @@ -2147,8 +2175,9 @@ public class ResultsState { VariableStorage retStorage = callingConvention.getReturnLocation(returnType, program); Varnode varnode = null; - if (retStorage.isValid() && (retStorage.getVarnodeCount()==1)) + if (retStorage.isValid() && (retStorage.getVarnodeCount()==1)) { varnode = retStorage.getFirstVarnode(); + } if (varnode != null) { // invalidate stored value currentState.store(varnode, getInvalidatedVarnode(calledFrom, varnode)); @@ -2183,9 +2212,10 @@ public class ResultsState { if (purge == Function.UNKNOWN_STACK_DEPTH_CHANGE || purge == Function.INVALID_STACK_DEPTH_CHANGE) { String name = func != null ? func.getName() : ("at " + destAddr); - if (DEBUG) + if (DEBUG) { Msg.debug(this, "Stack purge unknown for function " + name + " called from " + calledFrom.getTarget() + " - stack pointer invalidated"); + } currentState.store(getStackPointerVarnode(), getInvalidatedVarnode(calledFrom, getStackPointerVarnode())); return; @@ -2255,8 +2285,9 @@ public class ResultsState { * @return */ private static int getDefaultStackDepthChange(Program depthProgram, int depth) { - int callStackMod = depthProgram.getCompilerSpec().getCallStackMod(); - int callStackShift = depthProgram.getCompilerSpec().getCallStackShift(); + PrototypeModel defaultModel = depthProgram.getCompilerSpec().getDefaultCallingConvention(); + int callStackMod = defaultModel.getExtrapop(); + int callStackShift = defaultModel.getStackshift(); if (callStackMod != PrototypeModel.UNKNOWN_EXTRAPOP && callStackShift >= 0) { return callStackShift - callStackMod; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc index 36fa643f91..ebd9219e83 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc @@ -962,32 +962,35 @@ void Architecture::parseProcessorConfig(DocumentStorage &store) List::const_iterator iter; for(iter=list.begin();iter!=list.end();++iter) { - if ((*iter)->getName() == "programcounter") { + const string &elname( (*iter)->getName() ); + if (elname == "programcounter") { } - else if ((*iter)->getName() == "volatile") + else if (elname == "volatile") parseVolatile(*iter); - else if ((*iter)->getName() == "incidentalcopy") + else if (elname == "incidentalcopy") parseIncidentalCopy(*iter); - else if ((*iter)->getName() == "context_data") + else if (elname == "context_data") context->restoreFromSpec(*iter,this); - else if ((*iter)->getName() == "jumpassist") + else if (elname == "jumpassist") userops.parseJumpAssist(*iter, this); - else if ((*iter)->getName() == "register_data") { + else if (elname == "segmentop") + userops.parseSegmentOp(*iter,this); + else if (elname == "register_data") { } - else if ((*iter)->getName() == "segmented_address") { + else if (elname == "segmented_address") { } - else if ((*iter)->getName() == "default_symbols") { + else if (elname == "default_symbols") { } - else if ((*iter)->getName() == "default_memory_blocks") { + else if (elname == "default_memory_blocks") { } - else if ((*iter)->getName() == "address_shift_amount") { + else if (elname == "address_shift_amount") { } - else if ((*iter)->getName() == "properties") { + else if (elname == "properties") { } - else if ((*iter)->getName() == "data_space") { + else if (elname == "data_space") { } else - throw LowlevelError("Unknown element in : "+(*iter)->getName()); + throw LowlevelError("Unknown element in : "+elname); } } @@ -1135,7 +1138,7 @@ void Architecture::init(DocumentStorage &store) fillinReadOnlyFromLoader(); } -Address SegmentedResolver::resolve(uintb val,int4 sz,const Address &point) +Address SegmentedResolver::resolve(uintb val,int4 sz,const Address &point,uintb &fullEncoding) { int4 innersz = segop->getInnerSize(); @@ -1145,6 +1148,7 @@ Address SegmentedResolver::resolve(uintb val,int4 sz,const Address &point) // (as with near pointers) if (segop->getResolve().space != (AddrSpace *)0) { uintb base = glb->context->getTrackedValue(segop->getResolve(),point); + fullEncoding = (base << 8 * innersz) + (val & calc_mask(innersz)); vector seginput; seginput.push_back(val); seginput.push_back(base); @@ -1153,6 +1157,7 @@ Address SegmentedResolver::resolve(uintb val,int4 sz,const Address &point) } } else { // For anything else, consider it a "far" pointer + fullEncoding = val; int4 outersz = segop->getBaseSize(); uintb base = (val >> 8*innersz) & calc_mask(outersz); val = val & calc_mask(innersz); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh index 83d2db8c2a..cb361c36bd 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh @@ -284,7 +284,7 @@ public: /// \param sp is the segmented space /// \param sop is the segment operator SegmentedResolver(Architecture *g,AddrSpace *sp,SegmentOp *sop) { glb=g; spc=sp; segop=sop; } - virtual Address resolve(uintb val,int4 sz,const Address &point); + virtual Address resolve(uintb val,int4 sz,const Address &point,uintb &fullEncoding); }; /// The Translate object keeps track of address ranges for which diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/cast.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/cast.cc index c3ede0cfea..cab50a048b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/cast.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/cast.cc @@ -269,8 +269,14 @@ bool CastStrategyC::isSubpieceCast(Datatype *outtype,Datatype *intype,uint4 offs (outmeta!=TYPE_PTR)&& (outmeta!=TYPE_FLOAT)) return false; - if ((inmeta==TYPE_PTR)&&((outmeta!=TYPE_INT) && (outmeta!=TYPE_UINT))) - return false; //other casts don't make sense for pointers + if (inmeta==TYPE_PTR) { + if (outmeta == TYPE_PTR) { + if (outtype->getSize() < intype->getSize()) + return true; // Cast from far pointer to near pointer + } + if ((outmeta!=TYPE_INT) && (outmeta!=TYPE_UINT)) + return false; //other casts don't make sense for pointers + } return true; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index ea10394d27..082f5bf206 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -827,14 +827,14 @@ int4 ActionShadowVar::apply(Funcdata &data) /// \param rampoint will hold the Address of the resolved symbol /// \param data is the function being analyzed /// \return the recovered symbol or NULL -SymbolEntry *ActionConstantPtr::isPointer(AddrSpace *spc,Varnode *vn,PcodeOp *op,Address &rampoint,Funcdata &data) +SymbolEntry *ActionConstantPtr::isPointer(AddrSpace *spc,Varnode *vn,PcodeOp *op,Address &rampoint,uintb &fullEncoding,Funcdata &data) { bool needexacthit; Architecture *glb = data.getArch(); Varnode *outvn; if (vn->getType()->getMetatype() == TYPE_PTR) { // Are we explicitly marked as a pointer - rampoint = glb->resolveConstant(spc,vn->getOffset(),vn->getSize(),op->getAddr()); + rampoint = glb->resolveConstant(spc,vn->getOffset(),vn->getSize(),op->getAddr(),fullEncoding); needexacthit = false; } else { @@ -881,7 +881,7 @@ SymbolEntry *ActionConstantPtr::isPointer(AddrSpace *spc,Varnode *vn,PcodeOp *op // Check if the constant looks like a single bit or mask if (bit_transitions(vn->getOffset(),vn->getSize()) < 3) return (SymbolEntry *)0; - rampoint = glb->resolveConstant(spc,vn->getOffset(),vn->getSize(),op->getAddr()); + rampoint = glb->resolveConstant(spc,vn->getOffset(),vn->getSize(),op->getAddr(),fullEncoding); } if (rampoint.isInvalid()) return (SymbolEntry *)0; @@ -943,10 +943,11 @@ int4 ActionConstantPtr::apply(Funcdata &data) else if ((opc == CPUI_PTRSUB)||(opc==CPUI_PTRADD)) continue; Address rampoint; - entry = isPointer(rspc,vn,op,rampoint,data); + uintb fullEncoding; + entry = isPointer(rspc,vn,op,rampoint,fullEncoding,data); vn->setPtrCheck(); // Set check flag AFTER searching for symbol if (entry != (SymbolEntry *)0) { - data.spacebaseConstant(op,slot,entry,rampoint,vn->getOffset(),vn->getSize()); + data.spacebaseConstant(op,slot,entry,rampoint,fullEncoding,vn->getSize()); if ((opc == CPUI_INT_ADD)&&(slot==1)) data.opSwapInput(op,0,1); count += 1; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index 5c86fc30e2..56e4dc2dd1 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -162,7 +162,7 @@ public: /// \brief Check for constants, with pointer type, that correspond to global symbols class ActionConstantPtr : public Action { int4 localcount; ///< Number of passes made for this function - static SymbolEntry *isPointer(AddrSpace *spc,Varnode *vn,PcodeOp *op,Address &rampoint,Funcdata &data); + static SymbolEntry *isPointer(AddrSpace *spc,Varnode *vn,PcodeOp *op,Address &rampoint,uintb &fullEncoding,Funcdata &data); public: ActionConstantPtr(const string &g) : Action(0,"constantptr",g) {} ///< Constructor virtual void reset(Funcdata &data) { localcount = 0; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc index 234a581244..1d0064ccf6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc @@ -333,7 +333,7 @@ void Funcdata::spacebaseConstant(PcodeOp *op,int4 slot,SymbolEntry *entry,const outvn = outvn2; opInsertBefore(extraop,op); } - if (sz != origsize) { // There is a change in size from address -> varnode + if (sz < origsize) { // The new constant is smaller than the original varnode, so we extend it PcodeOp *zextop = newOp(1,op->getAddr()); Varnode *outvn2 = newUniqueOut(origsize,zextop); opSetOpcode(zextop,CPUI_INT_ZEXT); // Create an extension to get back to original varnode size @@ -341,6 +341,15 @@ void Funcdata::spacebaseConstant(PcodeOp *op,int4 slot,SymbolEntry *entry,const opInsertBefore(zextop,op); outvn = outvn2; } + else if (origsize < sz) { // The new constant is bigger than the original varnode, truncate it + PcodeOp *subOp = newOp(2,op->getAddr()); + Varnode *outvn3 = newUniqueOut(origsize,subOp); + opSetOpcode(subOp,CPUI_SUBPIECE); + opSetInput(subOp,outvn,0); + opSetInput(subOp,newConstant(4, 0), 1); // Take least significant piece + opInsertBefore(subOp,op); + outvn = outvn3; + } opSetInput(op,outvn,slot); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc index 3d6e4a6d9f..2b233c46f8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc @@ -1403,7 +1403,8 @@ bool PrintC::pushPtrCharConstant(uintb val,const TypePointer *ct,const Varnode * { if (val==0) return false; AddrSpace *spc = glb->getDefaultSpace(); - Address stringaddr = glb->resolveConstant(spc,val,ct->getSize(),op->getAddr()); + uintb fullEncoding; + Address stringaddr = glb->resolveConstant(spc,val,ct->getSize(),op->getAddr(),fullEncoding); if (stringaddr.isInvalid()) return false; if (!glb->symboltab->getGlobalScope()->isReadOnly(stringaddr,1,Address())) return false; // Check that string location is readonly diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc index 209b1b776a..98582c96c2 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc @@ -485,15 +485,27 @@ AddrSpace *AddrSpaceManager::getSpaceByShortcut(char sc) const return (*iter).second; } -Address AddrSpaceManager::resolveConstant(AddrSpace *spc,uintb val,int4 sz,const Address &point) const +/// \brief Resolve a native constant into an Address +/// +/// If there is a special resolver for the AddrSpace, this is invoked, otherwise +/// basic wordsize conversion and wrapping is performed. If the address encoding is +/// partial (as in a \e near pointer) and the full encoding can be recovered, it is passed back. +/// \param spc is the space to generate the address from +/// \param val is the constant encoding of the address +/// \param sz is the size of the constant encoding +/// \param point is the context address (for recovering full encoding info if necessary) +/// \param fullEncoding is used to pass back the recovered full encoding of the pointer +/// \return the formal Address associated with the encoding +Address AddrSpaceManager::resolveConstant(AddrSpace *spc,uintb val,int4 sz,const Address &point,uintb &fullEncoding) const { int4 ind = spc->getIndex(); if (ind < resolvelist.size()) { AddressResolver *resolve = resolvelist[ind]; if (resolve != (AddressResolver *)0) - return resolve->resolve(val,sz,point); + return resolve->resolve(val,sz,point,fullEncoding); } + fullEncoding = val; val = AddrSpace::addressToByte(val,spc->getWordSize()); val = spc->wrapOffset(val); return Address(spc,val); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh index 6fc4b0db28..b0f4c48105 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh @@ -147,8 +147,9 @@ public: /// \param val is constant to be resolved to an address /// \param sz is the size of \e val in context. /// \param point is the address at which this constant is being used + /// \param fullEncoding is used to hold the full pointer encoding if \b val is a partial encoding /// \return the resolved Address - virtual Address resolve(uintb val,int4 sz,const Address &point)=0; + virtual Address resolve(uintb val,int4 sz,const Address &point,uintb &fullEncoding)=0; }; /// \brief A virtual space \e stack space @@ -249,7 +250,7 @@ public: AddrSpace *getConstantSpace(void) const; ///< Get the constant space Address getConstant(uintb val) const; ///< Get a constant encoded as an Address Address createConstFromSpace(AddrSpace *spc) const; ///< Create a constant address encoding an address space - Address resolveConstant(AddrSpace *spc,uintb val,int4 sz,const Address &point) const; ///< Resolve native constant to address + Address resolveConstant(AddrSpace *spc,uintb val,int4 sz,const Address &point,uintb &fullEncoding) const; int4 numSpaces(void) const; ///< Get the number of address spaces for this processor AddrSpace *getSpace(int4 i) const; ///< Get an address space via its index AddrSpace *getNextSpaceInOrder(AddrSpace *spc) const; ///< Get the next \e contiguous address space diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index d575d55153..4e511230a4 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -1124,13 +1124,17 @@ Datatype *TypeSpacebase::getSubType(uintb off,uintb *newoff) const { Scope *scope = getMap(); - // uintb unoff = (uintb)(intb)off; // Make sure this is a sign-extension - Address addr(spaceid,spaceid->wrapOffset(off)); + off = AddrSpace::byteToAddress(off, spaceid->getWordSize()); // Convert from byte offset to address unit + // It should always be the case that given offset represents a full encoding of the + // pointer, so the point of context is unused + Address nullPoint; + uintb fullEncoding; + Address addr = glb->resolveConstant(spaceid, off, spaceid->getAddrSize(), nullPoint, fullEncoding); SymbolEntry *smallest; // Assume symbol being referenced is address tied, // so we use empty usepoint - smallest = scope->queryContainer(addr,1,Address()); + smallest = scope->queryContainer(addr,1,nullPoint); if (smallest == (SymbolEntry *)0) { *newoff = 0; @@ -1167,7 +1171,8 @@ int4 TypeSpacebase::compareDependency(const Datatype &op) const Address TypeSpacebase::getAddress(uintb off,int4 sz,const Address &point) const { - return glb->resolveConstant(spaceid,off,sz,point); + uintb fullEncoding; + return glb->resolveConstant(spaceid,off,sz,point,fullEncoding); } void TypeSpacebase::saveXml(ostream &s) const diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc index 4cfb1f4fec..e08b43f037 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc @@ -124,31 +124,6 @@ SegmentOp::SegmentOp(Architecture *g,const string &nm,int4 ind) constresolve.space = (AddrSpace *)0; } -/// \brief Execute a stream of operations (OpFollow) given a starting input value -/// -/// Each operation is performed in turn, with output from the previous becoming -/// input of the next. The final output is returned. -/// \param follow is the ordered set of operations to perform -/// \param input is the constant input for the first operation -/// \return the final constant output -uintb SegmentOp::executeSide(const vector &follow,uintb input) - -{ - for(int4 i=0;i &bindlist) const { @@ -163,7 +138,7 @@ bool SegmentOp::unify(Funcdata &data,PcodeOp *op, if (op->getIn(0)->getOffset() != useropindex) return false; if (op->numInput() != 3) return false; innervn = op->getIn(1); - if (basepresent) { + if (baseinsize != 0) { basevn = op->getIn(1); innervn = op->getIn(2); if (basevn->isConstant()) @@ -181,40 +156,23 @@ bool SegmentOp::unify(Funcdata &data,PcodeOp *op, uintb SegmentOp::execute(const vector &input) const { - uintb base,inner; - - if (basepresent) - base = executeSide(basefollow,input[1]); - else - base = 0; - - inner = executeSide(innerfollow,input[0]); - return (base+inner); + ExecutablePcode *pcodeScript = (ExecutablePcode *)glb->pcodeinjectlib->getPayload(injectId); + return pcodeScript->evaluate(input); } void SegmentOp::restoreXml(const Element *el) { spc = glb->getSpaceByName(el->getAttributeValue("space")); + injectId = -1; baseinsize = 0; innerinsize = 0; bool userdefined = false; - forcesegment = true; supportsfarpointer = false; name = "segment"; // Default name, might be overridden by userop attribute for(int4 i=0;igetNumAttributes();++i) { const string &nm(el->getAttributeName(i)); if (nm == "space") continue; - else if (nm == "baseinsize") { - istringstream s(el->getAttributeValue(i)); - s.unsetf(ios::dec | ios::hex | ios::oct); - s >> baseinsize; - } - else if (nm == "innerinsize") { - istringstream s1(el->getAttributeValue(i)); - s1.unsetf(ios::dec | ios::hex | ios::oct); - s1 >> innerinsize; - } else if (nm == "farpointer") supportsfarpointer = true; else if (nm == "userop") { // Based on existing sleigh op @@ -227,28 +185,17 @@ void SegmentOp::restoreXml(const Element *el) throw LowlevelError("Redefining userop "+name); } } - else if (nm == "force") - forcesegment = xml_readbool(el->getAttributeValue(i)); else throw LowlevelError("Bad segmentop tag attribute: "+nm); } if (!userdefined) throw LowlevelError("Missing userop attribute in segmentop tag"); - basepresent = (baseinsize != 0); const List &list(el->getChildren()); List::const_iterator iter; for(iter=list.begin();iter!=list.end();++iter) { const Element *subel = *iter; - if (subel->getName()=="baseop") { - basefollow.push_back(OpFollow()); - basefollow.back().restoreXml(subel); - } - else if (subel->getName()=="innerop") { - innerfollow.push_back(OpFollow()); - innerfollow.back().restoreXml(subel); - } - else if (subel->getName()=="constresolve") { + if (subel->getName()=="constresolve") { int4 sz; const List &sublist(subel->getChildren()); if (!sublist.empty()) { @@ -260,9 +207,28 @@ void SegmentOp::restoreXml(const Element *el) constresolve.size = sz; } } + else if (subel->getName() == "pcode") { + string nm = name + "_pcode"; + string source = "cspec"; + injectId = glb->pcodeinjectlib->restoreXmlInject(source, nm, InjectPayload::EXECUTABLEPCODE_TYPE, subel); + } else throw LowlevelError("Bad segment pattern tag: "+subel->getName()); } + if (injectId < 0) + throw LowlevelError("Missing child in tag"); + InjectPayload *payload = glb->pcodeinjectlib->getPayload(injectId); + if (payload->sizeOutput() != 1) + throw LowlevelError(" child of tag must declare one "); + if (payload->sizeInput() == 1) { + innerinsize = payload->getInput(0).getSize(); + } + else if (payload->sizeInput() == 2) { + baseinsize = payload->getInput(0).getSize(); + innerinsize = payload->getInput(1).getSize(); + } + else + throw LowlevelError(" child of tag must declare one or two tags"); } /// \param g is the Architecture owning this set of jump assist scripts diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/userop.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/userop.hh index 8b5f51fc5d..56521513b5 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/userop.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/userop.hh @@ -198,24 +198,19 @@ struct OpFollow { /// The core routine that looks for the term-tree is unify(). class SegmentOp : public TermPatternOp { AddrSpace *spc; ///< The physical address space into which a segmented pointer points + int4 injectId; ///< Id of InjectPayload that emulates \b this operation int4 baseinsize; ///< The size in bytes of the \e base or \e segment value int4 innerinsize; ///< The size in bytes of the \e near pointer value - bool basepresent; ///< Is \b true is a base value must be present in the raw p-code - bool forcesegment; ///< Is \b true if an exception is thrown when a segment op can't be unified bool supportsfarpointer; ///< Is \b true if the joined pair base:near acts as a \b far pointer - vector basefollow; ///< Sequence of operations performed on the \b base value - vector innerfollow; ///< Sequence of operations performed on the \b near value VarnodeData constresolve; ///< How to resolve constant near pointers - static uintb executeSide(const vector &follow,uintb input); public: SegmentOp(Architecture *g,const string &nm,int4 ind); ///< Constructor AddrSpace *getSpace(void) const { return spc; } ///< Get the address space being pointed to bool hasFarPointerSupport(void) const { return supportsfarpointer; } ///< Return \b true, if \b this op supports far pointers - bool isForced(void) const { return forcesegment; } ///< Return \b true if exceptions are thrown for bad unification int4 getBaseSize(void) const { return baseinsize; } ///< Get size in bytes of the base/segment value int4 getInnerSize(void) const { return innerinsize; } ///< Get size in bytes of the near value const VarnodeData &getResolve(void) const { return constresolve; } ///< Get the default register for resolving indirect segments - virtual int4 getNumVariableTerms(void) const { if (basepresent) return 2; return 1; } + virtual int4 getNumVariableTerms(void) const { if (baseinsize!=0) return 2; return 1; } virtual bool unify(Funcdata &data,PcodeOp *op,vector &bindlist) const; virtual uintb execute(const vector &input) const; virtual void restoreXml(const Element *el); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java index 498f7727fe..5696212774 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java @@ -40,7 +40,7 @@ import ghidra.program.model.symbol.*; import ghidra.util.Msg; import ghidra.util.UndefinedFunction; import ghidra.util.exception.UsrException; -import ghidra.util.task.TaskMonitorAdapter; +import ghidra.util.task.TaskMonitor; import ghidra.util.xml.SpecXmlUtils; import ghidra.util.xml.XmlUtilities; @@ -80,7 +80,7 @@ public class DecompileCallback { listing = program.getListing(); addrfactory = program.getAddressFactory(); dtmanage = dt; - default_extrapop = pcodecompilerspec.getCallStackMod(); + default_extrapop = pcodecompilerspec.getDefaultCallingConvention().getExtrapop(); cpool = null; nativeMessage = null; debug = null; @@ -103,6 +103,7 @@ public class DecompileCallback { * Establish function and debug context for next decompilation * * @param func is the function to be decompiled + * @param entry is the function's entry address * @param dbg is the debugging context (or null) */ public void setFunction(Function func, Address entry, DecompileDebug dbg) { @@ -132,11 +133,11 @@ public class DecompileCallback { } /** - * Used by the decompiler to return a message + * Cache a message returned by the decompiler process * - * @param msg + * @param msg is the message */ - public void setNativeMessage(String msg) { + void setNativeMessage(String msg) { nativeMessage = msg; } @@ -213,7 +214,8 @@ public class DecompileCallback { * Collect any/all comments for the function starting at the indicated * address * - * @param addrstring = string rep of function address + * @param addrstring is the XML rep of function address + * @param types is the string encoding of the comment type flags * @return XML document describing comments */ public String getComments(String addrstring, String types) { @@ -300,13 +302,6 @@ public class DecompileCallback { } - /** - * @param ops pcode ops - * @param fallthruoffset number of bytes after instruction start that pcode - * flow falls into - * - * @return XML document string representing all the pcode - */ /** * Build an XML representation of all the pcode op's a given Instruction is * defined to perform. @@ -315,8 +310,8 @@ public class DecompileCallback { * @param fallthruoffset number of bytes after instruction start that pcode * flow falls into * @param paramshift special instructions for injection use - * @param addrFactory - * @return XML document as string + * @param addrFactory is the address factory for recovering address space names + * @return XML document as string representing all the p-code */ public static String buildInstruction(PcodeOp[] ops, int fallthruoffset, int paramshift, AddressFactory addrFactory) { @@ -446,7 +441,7 @@ public class DecompileCallback { if (pseudoDisassembler == null) { pseudoDisassembler = Disassembler.getDisassembler(program, false, false, false, - TaskMonitorAdapter.DUMMY_MONITOR, msg -> { + TaskMonitor.DUMMY, msg -> { // TODO: Should we log errors? }); } @@ -859,35 +854,11 @@ public class DecompileCallback { return resBuf; } -// /** -// * For registers, return their name. -// * -// * TODO: should look in the scope and return the global register. -// * -// * @param addr -// * @return xml string -// */ -// private String buildRegisterRef(Register reg) { -// Symbol sym = program.getSymbolTable().getPrimarySymbol(reg.getAddress()); -// if (sym==null) -// return null; -// -// boolean readonly = false; -// DataType dt = dtmanage.findInteger(reg.getMinimumByteSize()); -// String symstring = -// HighVariable.buildMappedSymbolXML(dtmanage, reg.getName(), dt, -// reg.getMinimumByteSize(), true, true, readonly, false, -1, -1); -// if (debug != null) -// debug.getType(dt); -//// Namespace namespc = sym.getParentNamespace(); -// return buildResult(reg.getAddress(), null, symstring, null); -// } - /** - * Generate non-data symbol, probably a code label + * Generate description of a non-data symbol, probably a code label * - * @param sym - * @return + * @param sym is the symbol + * @return the XML description */ private String buildLabel(Symbol sym, Address addr) { // TODO: Assume this is not data @@ -973,7 +944,7 @@ public class DecompileCallback { * @param addr The queried address * @param includeDefaultNames true if default parameter names should be * included - * @return + * @return XML string describing the function or the hole */ private String buildFunctionXML(Function func, Address addr, boolean includeDefaultNames) { Address entry = func.getEntryPoint(); @@ -1179,7 +1150,7 @@ public class DecompileCallback { * Return the global object being referred to by addr * * @param addr = Address being queried - * @return + * @return the global object */ private Object lookupSymbol(Address addr) { ExternalReference ref = getExternalReference(addr); diff --git a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg index 1f9384ef2f..d22ad44b56 100644 --- a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg +++ b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg @@ -229,35 +229,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -299,22 +271,6 @@ - - - - INT_ZEXT - INT_LEFT - INT_AND - - - - - - - - - - diff --git a/Ghidra/Framework/SoftwareModeling/data/languages/language_common.rxg b/Ghidra/Framework/SoftwareModeling/data/languages/language_common.rxg index f67d4e4af3..b0766ab4bb 100644 --- a/Ghidra/Framework/SoftwareModeling/data/languages/language_common.rxg +++ b/Ghidra/Framework/SoftwareModeling/data/languages/language_common.rxg @@ -226,6 +226,22 @@ + + + + + + + + + + + + + + + + diff --git a/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg b/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg index 3297f05c27..faf6fd56da 100644 --- a/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg +++ b/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg @@ -43,9 +43,16 @@ + + + + + + + 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 7bde62db52..5bca728f89 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 @@ -79,6 +79,7 @@ public class SleighLanguage implements Language { * Non-null if a space should yes segmented addressing */ String segmentedspace = ""; + String segmentType = ""; AddressSet volatileAddresses; private ContextCache contextcache = null; /** @@ -100,6 +101,13 @@ public class SleighLanguage implements Language { initialize(description); } + private void addAdditionInject(InjectPayloadSleigh payload) { + if (additionalInject == null) { + additionalInject = new ArrayList<>(); + } + additionalInject.add(payload); + } + private void initialize(SleighLanguageDescription langDescription) throws SAXException, IOException, UnknownInstructionException { this.defaultSymbols = new ArrayList<>(); @@ -508,6 +516,10 @@ public class SleighLanguage implements Language { if (nextElement != null) { XmlElement element = parser.start(); // segmented_address element segmentedspace = element.getAttribute("space"); + segmentType = element.getAttribute("type"); + if (segmentType == null) { + segmentType = ""; + } } parser.dispose(); } @@ -586,20 +598,40 @@ public class SleighLanguage implements Language { } InjectPayloadSleigh payload = new InjectPayloadSleigh(subName, InjectPayload.EXECUTABLEPCODE_TYPE, source); - if (additionalInject == null) { - additionalInject = new ArrayList<>(); - } payload.restoreXml(parser); - additionalInject.add(payload); + addAdditionInject(payload); } } + public InjectPayloadSleigh parseSegmentOp(XmlElement el, XmlPullParser parser) { + String name = el.getAttribute("userop"); + if (name == null) { + name = "segment"; + } + name = name + "_pcode"; + String source = "pspec: " + getLanguageID().getIdAsString(); + InjectPayloadSleigh payload = null; + if (parser.peek().isStart()) { + if (parser.peek().getName().equals("pcode")) { + payload = new InjectPayloadSleigh(name, InjectPayload.EXECUTABLEPCODE_TYPE, source); + payload.restoreXml(parser); + } + } + while (parser.peek().isStart()) { + parser.discardSubTree(); + } + if (payload == null) { + throw new SleighException("Missing child for tag"); + } + return payload; + } + private void read(XmlPullParser parser) { Set registerDataSet = new HashSet<>(); XmlElement element = parser.start("processor_spec"); while (!parser.peek().isEnd()) { - element = parser.start("properties", "segmented_address", "programcounter", + element = parser.start("properties", "segmented_address", "segmentop", "programcounter", "data_space", "context_data", "volatile", "jumpassist", "incidentalcopy", "register_data", "default_symbols", "default_memory_blocks"); if (element.getName().equals("properties")) { @@ -769,6 +801,10 @@ public class SleighLanguage implements Language { parser.discardSubTree(); } } + else if (element.getName().equals("segmentop")) { + InjectPayloadSleigh payload = parseSegmentOp(element, parser); + addAdditionInject(payload); + } // get rid of the end tag of whatever we started with at the top of the while parser.end(element); } @@ -896,7 +932,12 @@ public class SleighLanguage implements Language { throw new SleighException( "Segmented space does not support truncation: " + name); } - spc = new SegmentedAddressSpace(name, index); + if (segmentType.equals("protected")) { + spc = new ProtectedAddressSpace(name, index); + } + else { + spc = new SegmentedAddressSpace(name, index); + } } else { if (truncateSpace) { @@ -1378,8 +1419,8 @@ public class SleighLanguage implements Language { SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "index", element.getUnique()); int size = element.getSize(); // Size in bits - if (size == 20) { - // TODO: SegmentedAddressSpace shouldn't really return 20 + if (element instanceof SegmentedAddressSpace) { + // TODO: SegmentedAddressSpace shouldn't really return 21 size = 32; } if (size > 64) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/ProtectedAddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/ProtectedAddressSpace.java new file mode 100644 index 0000000000..7dd5b08c7d --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/ProtectedAddressSpace.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.program.model.address; + +/** + * Address Space for (intel) 16-bit protected mode programs. This space produces + * SegmentedAddress objects whose underlying (flat) offset encodes both the + * segment and the segment offset without losing information. There is no possibility + * of alternate encodings for a single address as with real-mode. + */ +public class ProtectedAddressSpace extends SegmentedAddressSpace { + + private final static int PROTECTEDMODE_SIZE = 32; + private final static int PROTECTEDMODE_OFFSETSIZE = 16; + + private int offsetSize; // Number of bits in the segment offset + private long offsetMask; // Mask for retrieving the segment offset + + public ProtectedAddressSpace(String name, int unique) { + super(name, PROTECTEDMODE_SIZE, unique); + offsetSize = PROTECTEDMODE_OFFSETSIZE; + offsetMask = 1; + offsetMask <<= offsetSize; + offsetMask -= 1; + maxAddress = getUncheckedAddress(maxOffset); + } + + @Override + protected long getFlatOffset(int segment, long offset) { + long res = segment; + res <<= offsetSize; + res += offset; + return res; + } + + @Override + protected int getDefaultSegmentFromFlat(long flat) { + return (int) (flat >>> offsetSize); + } + + @Override + protected long getDefaultOffsetFromFlat(long flat) { + return (flat & offsetMask); + } + + @Override + protected long getOffsetFromFlat(long flat, int segment) { + return (flat & offsetMask); // segment does not affect the offset + } + + @Override + protected SegmentedAddress getAddressInSegment(long flat, int preferredSegment) { + return null; // The segment cannot be changed as the flat explicitly encodes it + } + + @Override + public int getNextOpenSegment(Address addr) { + int res = getDefaultSegmentFromFlat(addr.getOffset()); + // Advance the selector by 8, accounting for the descriptor table bit and the privilege level bits + res = (res + 8) & 0xfff8; + return res; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddress.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddress.java index f4590110a8..4b939baa17 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddress.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddress.java @@ -17,74 +17,56 @@ package ghidra.program.model.address; /** - * Address class for dealing with intel 20 bit segmented addresses. + * Address class for dealing with (intel) segmented addresses. The class itself is agnostic + * about the mapping from segmented encoding to flat address offset, it uses the + * SegmentedAddressSpace to perform this mapping. So the same class can be used to represent + * either a real-mode address or a protected-mode address. The class uses the underlying + * offset field to hold the flat encoding. */ public class SegmentedAddress extends GenericAddress { - private static final long serialVersionUID = 0; - public static final int OFFSET_SIZE = 16; - public static final int SEGMENT_SIZE = 16; - - private final SegmentedAddressSpace addrSpace; - private final int segment; + private final int segment; // The specific segment value associated with this address /** * Constructor for SegmentedAddress. * Offset is not validated against address space. - * @param addrSpace address space for this address - * @param offset offset into the space + * @param addrSpace is the address space for this address + * @param flat is the flat offset into the space */ - SegmentedAddress(long offset, SegmentedAddressSpace addrSpace) { - super(adjustOffset(offset), addrSpace); - this.addrSpace = addrSpace; - if (offset > 0xFFFFF) { - this.segment = 0xFFFF; - } else { - this.segment = (int) ((offset >> 4) & 0xf000); - } + SegmentedAddress(long flat, SegmentedAddressSpace addrSpace) { + super(adjustOffset(flat, addrSpace), addrSpace); + segment = addrSpace.getDefaultSegmentFromFlat(flat); } /** * Constructor for SegmentedAddress. - * @param addrSpace address space for this address - * @param segmentOffset offset into the segment - * @param overlayId overlay number - * @param segment segment number + * @param addrSpace is the address space for this address + * @param segment is the segment number + * @param segmentOffset is the offset into the segment + * @throws AddressOutOfBoundsException if the address does not fit in the space */ SegmentedAddress(SegmentedAddressSpace addrSpace, int segment, int segmentOffset) throws AddressOutOfBoundsException { - super(addrSpace, (segment << 4) + segmentOffset); - this.addrSpace = addrSpace; - if (offset > 0xFFFFF) { - this.segment = 0xFFFF; - } else { - this.segment = segment; - } + super(addrSpace, addrSpace.getFlatOffset(segment, segmentOffset)); + this.segment = segment; } /** * Constructor for SegmentedAddress. * @param addrSpace address space for this address - * @param offset offset into the space + * @param flat is the flat offset into the space + * @throws AddressOutOfBoundsException if the flat address does not fit in the space */ - SegmentedAddress(SegmentedAddressSpace addrSpace, long offset) + SegmentedAddress(SegmentedAddressSpace addrSpace, long flat) throws AddressOutOfBoundsException { - super(addrSpace, adjustOffset(offset)); - this.addrSpace = addrSpace; - if (offset > 0xFFFFF) { - this.segment = 0xFFFF; - } else { - this.segment = (int) ((offset >> 4) & 0xf000); - } + super(addrSpace, adjustOffset(flat, addrSpace)); + segment = addrSpace.getDefaultSegmentFromFlat(flat); } - private static long adjustOffset(long offset) { - // Decompiler treats segmented space as a 32-bit space and may produce an address offset - // of 0xffffffff for a first use offset (= 0 minus 1). - if (offset == 0x0ffffffffL) { - offset = 0x0fffffL; - } - return offset; + private static long adjustOffset(long flat, SegmentedAddressSpace addrSpace) { + int seg = addrSpace.getDefaultSegmentFromFlat(flat); + long offset = addrSpace.getDefaultOffsetFromFlat(flat); + return addrSpace.getFlatOffset(seg, offset); } /** @@ -97,25 +79,24 @@ public class SegmentedAddress extends GenericAddress { /** * Returns the offset within the segment. + * @return the offset value */ public int getSegmentOffset() { - return (int) (offset - (segment << 4)); + return (int) ((SegmentedAddressSpace) addrSpace).getOffsetFromFlat(offset, segment); } /** * Returns a new address that is equivalent to this address using * the given segment number. * @param seg the seqment value to normalize to. + * @return the new address */ public SegmentedAddress normalize(int seg) { - if ((seg << 4) > offset) { + SegmentedAddress res = ((SegmentedAddressSpace) addrSpace).getAddressInSegment(offset, seg); + if (res == null) { return this; } - int off = (int) (offset - (seg << 4)); - if (off > 0xffff) { - return this; - } - return new SegmentedAddress(addrSpace, seg, off); + return res; } /** @@ -124,8 +105,12 @@ public class SegmentedAddress extends GenericAddress { */ @Override public Address getNewAddress(long byteOffset) { - SegmentedAddress segAddr = addrSpace.getAddress(byteOffset); - return segAddr.normalize(segment); + SegmentedAddressSpace segSpace = (SegmentedAddressSpace) addrSpace; + SegmentedAddress res = segSpace.getAddressInSegment(byteOffset, segment); + if (res == null) { + return segSpace.getAddress(byteOffset); + } + return res; } @Override @@ -174,33 +159,4 @@ public class SegmentedAddress extends GenericAddress { } return addr; } - - /** - * @see ghidra.program.model.address.GenericAddress#next() - */ - /* - @Override - public Address next() { - if ((offset & SegmentedAddressSpace.MASK) == SegmentedAddressSpace.MASK) { - return null; - } - long newOffset = (offset + 1) & SegmentedAddressSpace.MASK; - return new SegmentedAddress(addrSpace, newOffset).normalize(segment); - } - */ - - /** - * @see ghidra.program.model.address.GenericAddress#previous() - */ - /* - @Override - public Address previous() { - if ((offset & SegmentedAddressSpace.MASK) == 0) { - return null; - } - long newOffset = (offset - 1) & SegmentedAddressSpace.MASK; - return new SegmentedAddress(addrSpace, newOffset).normalize(segment); - } - */ - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddressSpace.java index 4beccba018..71f7581304 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddressSpace.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddressSpace.java @@ -20,30 +20,120 @@ import org.apache.commons.lang3.StringUtils; import ghidra.util.NumericUtilities; /** - * Address Space for dealing with Intel 20 bit segmented addresses. + * Address Space for dealing with (intel) segmented address spaces. + * It understands the mapping between the segmented encoding (seg:offset) and + * the flat address encoding necessary to produce an Address object that can be + * used by other analyses. This mapping is inherent in protected methods: + * - getDefaultOffsetFromFlat + * - getDefaultSegmentFromFlat + * - getFlatOffset + * - getOffsetFromFlat + * - getAddressInSegment + * + * These 5 methods can be overridden to get a different mapping. This base class is + * set up to map as for x86 16-bit real-mode. */ public class SegmentedAddressSpace extends GenericAddressSpace { - private final static int SIZE = 21; - - //private final static int SEGMENT_OFFSET_MASK = 0xffff; - //final static long MASK = (1L << SIZE) - 1; + private final static int REALMODE_SIZE = 21; + private final static long REALMODE_MAXOFFSET = 0x10FFEF; /** - * Constructs a new Segmented AddressSpace. - * - * @param name - * the name of the space - * @param unique - * the unique id for the space. + * Constructor for larger size address spaces (than the real-mode space) + * @param name is the name of the space + * @param size is the number of bits in a (flat) address + * @param unique is the unique id for the space + */ + protected SegmentedAddressSpace(String name, int size, int unique) { + super(name, size, TYPE_RAM, unique); +// maxAddress = getUncheckedAddress(maxOffset); + // Constructors for derived classes that call this will + // need to reconstruct maxAddress themselves. + } + + /** + * Constructs a new Segmented AddressSpace for x86 real-mode, with 21-bit addresses. + * @param name is the name of the space + * @param unique is the unique id for the space. */ public SegmentedAddressSpace(String name, int unique) { - super(name, SIZE, TYPE_RAM, unique); - maxOffset = 0x10FFEF; + super(name, REALMODE_SIZE, TYPE_RAM, unique); + maxOffset = REALMODE_MAXOFFSET; spaceSize = maxOffset + 1; maxAddress = getUncheckedAddress(maxOffset); } + /** + * Given a 16-bit segment and an offset, produce the flat address offset + * @param segment is the segment value + * @param offset is the 16-bit offset into the segment + * @return the encoded flat offset + */ + protected long getFlatOffset(int segment, long offset) { + long res = segment; + res <<= 4; + res += offset; + return res; + } + + /** + * Given a flat address offset, extract the default 16-bit segment portion + * @param flat is the flat offset + * @return the segment value + */ + protected int getDefaultSegmentFromFlat(long flat) { + if (flat > 0xFFFFFL) { + return 0xFFFF; + } + return (int) ((flat >> 4) & 0xF000); + } + + /** + * Given a flat address offset, extract the offset portion assuming the + * default segment. + * @param flat is the flat offset + * @return the offset value + */ + protected long getDefaultOffsetFromFlat(long flat) { + if (flat > 0xFFFFFL) { + return flat - 0xFFFF0; + } + return flat & 0xFFFFL; + } + + /** + * Given a flat address offset, extract a segment offset assuming a + * specific segment value. + * @param flat is the flat offset + * @param segment is the specific segment value + * @return the segment offset + */ + protected long getOffsetFromFlat(long flat, int segment) { + return flat - (segment << 4); + } + + /** + * Given a flat address offset and a preferred segment, try + * to create an address that maps to the offset and is in the segment. For + * architectures like x86 real-mode, multiple address encodings can map to + * the same flat address offset. This method tries to select between the different + * encodings. If the flat offset cannot be encoded with the preferred segment, + * null is returned. + * + * @param flat is the flat offset + * @param preferredSegment is the 16-bit preferred segment value + * @return the segment encoded address or null + */ + protected SegmentedAddress getAddressInSegment(long flat, int preferredSegment) { + if ((preferredSegment << 4) <= flat) { + int off = (int) (flat - (preferredSegment << 4)); + if (off <= 0xffff) { + return new SegmentedAddress(this, preferredSegment, off); + } + } + return null; + } + /** * * @see ghidra.program.model.address.AddressSpace#getAddress(java.lang.String) @@ -98,62 +188,16 @@ public class SegmentedAddressSpace extends GenericAddressSpace { long off = addr.getOffset() - displacement; if (off >= 0) { SegmentedAddress saddr = (SegmentedAddress) addr; - return new SegmentedAddress(this, off).normalize(saddr.getSegment()); + Address resaddr = getAddressInSegment(off, saddr.getSegment()); + if (resaddr == null) { // Could not map into desired segment + resaddr = new SegmentedAddress(this, off); // just use default + } + return resaddr; } throw new AddressOutOfBoundsException( "Address Overflow in subtract: " + addr + " + " + displacement); } - /** - * - * @see ghidra.program.model.address.AddressSpace#subtractWrap(ghidra.program.model.address.Address, - * long) - */ - /* - @Override - public Address subtractWrap(Address addr, long displacement) { - - testAddressSpace(addr); - SegmentedAddress saddr = (SegmentedAddress) addr; - - int segOffset = (int) ((saddr.getSegmentOffset() - displacement) & SEGMENT_OFFSET_MASK); - return new SegmentedAddress(this, saddr.getSegment(), segOffset); - } - */ - - /** - * @see ghidra.program.model.address.AbstractAddressSpace#subtractWrapSpace(ghidra.program.model.address.Address, long) - */ - /* - @Override - public Address subtractWrapSpace(Address addr, long displacement) { - testAddressSpace(addr); - return new SegmentedAddress(this, (addr.getOffset() - displacement) & MASK); - } - */ - - /** - * - * @see ghidra.program.model.address.AddressSpace#subtractNoWrap(ghidra.program.model.address.Address, - * long) - */ - /* - @Override - public Address subtractNoWrap(Address addr, long displacement) throws AddressOverflowException { - - testAddressSpace(addr); - SegmentedAddress saddr = (SegmentedAddress) addr; - - long off = addr.getOffset() - displacement; - if ((off & MASK) != off) { - throw new AddressOverflowException(); - } - - return new SegmentedAddress(this, off).normalize(saddr.getSegment()); - - } - */ - /** * * @see ghidra.program.model.address.AddressSpace#add(ghidra.program.model.address.Address, @@ -175,82 +219,35 @@ public class SegmentedAddressSpace extends GenericAddressSpace { //if ((off & MASK) == off) { if (off >= 0 && off <= maxOffset) { SegmentedAddress saddr = (SegmentedAddress) addr; - return new SegmentedAddress(this, off).normalize(saddr.getSegment()); + Address resaddr = getAddressInSegment(off, saddr.getSegment()); + if (resaddr == null) { // Could not map into desired segment + resaddr = new SegmentedAddress(this, off); // just use default + } + return resaddr; } throw new AddressOutOfBoundsException( "Address Overflow in add: " + addr + " + " + displacement); } - /** - * - * @see ghidra.program.model.address.AddressSpace#addWrap(ghidra.program.model.address.Address, - * long) - */ - /* - @Override - public Address addWrap(Address addr, long displacement) { - testAddressSpace(addr); - SegmentedAddress saddr = (SegmentedAddress) addr; - - int segOffset = (int) ((saddr.getSegmentOffset() + displacement) & SEGMENT_OFFSET_MASK); - return new SegmentedAddress(this, saddr.getSegment(), segOffset); - } - */ - - /** - * @see ghidra.program.model.address.AddressSpace#addWrapSpace(ghidra.program.model.address.Address, - * long) - */ - /* - @Override - public Address addWrapSpace(Address addr, long displacement) { - testAddressSpace(addr); - return new SegmentedAddress(this, (addr.getOffset() + displacement) & MASK); - } - */ - - /** - * - * @see ghidra.program.model.address.AddressSpace#addNoWrap(ghidra.program.model.address.Address, - * long) - */ - /* - @Override - public Address addNoWrap(Address addr, long displacement) throws AddressOverflowException { - - SegmentedAddress saddr = (SegmentedAddress) addr; - testAddressSpace(addr); - - long off = addr.getOffset() + displacement; - if ((off & MASK) != off) { - throw new AddressOverflowException(); - } - - return new SegmentedAddress(this, off).normalize(saddr.getSegment()); - } - */ - private long parseString(String addr) { if (addr.startsWith("0x") || addr.startsWith("0X")) { return NumericUtilities.parseHexLong(addr.substring(2)); } return NumericUtilities.parseHexLong(addr); - } private SegmentedAddress parseNonSegmented(String offStr) throws AddressFormatException { try { long off = (int) parseString(offStr); - if (off < 0 || off > 0xfffff) { - throw new AddressFormatException("Offset is outside the range 0 to 0xfffff"); - } return new SegmentedAddress(this, off); - } catch (NumberFormatException e) { throw new AddressFormatException("Cannot parse (" + offStr + ") as a number."); } + catch (AddressOutOfBoundsException e) { + throw new AddressFormatException(e.getMessage()); + } } private SegmentedAddress parseSegmented(String segStr, String offStr) @@ -262,23 +259,22 @@ public class SegmentedAddressSpace extends GenericAddressSpace { catch (NumberFormatException e) { return null; } - if (seg < 0 || seg > 0xffff) { - throw new AddressFormatException("Segment is outside the range 0 to 0xffff"); + + int off = -1; + try { + off = (int) parseString(offStr); + } + catch (NumberFormatException e) { + throw new AddressFormatException( + "Cannot parse (" + segStr + ':' + offStr + ") as a number."); } try { - int off = (int) parseString(offStr); - if (off < 0 || off > 0xffff) { - throw new AddressFormatException("Offset is outside the range 0 to 0xffff"); - } - return new SegmentedAddress(this, seg, off); + return getAddress(seg, off); } catch (AddressOutOfBoundsException e) { throw new AddressFormatException(e.getMessage()); } - catch (NumberFormatException e) { - throw new AddressFormatException("Cannot parse (" + offStr + ") as a number."); - } } /** @@ -315,12 +311,24 @@ public class SegmentedAddressSpace extends GenericAddressSpace { if (segmentOffset > 0xffff) { throw new AddressOutOfBoundsException("Offset is too large."); } - if ((segment << 4) + segmentOffset > maxOffset) { - throw new AddressOutOfBoundsException("Segmented address is too large."); + if (segment > 0xffff) { + throw new AddressOutOfBoundsException("Segment is too large."); } return new SegmentedAddress(this, segment, segmentOffset); } + /** + * Get the segment index for the first segment whose start address + * comes after the given address + * @param addr is the given address + * @return the segment index + */ + public int getNextOpenSegment(Address addr) { + int res = (int) addr.getOffset(); // The "flat" offset (presumably real-mode encoded) + res = (res >> 4) + 1; + return res; + } + /** * * @see ghidra.program.model.address.AddressSpace#getPhysicalSpace() diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java index f917908287..06b5bcd60e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java @@ -163,29 +163,30 @@ public class BasicCompilerSpec implements CompilerSpec { language.getProperty(GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS); if (classname == null) { pcodeInject = new PcodeInjectLibrary(language); // This is the default implementation - return; } - try { - Class c = Class.forName(classname); - if (!PcodeInjectLibrary.class.isAssignableFrom(c)) { + else { + try { + Class c = Class.forName(classname); + if (!PcodeInjectLibrary.class.isAssignableFrom(c)) { + Msg.error(this, + "Language " + language.getLanguageID() + " does not specify a valid " + + GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS); + throw new RuntimeException(classname + " does not implement interface " + + PcodeInjectLibrary.class.getName()); + } + Class injectLibraryClass = + (Class) c; + Constructor constructor = + injectLibraryClass.getConstructor(SleighLanguage.class); + pcodeInject = constructor.newInstance(language); + } + catch (Exception e) { Msg.error(this, "Language " + language.getLanguageID() + " does not specify a valid " + GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS); - throw new RuntimeException(classname + " does not implement interface " + - PcodeInjectLibrary.class.getName()); + throw new RuntimeException("Failed to instantiate " + classname + " for language " + + language.getLanguageID(), e); } - Class injectLibraryClass = - (Class) c; - Constructor constructor = - injectLibraryClass.getConstructor(SleighLanguage.class); - pcodeInject = constructor.newInstance(language); - } - catch (Exception e) { - Msg.error(this, "Language " + language.getLanguageID() + " does not specify a valid " + - GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS); - throw new RuntimeException( - "Failed to instantiate " + classname + " for language " + language.getLanguageID(), - e); } List additionalInject = language.getAdditionalInject(); if (additionalInject != null) { @@ -254,16 +255,6 @@ public class BasicCompilerSpec implements CompilerSpec { ctxsetting.add(new ContextSetting(reg, value, begad, endad)); } - @Override - public int getCallStackMod() { - return defaultModel.getExtrapop(); - } - - @Override - public int getCallStackShift() { - return defaultModel.getStackshift(); - } - @Override public PrototypeModel[] getCallingConventions() { return models; @@ -491,6 +482,12 @@ public class BasicCompilerSpec implements CompilerSpec { evalCurrentPrototype = parser.start().getAttribute("name"); parser.end(); } + else if (name.equals("segmentop")) { + XmlElement el = parser.start(); + InjectPayloadSleigh payload = language.parseSegmentOp(el, parser); + parser.end(); + pcodeInject.registerInject(payload); + } else { XmlElement el = parser.start(); parser.discardSubTree(el); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpec.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpec.java index 98fc3f8dc3..3ad0a8b151 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpec.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpec.java @@ -89,22 +89,6 @@ public interface CompilerSpec { */ public AddressSpace getStackBaseSpace(); - /** - * Number of extra bytes popped from the stack on return - * -1 if it is unknown. - * @return # of bytes popped from the stack by a called function on return. - */ - public int getCallStackMod(); - - /** - * Get the normal shift in the stack at the call to this routine. - * This will be the things pushed on the stack as part of the calling - * conventions. Normally the return value. - * - * @return entry stack shift or -1 if it is unknown - */ - public int getCallStackShift(); - /** * Returns true if stack grows with negative offsets */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java index c1456a02d5..34ae07d970 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java @@ -112,18 +112,19 @@ public class FunctionPrototype { // function pointer, in which case forcing the void // causes the decompiler to drop real parameters. // At the moment, we turn on varargs if there are no params - if (voidimpliesdotdotdot && voidinputlock) + if (voidimpliesdotdotdot && voidinputlock) { dotdotdot = true; + } } /** * Populate Function Prototype from information attached to a function in the Program DB. * * @param f is the function to grab prototype from - * @param default_extrapop - * @param override_extrapop + * @param overrideExtrapop is the override value to use for extrapop + * @param doOverride is true if the override value should be used */ - void grabFromFunction(Function f, int default_extrapop, boolean override_extrapop) { + void grabFromFunction(Function f, int overrideExtrapop, boolean doOverride) { modelname = f.getCallingConventionName(); modellock = ((modelname != null) && (modelname != Function.UNKNOWN_CALLING_CONVENTION_STRING)); @@ -134,10 +135,12 @@ public class FunctionPrototype { returnstorage = returnparam.getVariableStorage(); SourceType sigSource = f.getSignatureSource(); - if (sigSource != SourceType.DEFAULT) + if (sigSource != SourceType.DEFAULT) { outputlock = DataType.DEFAULT != returntype; - else + } + else { outputlock = false; + } if ((returnstorage == null) || (!returnstorage.isValid())) { // Unassigned or otherwise invalid storage outputlock = false; @@ -157,12 +160,21 @@ public class FunctionPrototype { // stackshift is the normal stack change because of a call. // int purge = f.getStackPurgeSize(); - if (override_extrapop || purge == Function.INVALID_STACK_DEPTH_CHANGE || - purge == Function.UNKNOWN_STACK_DEPTH_CHANGE) { - extrapop = default_extrapop; + if (doOverride) { + extrapop = overrideExtrapop; } else { - extrapop = purge + f.getProgram().getCompilerSpec().getCallStackShift(); + PrototypeModel protoModel = f.getCallingConvention(); + if (protoModel == null) { + protoModel = f.getProgram().getCompilerSpec().getDefaultCallingConvention(); + } + if (purge == Function.INVALID_STACK_DEPTH_CHANGE || + purge == Function.UNKNOWN_STACK_DEPTH_CHANGE) { + extrapop = protoModel.getExtrapop(); + } + else { + extrapop = purge + protoModel.getStackshift(); + } } } @@ -187,8 +199,9 @@ public class FunctionPrototype { * @return the number of defined parameters for this function prototype */ public int getNumParams() { - if (localsyms != null) + if (localsyms != null) { return localsyms.getNumParams(); + } return params.length; } @@ -198,8 +211,9 @@ public class FunctionPrototype { * if this prototype is not backed by a LocalSymbolMap */ public HighParam getParam(int i) { - if (localsyms != null) + if (localsyms != null) { return localsyms.getParam(i); + } return null; } @@ -299,36 +313,50 @@ public class FunctionPrototype { /** * append an XML string representing this function prototype + * @param res is where the string should be appended + * @param dtmanage is the DataTypeManager for building type reference tags */ public void buildPrototypeXML(StringBuilder res, PcodeDataTypeManager dtmanage) { res.append("\n"); res.append(" \n "); int sz = returntype.getLength(); if (sz < 0) { @@ -338,14 +366,16 @@ public class FunctionPrototype { if ((returnstorage != null) && returnstorage.isValid() && (!returnstorage.isVoidStorage())) { int logicalsize = 0; // Assume logicalsize of return matches datatype size - if (sz != returnstorage.size()) // If the sizes do not match + if (sz != returnstorage.size()) { // If the sizes do no match logicalsize = sz; // force the logical size on the varnode + } String addrstring = Varnode.buildXMLAddress(returnstorage.getVarnodes(), logicalsize); res.append(addrstring).append("\n "); } - else + else { // Decompiler will use model for storage res.append("\n "); // Don't specify where return type is stored + } res.append(dtmanage.buildTypeRef(returntype, sz)); res.append(" \n"); @@ -370,8 +400,9 @@ public class FunctionPrototype { res.append("\">\n"); res.append(" \n "); // Blank address sz = dt.getLength(); - if (sz < 0) + if (sz < 0) { sz = 1; + } res.append(dtmanage.buildTypeRef(dt, sz)); res.append("\n"); } @@ -381,21 +412,22 @@ public class FunctionPrototype { } /** - * Parse the function prototype from an XML tree node. - * - * @param node XML tree node from a parsing of a larger XML document - * - * @throws PcodeXMLException + * Parse the function prototype from tag. + * @param parser is the XML document to parse + * @param dtmanage is the DataTypeManager used to parse data-type tags + * @throws PcodeXMLException for any problems parsing */ public void readPrototypeXML(XmlPullParser parser, PcodeDataTypeManager dtmanage) throws PcodeXMLException { XmlElement node = parser.start("prototype"); modelname = node.getAttribute("model"); String val = node.getAttribute("extrapop"); - if (val.equals("unknown")) + if (val.equals("unknown")) { extrapop = PrototypeModel.UNKNOWN_EXTRAPOP; - else + } + else { extrapop = SpecXmlUtils.decodeInt(val); + } modellock = false; if (node.hasAttribute("modellock")) { modellock = SpecXmlUtils.decodeBoolean(node.getAttribute("modellock")); @@ -434,16 +466,18 @@ public class FunctionPrototype { } XmlElement retel = parser.start("returnsym"); outputlock = false; - if (retel.hasAttribute("typelock")) + if (retel.hasAttribute("typelock")) { outputlock = SpecXmlUtils.decodeBoolean(retel.getAttribute("typelock")); + } parser.discardSubTree(); returnstorage = null; // For now don't use decompiler's return storage returntype = dtmanage.readXMLDataType(parser); parser.end(retel); XmlElement peeknode = parser.peek(); - if ((peeknode != null) && peeknode.isStart()) + if ((peeknode != null) && peeknode.isStart()) { parser.discardSubTree(); // The decompiler may return an tag + } parser.end(node); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java index ffdcc8f2f2..6aba92aa4a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java @@ -54,7 +54,8 @@ public class HighFunction extends PcodeSyntaxTree { /** * @param function function associated with the higher level function abstraction. - * @param langParser language parser used to disassemble/get info on the language + * @param language description of the processor language of the function + * @param compilerSpec description of the compiler that produced the function * @param dtManager data type manager * @param showNamespace true signals to print function names with their namespace */ @@ -124,33 +125,20 @@ public class HighFunction extends PcodeSyntaxTree { } /** - * Populate the information for the HighFunction from the information stored in Ghidra attached - * to the function. + * Populate the information for the HighFunction from the information in the + * Function object. * - * @param default_extrapop - * @param includeDefaultNames - * @param override_extrapop + * @param overrideExtrapop is the value to use if extrapop is overridden + * @param includeDefaultNames is true if default symbol names should be considered locked + * @param doOverride is true if extrapop is overridden */ - public void grabFromFunction(int default_extrapop, boolean includeDefaultNames, - boolean override_extrapop) { + public void grabFromFunction(int overrideExtrapop, boolean includeDefaultNames, + boolean doOverride) { localSymbols.grabFromFunction(includeDefaultNames); // Locals must be read first - proto.grabFromFunction(func, default_extrapop, override_extrapop); + proto.grabFromFunction(func, overrideExtrapop, doOverride); jumpTables = null; protoOverrides = null; grabOverrides(); - -// This reads any old actions from the StringProperty -// This is needed for backward compatibility -// String actstring = HighFunction.tagFindInclude("actionlist",funcstring); -// if (actstring != null) { -// try { -// Element el = stringTree(actstring); -// readActionXML(el,this); -// } catch (PcodeXMLException e) { -// Err.error(this, null, "Error", "Unexpected Exception: " + e.getMessage(), e); -// } -// } - } /** @@ -312,10 +300,10 @@ public class HighFunction extends PcodeSyntaxTree { } /** - * Read in the Jump Table list for this function from an XML rep + * Read in the Jump Table list for this function from XML * - * @param el - * @throws PcodeXMLException + * @param parser is the XML stream + * @throws PcodeXMLException for any format errors */ private void readJumpTableListXML(XmlPullParser parser) throws PcodeXMLException { XmlElement el = parser.start("jumptablelist"); @@ -425,11 +413,9 @@ public class HighFunction extends PcodeSyntaxTree { * @param high is the HighVariable to split * @param vn is a representative of the merge group to split out * @return a HighVariable containing just the forced merge group of vn - * @throws PcodeException + * @throws PcodeException if the split can't be performed */ public HighVariable splitOutMergeGroup(HighVariable high, Varnode vn) throws PcodeException { -// if (high.isNameLocked() || high.isTypeLocked()) -// return high; // Locked variable should not be speculatively merged try { ArrayList newinst = new ArrayList(); ArrayList oldinst = new ArrayList(); @@ -508,9 +494,13 @@ public class HighFunction extends PcodeSyntaxTree { } /** - * Build an XML string that represents all the information about this HighFunction. + * Build an XML string that represents all the information about this HighFunction. The + * size describes how many bytes starting from the entry point are used by the function, but + * this doesn't need to be strictly accurate as it is only used to associate the function with + * addresses near its entry point. * * @param entryPoint pass null to use the function entryPoint, pass an address to force an entry point + * @param size describes how many bytes the function occupies as code * @return the XML string */ public String buildFunctionXML(Address entryPoint, int size) { @@ -588,28 +578,6 @@ public class HighFunction extends PcodeSyntaxTree { return resBuf.toString(); } - /** - * Convert old decompiler tags to new Register variables - */ -// public void updateVersion() { -// StringPropertyMap stringmap = func.getProgram().getUsrPropertyManager().getStringPropertyMap(DECOMPILER_TAG_MAP); -// String funcstring = null; -// if (stringmap != null) -// funcstring = stringmap.getString(func.getEntryPoint()); -// -// String actstring = HighFunction.tagFindInclude("actionlist",funcstring); -// if (actstring == null) return; -// try { -// Element el = stringTree(actstring); -// readActionXML(el,this); -// } catch (PcodeXMLException e) { -// Err.error(this, null, "Error", "Unexpected Exception: " + e.getMessage(), e); -// } -// locals.grabFromFunction(); -// getLocalVariableMap().storeUnMappedToDatabase(); -// updateProperties(); -// } - public static ErrorHandler getErrorHandler(final Object errOriginator, final String targetName) { return new ErrorHandler() { @@ -708,14 +676,15 @@ public class HighFunction extends PcodeSyntaxTree { } /** - * Create and XML SAX parse tree from an input XML string + * Create XML parse tree from an input XML string * * TODO: this probably doesn't belong here. * - * @param xml string to parse - * @return an XML tree element + * @param xml is the XML string to parse + * @param handler is the handler to use for parsing errors + * @return the XML tree * - * @throws PcodeXMLException + * @throws PcodeXMLException for format errors in the XML */ static public XmlPullParser stringTree(InputStream xml, ErrorHandler handler) throws PcodeXMLException { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LanguageTranslatorAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LanguageTranslatorAdapter.java index 85b235ae8e..22259a13c3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LanguageTranslatorAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LanguageTranslatorAdapter.java @@ -539,16 +539,6 @@ class TemporaryCompilerSpec implements CompilerSpec { public void applyContextSettings(DefaultProgramContext ctx) { } - @Override - public int getCallStackMod() { - return newCompilerSpec.getCallStackMod(); - } - - @Override - public int getCallStackShift() { - return newCompilerSpec.getCallStackShift(); - } - @Override public PrototypeModel[] getCallingConventions() { return new PrototypeModel[0]; diff --git a/Ghidra/Processors/Z80/data/languages/z80.cspec b/Ghidra/Processors/Z80/data/languages/z80.cspec index 37ed6684c8..9a4e34c425 100644 --- a/Ghidra/Processors/Z80/data/languages/z80.cspec +++ b/Ghidra/Processors/Z80/data/languages/z80.cspec @@ -9,10 +9,15 @@ - - - - + + + + + + + diff --git a/Ghidra/Processors/x86/certification.manifest b/Ghidra/Processors/x86/certification.manifest index b499e4cc9a..023526417c 100644 --- a/Ghidra/Processors/x86/certification.manifest +++ b/Ghidra/Processors/x86/certification.manifest @@ -27,6 +27,7 @@ data/languages/rdseed.sinc||GHIDRA||||END| data/languages/sgx.sinc||GHIDRA||||END| data/languages/sha.sinc||GHIDRA||||END| data/languages/smx.sinc||GHIDRA||||END| +data/languages/x86-16-real.pspec||GHIDRA||||END| data/languages/x86-16.cspec||GHIDRA||||END| data/languages/x86-16.pspec||GHIDRA||||END| data/languages/x86-64-gcc.cspec||GHIDRA||||END| @@ -34,8 +35,6 @@ data/languages/x86-64-win.cspec||GHIDRA||||END| data/languages/x86-64.dwarf||GHIDRA||||END| data/languages/x86-64.pspec||GHIDRA||||END| data/languages/x86-64.slaspec||GHIDRA||||END| -data/languages/x86-smm.cspec||GHIDRA||||END| -data/languages/x86-smm.pspec||GHIDRA||||END| data/languages/x86.dwarf||GHIDRA||||END| data/languages/x86.ldefs||GHIDRA||||END| data/languages/x86.opinion||GHIDRA||||END| diff --git a/Ghidra/Processors/x86/data/languages/ia.sinc b/Ghidra/Processors/x86/data/languages/ia.sinc index c33b90ea07..8aec9772fc 100644 --- a/Ghidra/Processors/x86/data/languages/ia.sinc +++ b/Ghidra/Processors/x86/data/languages/ia.sinc @@ -251,7 +251,7 @@ define context contextreg opsize=(2,3) # =0 16-bit operands =1 32-bit operands =2 64-bit operands segover=(4,6) # 0=default 1=cs 2=ss 3=ds 4=es 5=fs 6=gs highseg=(4,4) # high bit of segover will be set for ES, FS, GS -# sstype=(7,7) # 0=DS default 1=SS default + protectedMode=(7,7) # 0 for real mode, 1 for protected mode repneprefx=(8,8) # 0xf2 REPNE prefix repprefx=(9,9) # 0xf3 REP prefix @@ -1056,7 +1056,9 @@ addr64: [Base64 + Index64*ss] is mod=2 & r_m=4; Index64 & Base64 & ss; imm32=0 addr64: [Base64] is mod=2 & r_m=4; rexXprefix=0 & index64=4 & Base64; imm32=0 { export Base64; } @endif - +currentCS: CS is protectedMode=0 & CS { tmp:4 = (inst_next >> 4) & 0xf000; CS = tmp:2; export CS; } +currentCS: CS is protectedMode=1 & CS { tmp:4 = (inst_next >> 16) & 0xffff; CS = tmp:2; export CS; } + segWide: is segover=0 { export 0:$(SIZE); } segWide: CS: is segover=1 & CS { export 0:$(SIZE); } segWide: SS: is segover=2 & SS { export 0:$(SIZE); } @@ -1066,7 +1068,7 @@ segWide: FS: is segover=5 & FS { export FS_OFFSET; } segWide: GS: is segover=6 & GS { export GS_OFFSET; } seg16: is segover=0 { export DS; } -seg16: CS: is segover=1 & CS { export CS; } +seg16: currentCS: is segover=1 & currentCS { export currentCS; } seg16: SS: is segover=2 & SS { export SS; } seg16: DS: is segover=3 & DS { export DS; } seg16: ES: is segover=4 & ES { export ES; } @@ -1387,10 +1389,8 @@ check_Rmr32_dest: is epsilon { } check_rm32_dest: is epsilon { } check_EAX_dest: is epsilon { } -# The far addresses listed here actually specify the CS segment to use -# but we do not model changing the CS segment in protected mode -# so we just use the offset portion of the address -ptr1616: reloc is imm16; j16 [ reloc = j16*16 + imm16; ] { CS = j16; export *[ram]:4 reloc; } +ptr1616: reloc is protectedMode=0 & imm16; j16 [ reloc = j16*0x10 + imm16; ] { CS = j16; export *[ram]:4 reloc; } +ptr1616: reloc is protectedMode=1 & imm16; j16 [ reloc = j16*0x10000 + imm16; ] { CS = j16; export *[ram]:4 reloc; } ptr1632: j16":"imm32 is imm32; j16 { CS = j16; export *:4 imm32; } @@ -2711,7 +2711,7 @@ Suffix3D: imm8 is imm8 [ suffix3D=imm8; ] { } @ifdef IA64 :CALL rel32 is vexMode=0 & addrsize=2 & (opsize=1 | opsize=2) & byte=0xe8; simm32=0 & rel32 { push88(&:8 inst_next); goto rel32; } @endif -:CALL rm16 is addrsize=0 & opsize=0 & byte=0xff; rm16 & reg_opcode=2 ... { push22(&:2 inst_next); tmp:4 = segment(CS,rm16); call [tmp]; } +:CALL rm16 is addrsize=0 & opsize=0 & byte=0xff & currentCS; rm16 & reg_opcode=2 ... { push22(&:2 inst_next); tmp:4 = segment(currentCS,rm16); call [tmp]; } :CALL rm16 is vexMode=0 & addrsize=1 & opsize=0 & byte=0xff; rm16 & reg_opcode=2 ... { push42(&:2 inst_next); call [rm16]; } @ifdef IA64 :CALL rm16 is vexMode=0 & addrsize=2 & opsize=0 & byte=0xff; rm16 & reg_opcode=2 ... { push82(&:2 inst_next); tmp:8 = inst_next + zext(rm16); call [tmp]; } @@ -3415,7 +3415,7 @@ enterFrames: low5 is low5 { tmp:1 = low5; export tmp; } :JMP rel16 is vexMode=0 & opsize=0 & byte=0xe9; rel16 { goto rel16; } :JMP rel32 is vexMode=0 & opsize=1 & byte=0xe9; rel32 { goto rel32; } :JMP rel32 is vexMode=0 & opsize=2 & byte=0xe9; rel32 { goto rel32; } -:JMP rm16 is vexMode=0 & addrsize=0 & opsize=0 & byte=0xff; rm16 & reg_opcode=4 ... { target:4 = segment(CS,rm16); goto [target]; } +:JMP rm16 is vexMode=0 & addrsize=0 & opsize=0 & byte=0xff & currentCS; rm16 & reg_opcode=4 ... { target:4 = segment(currentCS,rm16); goto [target]; } :JMP rm16 is vexMode=0 & addrsize=1 & opsize=0 & byte=0xff; rm16 & reg_opcode=4 ... { goto [rm16]; } :JMP rm32 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xff; rm32 & reg_opcode=4 ... { goto [rm32]; } @ifdef IA64 diff --git a/Ghidra/Processors/x86/data/languages/x86-16-real.pspec b/Ghidra/Processors/x86/data/languages/x86-16-real.pspec new file mode 100644 index 0000000000..8b307e612e --- /dev/null +++ b/Ghidra/Processors/x86/data/languages/x86-16-real.pspec @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ghidra/Processors/x86/data/languages/x86-16.cspec b/Ghidra/Processors/x86/data/languages/x86-16.cspec index e8ada927dd..f07cea0b16 100644 --- a/Ghidra/Processors/x86/data/languages/x86-16.cspec +++ b/Ghidra/Processors/x86/data/languages/x86-16.cspec @@ -31,14 +31,6 @@ - - - - - - - - @@ -93,7 +85,7 @@ - + @@ -119,7 +111,7 @@ - + diff --git a/Ghidra/Processors/x86/data/languages/x86-16.pspec b/Ghidra/Processors/x86/data/languages/x86-16.pspec index b1c2038b07..1b3042e81a 100644 --- a/Ghidra/Processors/x86/data/languages/x86-16.pspec +++ b/Ghidra/Processors/x86/data/languages/x86-16.pspec @@ -1,15 +1,31 @@ + + - + + + + + + + + + + + + + diff --git a/Ghidra/Processors/x86/data/languages/x86-smm.cspec b/Ghidra/Processors/x86/data/languages/x86-smm.cspec deleted file mode 100644 index 077b5c60e0..0000000000 --- a/Ghidra/Processors/x86/data/languages/x86-smm.cspec +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Ghidra/Processors/x86/data/languages/x86-smm.pspec b/Ghidra/Processors/x86/data/languages/x86-smm.pspec deleted file mode 100644 index 9a09f7566f..0000000000 --- a/Ghidra/Processors/x86/data/languages/x86-smm.pspec +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/Ghidra/Processors/x86/data/languages/x86.ldefs b/Ghidra/Processors/x86/data/languages/x86.ldefs index 475f8b8a23..70362290d8 100644 --- a/Ghidra/Processors/x86/data/languages/x86.ldefs +++ b/Ghidra/Processors/x86/data/languages/x86.ldefs @@ -34,11 +34,11 @@ variant="System Management Mode" version="2.8" slafile="x86.sla" - processorspec="x86-smm.pspec" + processorspec="x86-16.pspec" manualindexfile="../manuals/x86.idx" id="x86:LE:32:System Management Mode"> Intel/AMD 32-bit x86 System Management Mode - + Intel/AMD 16-bit x86 Real Mode - + + Intel/AMD 16-bit x86 Protected Mode + + + - + diff --git a/Ghidra/Processors/x86/data/patterns/patternconstraints.xml b/Ghidra/Processors/x86/data/patterns/patternconstraints.xml index b9cfea5d23..760c15795a 100644 --- a/Ghidra/Processors/x86/data/patterns/patternconstraints.xml +++ b/Ghidra/Processors/x86/data/patterns/patternconstraints.xml @@ -29,4 +29,9 @@ + + + x86-16_default_patterns.xml + + diff --git a/Ghidra/Processors/x86/data/patterns/x86-16_default_patterns.xml b/Ghidra/Processors/x86/data/patterns/x86-16_default_patterns.xml index b6830d43f1..c311272765 100644 --- a/Ghidra/Processors/x86/data/patterns/x86-16_default_patterns.xml +++ b/Ghidra/Processors/x86/data/patterns/x86-16_default_patterns.xml @@ -6,14 +6,17 @@ 0x4dcb 0.011... 1100.011 1100.010 .0.....0 0x00 - 0xc390 + 1100.010 .0.....0 0x00 0x90 + 0xc390 0xcb90 + 0xc3 0x558bec 0x5589e5 0xc8 000....0 0x0000 0x8cd89045 + 0x8cd055 diff --git a/GhidraBuild/Skeleton/data/languages/skel.cspec b/GhidraBuild/Skeleton/data/languages/skel.cspec index 29393df996..764e2ba184 100644 --- a/GhidraBuild/Skeleton/data/languages/skel.cspec +++ b/GhidraBuild/Skeleton/data/languages/skel.cspec @@ -11,14 +11,6 @@ - - - - - - - -