GP-5189 Add range attributes to VarargsFilter

This commit is contained in:
caheckman 2024-12-02 20:33:01 +00:00
parent 57df41297f
commit e3aa064061
10 changed files with 128 additions and 28 deletions

View file

@ -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(); // <retparam> or <param>
if (subId == 0) break;

View file

@ -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);
}

View file

@ -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.
/// - \<varargs first="0"/> - matches optional arguments but not non-optional ones.
/// - \<varargs first="0" last="0"/> - matches the first optional argument.
/// - \<varargs first="-1"/> - 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);
};

View file

@ -355,7 +355,12 @@
<interleave>
<optional>
<element name="varargs">
<empty/>
<optional>
<attribute name="first"/>
</optional>
<optional>
<attribute name="last"/>
</optional>
</element>
</optional>
<optional>

View file

@ -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 <varargs first="0"/>} - matches optional arguments but not non-optional ones.
* {@code <varargs first="0" last="0"/>} - matches the first optional argument.
* {@code <varargs first="-1"/>} - 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);
}
}

View file

@ -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();

View file

@ -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);

View file

@ -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(<spacebase>, #const)}. This decodes the reference and

View file

@ -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);
}

View file

@ -43,6 +43,11 @@
<pentry maxsize="500" minsize="1" align="1">
<addr space="stack" offset="2"/>
</pentry>
<rule>
<datatype name="any"/>
<varargs first="-1"/>
<goto_stack/>
</rule>
<rule>
<datatype name="struct" minsize="1"/>
<convert_to_ptr/>