Merge remote-tracking branch 'origin/GP-3330_JoinStack' into Ghidra_12.1

(Closes #8377)
This commit is contained in:
Ryan Kurtz
2026-04-24 12:58:33 -04:00
6 changed files with 131 additions and 11 deletions
@@ -81,6 +81,7 @@ src/decompile/datatests/revisit.xml||GHIDRA||||END|
src/decompile/datatests/sbyte.xml||GHIDRA||||END|
src/decompile/datatests/skipnext2.xml||GHIDRA||||END|
src/decompile/datatests/stackreturn.xml||GHIDRA||||END|
src/decompile/datatests/stackspill.xml||GHIDRA||||END|
src/decompile/datatests/stackstring.xml||GHIDRA||||END|
src/decompile/datatests/statuscmp.xml||GHIDRA||||END|
src/decompile/datatests/switchhide.xml||GHIDRA||||END|
@@ -1504,9 +1504,37 @@ void ActionFuncLink::funcLinkInput(FuncCallSpecs *fc,Funcdata &data)
loadval->setSpacebasePlaceholder();
spacebase = (AddrSpace *)0; // With a locked stack parameter, we don't need a stackplaceholder
}
continue;
}
else
data.opInsertInput(op,data.newVarnode(param->getSize(),param->getAddress()),op->numInput());
if (spc->getType() == IPTR_JOIN) {
JoinRecord *join = data.getArch()->findJoin(off);
int4 index = -1;
if (join->getPiece(0).space->getType() == IPTR_SPACEBASE)
index = 0;
else if (join->getPiece(join->numPieces()-1).space->getType() == IPTR_SPACEBASE)
index = join->numPieces()-1;
if (index >= 0) {
const VarnodeData &stack(join->getPiece(index));
const VarnodeData &remain(data.getArch()->stripJoinPiece(join, index));
Varnode *loadval = data.opStackLoad(stack.space,stack.offset,stack.size,op,(Varnode *)0,false);
Varnode *remainval = data.newVarnode(remain.size, remain.space, remain.offset);
PcodeOp *concatOp = data.newOp(2, op->getAddr());
data.opSetOpcode(concatOp, CPUI_PIECE);
if (index == 0) {
data.opSetInput(concatOp,loadval,0);
data.opSetInput(concatOp,remainval,1);
}
else {
data.opSetInput(concatOp,remainval,0);
data.opSetInput(concatOp,loadval,1);
}
Varnode *outvn = data.newUniqueOut(sz, concatOp);
data.opInsertBefore(concatOp, op);
data.opInsertInput(op,outvn,op->numInput());
continue;
}
}
data.opInsertInput(op,data.newVarnode(param->getSize(),param->getAddress()),op->numInput());
}
}
if (spacebase != (AddrSpace *)0) // If we need it, create the stackplaceholder
@@ -1975,9 +2003,21 @@ int4 ActionRestrictLocal::apply(Funcdata &data)
for(int4 j=0;j<numparam;++j) {
ProtoParameter *param = fc->getParam(j);
Address addr = param->getAddress();
if (addr.getSpace()->getType() != IPTR_SPACEBASE) continue;
uintb off = addr.getSpace()->wrapOffset(fc->getSpacebaseOffset() + addr.getOffset());
data.getScopeLocal()->markNotMapped(addr.getSpace(),off,param->getSize(),true);
spacetype tp = addr.getSpace()->getType();
if (tp == IPTR_SPACEBASE) {
uintb off = addr.getSpace()->wrapOffset(fc->getSpacebaseOffset() + addr.getOffset());
data.getScopeLocal()->markNotMapped(addr.getSpace(),off,param->getSize(),true);
}
else if (tp == IPTR_JOIN) {
JoinRecord *joinRec = data.getArch()->findJoin(addr.getOffset());
for(int4 k=0;k<joinRec->numPieces();++k) {
const VarnodeData &vdata(joinRec->getPiece(k));
if (vdata.space->getType() == IPTR_SPACEBASE) {
uintb off = vdata.space->wrapOffset(fc->getSpacebaseOffset() + vdata.offset);
data.getScopeLocal()->markNotMapped(vdata.space,off,vdata.size,true);
}
}
}
}
}
@@ -915,6 +915,34 @@ void AddrSpaceManager::renormalizeJoinAddress(Address &addr,int4 size)
addr = Address(newJoinRecord->unified.space,newJoinRecord->unified.offset);
}
/// If only 1 piece remains, the VarnodeData of that piece is returned.
/// Otherwise a new JoinRecord is created and its unified VarnodeData is returned.
/// \param join is the JoinRecord to strip
/// \param index is the index of the piece to strip, which must be at the front or back
/// \return the VarnodeData corresponding to the remaining piece(s)
const VarnodeData &AddrSpaceManager::stripJoinPiece(JoinRecord *join,int4 index)
{
int4 start,end;
if (index == 0) {
start = 1;
end = join->numPieces()-1;
}
else if (index == join->numPieces() - 1) {
start = 0;
end = join->numPieces()-2;
}
else
throw LowlevelError("Stripping middle piece from JoinRecord");
if (start == end)
return join->getPiece(start);
vector<VarnodeData> newPieces;
for(int4 i=start;i<=end;++i)
newPieces.push_back(join->getPiece(i));
JoinRecord *newJoinRecord = findAddJoin(newPieces, 0);
return newJoinRecord->unified;
}
/// The string \e must contain a hexadecimal offset. The offset may be optionally prepended with "0x".
/// The string may optionally start with the name of the address space to associate with the offset, followed
/// by ':' to separate it from the offset. If the name is not present, the default data space is assumed.
@@ -281,6 +281,9 @@ public:
/// \brief Make sure a possibly offset \e join address has a proper JoinRecord
void renormalizeJoinAddress(Address &addr,int4 size);
/// \brief Create an Address by stripping a piece from a JoinRecord
const VarnodeData &stripJoinPiece(JoinRecord *join,int4 index);
/// \brief Parse a string with just an \e address \e space name and a hex offset
Address parseAddressSimple(const string &val);
};
@@ -0,0 +1,36 @@
<decompilertest>
<binaryimage arch="MIPS:LE:32:default:default">
<!--
Example of a parameter that joins the stack with registers.
-->
<bytechunk space="ram" offset="0x4076c0" readonly="true">
000000001000a28f0800e00321108200
d0ffbd272c00bfaf640002241000a2af
253880001e000624140005240a000424
b01d100c000000002c00bf8f00000000
0800e0033000bd27
</bytechunk>
<symbol space="ram" offset="0x4076c0" name="receive"/>
<symbol space="ram" offset="0x4076c4" name="spill"/>
<symbol space="ram" offset="0x4076d0" name="callspill"/>
</binaryimage>
<script>
<com>parse line struct foo { int4 field_a; int4 field_b; };</com>
<com>parse line extern void receive(int4 a,int4 b,int4 c,foo dvar);</com>
<com>parse line extern int4 spill(int4 a,int4 b,int4 c,foo d);</com>
<com>parse line extern void callspill(int4 val1,int4 val2);</com>
<com>lo fu spill</com>
<com>decompile</com>
<com>print C</com>
<com>lo fu callspill</com>
<com>decompile</com>
<com>print C</com>
</script>
<stringmatch name="Stack spill #1" min="1" max="1">return a \+ d\.field_b;</stringmatch>
<stringmatch name="Stack spill #2" min="1" max="1">foo dvar;</stringmatch>
<stringmatch name="Stack spill #3" min="1" max="1">dvar\.field_b = 100;</stringmatch>
<stringmatch name="Stack spill #4" min="1" max="1">dvar\.field_a = val1;</stringmatch>
<stringmatch name="Stack spill #5" min="1" max="1">receive\(10,0x14,0x1e,dvar\);</stringmatch>
<stringmatch name="Stack spill #6" min="0" max="0">in_stack</stringmatch>
<stringmatch name="Stack spill #7" min="0" max="0">CONCAT</stringmatch>
</decompilertest>
@@ -445,7 +445,7 @@ public class VariableStorage implements Comparable<VariableStorage> {
* @return true if storage consists of a single stack varnode
*/
public boolean isStackStorage() {
if (varnodes == null || varnodes.length == 0) {
if (varnodes == null || varnodes.length != 1) {
return false;
}
// check first varnode for stack use
@@ -454,14 +454,20 @@ public class VariableStorage implements Comparable<VariableStorage> {
}
/**
* @return true if the last varnode for simple or compound storage is a stack varnode
* @return true if the first or last varnode for simple or compound storage is a stack varnode
*/
public boolean hasStackStorage() {
if (varnodes == null || varnodes.length == 0) {
return false;
}
// check last varnode for stack use
Address storageAddr = getLastVarnode().getAddress();
Address storageAddr = getFirstVarnode().getAddress();
if (storageAddr.isStackAddress()) {
return true;
}
if (varnodes.length == 1) {
return false;
}
storageAddr = getLastVarnode().getAddress();
return storageAddr.isStackAddress();
}
@@ -534,15 +540,21 @@ public class VariableStorage implements Comparable<VariableStorage> {
/**
* @return the stack offset associated with simple stack storage or compound
* storage where the last varnode is stack, see {@link #hasStackStorage()}.
* storage where the first or last varnode is stack, see {@link #hasStackStorage()}.
* @throws UnsupportedOperationException if storage does not have a stack varnode
*/
public int getStackOffset() {
if (varnodes != null && varnodes.length != 0) {
Address storageAddr = getLastVarnode().getAddress();
Address storageAddr = getFirstVarnode().getAddress();
if (storageAddr.isStackAddress()) {
return (int) storageAddr.getOffset();
}
if (varnodes.length > 1) {
storageAddr = getLastVarnode().getAddress();
if (storageAddr.isStackAddress()) {
return (int) storageAddr.getOffset();
}
}
}
throw new UnsupportedOperationException("Storage does not have a stack varnode");
}