mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-25 02:26:13 +08:00
Merge remote-tracking branch 'origin/GP-3330_JoinStack' into Ghidra_12.1
(Closes #8377)
This commit is contained in:
@@ -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>
|
||||
+18
-6
@@ -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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user