diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc index d78b78731c..3e9f310446 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc @@ -3433,6 +3433,10 @@ void ProtoStoreInternal::decode(Decoder &decoder,ProtoModel *model) addressesdetermined = false; uint4 elemId = decoder.openElement(ELEM_INTERNALLIST); + uint4 firstId = decoder.getNextAttributeId(); + if (firstId == ATTRIB_FIRST) { + proto.firstVarArgSlot = decoder.readSignedInteger(); + } for(;;) { // This is only the input params uint4 subId = decoder.openElement(); // or if (subId == 0) break; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc index 1986094845..9335eb6ab9 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc @@ -412,13 +412,22 @@ bool VarargsFilter::filter(const PrototypePieces &proto,int4 pos) const { if (proto.firstVarArgSlot < 0) return false; - return (pos >= proto.firstVarArgSlot); + pos -= proto.firstVarArgSlot; + return (pos >= firstPos && pos <= lastPos); } void VarargsFilter::decode(Decoder &decoder) { uint4 elemId = decoder.openElement(ELEM_VARARGS); + for(;;) { + uint4 attribId = decoder.getNextAttributeId(); + if (attribId == 0) break; + if (attribId == ATTRIB_FIRST) + firstPos = decoder.readSignedInteger(); + else if (attribId == ATTRIB_LAST) + lastPos = decoder.readSignedInteger(); + } decoder.closeElement(elemId); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh index d352655b10..608892104e 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -190,15 +190,24 @@ public: virtual void decode(Decoder &decoder) {} }; -/// \brief A filter that selects function parameters that are considered optional +/// \brief A filter that selects a range of function parameters that are considered optional. /// /// If the underlying function prototype is considered to take variable arguments, the first -/// \e n parameters (as determined by PrototypePieces.firstVarArgSlot) are considered non-optional. -/// If additional data-types are provided beyond the initial \e n, these are considered optional. -/// This filter returns \b true for these optional parameters +/// n parameters (as determined by PrototypePieces.firstVarArgSlot) are considered non-optional. +///\e If additional data-types are provided beyond the initial \e n, these are considered optional. +/// By default this filter matches on any parameter in a prototype with variable arguments. +/// Optionally, it can filter on a range of parameters that are specified relative to the +/// first variable argument. +/// - \ - matches optional arguments but not non-optional ones. +/// - \ - matches the first optional argument. +/// - \ - matches the last non-optional argument and all optional ones. class VarargsFilter : public QualifierFilter { + int4 firstPos; ///< Start of range to match (offset relative to first variable arg) + int4 lastPos; ///< End of range to match public: - virtual QualifierFilter *clone(void) const { return new VarargsFilter(); } + VarargsFilter(void) { firstPos = 0x80000000; lastPos = 0x7fffffff; } + VarargsFilter(int4 first,int4 last) { firstPos = first; lastPos = last; } + virtual QualifierFilter *clone(void) const { return new VarargsFilter(firstPos,lastPos); } virtual bool filter(const PrototypePieces &proto,int4 pos) const; virtual void decode(Decoder &decoder); }; diff --git a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg index b34f7c1877..d9a1095a3d 100644 --- a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg +++ b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg @@ -355,7 +355,12 @@ - + + + + + + diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/VarargsFilter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/VarargsFilter.java index 9b6f1f2ab8..c4545cdebb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/VarargsFilter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/VarargsFilter.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,30 +15,54 @@ */ package ghidra.program.model.lang.protorules; +import static ghidra.program.model.pcode.AttributeId.*; import static ghidra.program.model.pcode.ElementId.*; import java.io.IOException; import ghidra.program.model.lang.PrototypePieces; import ghidra.program.model.pcode.Encoder; +import ghidra.util.xml.SpecXmlUtils; import ghidra.xml.*; /** - * A filter that selects function parameters that are considered optional. - * If the underlying function prototype is considered to take variable arguments, the first - * n parameters (as determined by PrototypePieces.firstVarArgSlot) are considered non-optional. + * A filter that selects a range of function parameters that are considered optional. + * If the underlying function prototype takes variable arguments, the first n + * parameters (as determined by PrototypePieces.firstVarArgSlot) are considered non-optional. * If additional data-types are provided beyond the initial n, these are considered optional. - * This filter returns true for these optional parameters + * By default this filter matches on all parameters in a prototype with variable arguments. + * Optionally, it can filter on a range of parameters that are specified relative to the + * first variable argument. + * {@code } - matches optional arguments but not non-optional ones. + * {@code } - matches the first optional argument. + * {@code } - matches the last non-optional argument and all optional ones. */ public class VarargsFilter implements QualifierFilter { + private int firstPos; // Range of params to match (relative to first variable arg) + private int lastPos; + + public VarargsFilter() { + firstPos = Integer.MIN_VALUE; + lastPos = Integer.MAX_VALUE; + } + + public VarargsFilter(int first, int last) { + firstPos = first; + lastPos = last; + } + @Override public QualifierFilter clone() { - return new VarargsFilter(); + return new VarargsFilter(firstPos, lastPos); } @Override public boolean isEquivalent(QualifierFilter op) { - return (this.getClass() == op.getClass()); + if (this.getClass() != op.getClass()) { + return false; + } + VarargsFilter otherFilter = (VarargsFilter) op; + return (firstPos == otherFilter.firstPos && lastPos == otherFilter.lastPos); } @Override @@ -46,18 +70,33 @@ public class VarargsFilter implements QualifierFilter { if (proto.firstVarArgSlot < 0) { return false; } - return (pos >= proto.firstVarArgSlot); + pos -= proto.firstVarArgSlot; + return (pos >= firstPos && pos <= lastPos); } @Override public void encode(Encoder encoder) throws IOException { encoder.openElement(ELEM_VARARGS); + if (firstPos != Integer.MIN_VALUE) { + encoder.writeSignedInteger(ATTRIB_FIRST, firstPos); + } + if (lastPos != Integer.MAX_VALUE) { + encoder.writeSignedInteger(ATTRIB_LAST, lastPos); + } encoder.closeElement(ELEM_VARARGS); } @Override public void restoreXml(XmlPullParser parser) throws XmlParseException { XmlElement elem = parser.start(ELEM_VARARGS.name()); + String firstPosString = elem.getAttribute(ATTRIB_FIRST.name()); + if (firstPosString != null) { + firstPos = SpecXmlUtils.decodeInt(firstPosString); + } + String lastPosString = elem.getAttribute(ATTRIB_LAST.name()); + if (lastPosString != null) { + lastPos = SpecXmlUtils.decodeInt(lastPosString); + } parser.end(elem); } } 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 6785e0563e..e7c84c2e7b 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 @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -311,9 +311,11 @@ public class FunctionPrototype { * Encode this function prototype to a stream. * @param encoder is the stream encoder * @param dtmanage is the DataTypeManager for building type reference tags + * @param firstVarArg is index of first variable argument or -1 * @throws IOException for errors in the underlying stream */ - public void encodePrototype(Encoder encoder, PcodeDataTypeManager dtmanage) throws IOException { + public void encodePrototype(Encoder encoder, PcodeDataTypeManager dtmanage, int firstVarArg) + throws IOException { encoder.openElement(ELEM_PROTOTYPE); if (extrapop == PrototypeModel.UNKNOWN_EXTRAPOP) { encoder.writeString(ATTRIB_EXTRAPOP, "unknown"); @@ -378,6 +380,10 @@ public class FunctionPrototype { } if (params != null) { encoder.openElement(ELEM_INTERNALLIST); + if (firstVarArg >= 0) { + // Encoding detail because we are not sending the storage address + encoder.writeSignedInteger(ATTRIB_FIRST, firstVarArg); + } for (ParameterDefinition param : params) { encoder.openElement(ELEM_PARAM); String name = param.getName(); 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 ce492b49ad..5f614b6ad8 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 @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -454,7 +454,7 @@ public class HighFunction extends PcodeSyntaxTree { AddressXML.encode(encoder, entryPoint); // Address is forced on XML } localSymbols.encodeLocalDb(encoder, namespace, getDataTypeManager().getNameTransformer()); - proto.encodePrototype(encoder, getDataTypeManager()); + proto.encodePrototype(encoder, getDataTypeManager(), -1); if ((jumpTables != null) && (jumpTables.size() > 0)) { encoder.openElement(ELEM_JUMPTABLELIST); for (JumpTable jumpTable : jumpTables) { @@ -466,13 +466,15 @@ public class HighFunction extends PcodeSyntaxTree { if (hasOverrideTag) { encoder.openElement(ELEM_OVERRIDE); PcodeDataTypeManager dtmanage = getDataTypeManager(); + Program prog = func.getProgram(); for (DataTypeSymbol sym : protoOverrides) { Address addr = sym.getAddress(); + int firstVarArg = HighFunctionDBUtil.getFirstVarArg(prog, addr); FunctionPrototype fproto = new FunctionPrototype( (FunctionSignature) sym.getDataType(), compilerSpec, false); encoder.openElement(ELEM_PROTOOVERRIDE); AddressXML.encode(encoder, addr); - fproto.encodePrototype(encoder, dtmanage); + fproto.encodePrototype(encoder, dtmanage, firstVarArg); encoder.closeElement(ELEM_PROTOOVERRIDE); } encoder.closeElement(ELEM_OVERRIDE); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java index 8daa772c83..ad7d06df28 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -739,6 +739,27 @@ public class HighFunctionDBUtil { return datsym; } + /** + * If there is a call to a function at the given address, and the function takes variable arguments, + * return the index of the first variable argument. Return -1 otherwise. + * @param program is the Program + * @param addr is the given address of the call + * @return the index of the first variable argument or -1 + */ + public static int getFirstVarArg(Program program, Address addr) { + for (Reference ref : program.getReferenceManager().getReferencesFrom(addr)) { + if (ref.isPrimary() && ref.getReferenceType().isCall()) { + Address callDestAddr = ref.getToAddress(); + Function func = program.getFunctionManager().getFunctionAt(callDestAddr); + if (func != null && func.hasVarArgs()) { + return func.getParameterCount(); + } + break; + } + } + return -1; + } + /** * Get the Address referred to by a spacebase reference. Address-of references are encoded in * the p-code syntax tree as: {@code vn = PTRSUB(, #const)}. This decodes the reference and diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java index 93e7ee86e8..789b29f95c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java @@ -681,7 +681,7 @@ public class PcodeDataTypeManager { encoder.writeSignedInteger(ATTRIB_SIZE, 1); // Force size of 1 CompilerSpec cspec = program.getCompilerSpec(); FunctionPrototype fproto = new FunctionPrototype(type, cspec, voidInputIsVarargs); - fproto.encodePrototype(encoder, this); + fproto.encodePrototype(encoder, this, -1); encoder.closeElement(ELEM_TYPE); } diff --git a/Ghidra/Processors/TI_MSP430/data/languages/TI_MSP430.cspec b/Ghidra/Processors/TI_MSP430/data/languages/TI_MSP430.cspec index 389ea2686d..a264ced13d 100644 --- a/Ghidra/Processors/TI_MSP430/data/languages/TI_MSP430.cspec +++ b/Ghidra/Processors/TI_MSP430/data/languages/TI_MSP430.cspec @@ -43,6 +43,11 @@ + + + + +