mirror of
https://github.com/libretro/Play-.git
synced 2024-11-28 03:00:49 +00:00
Improved MIPS analysis so that it can cover functions that don't allocate any stack space and find the right begin address of a function.
git-svn-id: http://svn.purei.org/purei/trunk@879 b36208d7-6611-0410-8bec-b1987f11c4a2
This commit is contained in:
parent
7c8a4caca9
commit
425d6a8c1e
@ -2,15 +2,10 @@
|
||||
#include "MIPSAnalysis.h"
|
||||
#include "MIPS.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define MASK (0x1FFFFFFF)
|
||||
#define SHIFT (3)
|
||||
#define HASH(a) ((((a) & MASK) << SHIFT) >> 16)
|
||||
|
||||
CMIPSAnalysis::CMIPSAnalysis(CMIPS* pCtx)
|
||||
: m_pCtx(pCtx)
|
||||
{
|
||||
m_pCtx = pCtx;
|
||||
|
||||
}
|
||||
|
||||
CMIPSAnalysis::~CMIPSAnalysis()
|
||||
@ -20,129 +15,178 @@ CMIPSAnalysis::~CMIPSAnalysis()
|
||||
|
||||
void CMIPSAnalysis::Clear()
|
||||
{
|
||||
m_Subroutines.clear();
|
||||
m_subroutines.clear();
|
||||
}
|
||||
|
||||
void CMIPSAnalysis::InsertSubroutine(uint32 nStart, uint32 nEnd, uint32 nAllocStart, uint32 nAllocEnd, uint32 nStackSize, uint32 nReturnAddrPos)
|
||||
{
|
||||
uint32 nHashStart, nHashEnd;
|
||||
SUBROUTINE Subroutine;
|
||||
assert(FindSubroutine(nStart) == NULL);
|
||||
assert(FindSubroutine(nEnd) == NULL);
|
||||
|
||||
nHashStart = HASH(nStart);
|
||||
nHashEnd = HASH(nEnd);
|
||||
SUBROUTINE subroutine;
|
||||
subroutine.nStart = nStart;
|
||||
subroutine.nEnd = nEnd;
|
||||
subroutine.nStackAllocStart = nAllocStart;
|
||||
subroutine.nStackAllocEnd = nAllocEnd;
|
||||
subroutine.nStackSize = nStackSize;
|
||||
subroutine.nReturnAddrPos = nReturnAddrPos;
|
||||
|
||||
Subroutine.nStart = nStart;
|
||||
Subroutine.nEnd = nEnd;
|
||||
Subroutine.nStackAllocStart = nAllocStart;
|
||||
Subroutine.nStackAllocEnd = nAllocEnd;
|
||||
Subroutine.nStackSize = nStackSize;
|
||||
Subroutine.nReturnAddrPos = nReturnAddrPos;
|
||||
m_subroutines.insert(SubroutineList::value_type(nStart, subroutine));
|
||||
}
|
||||
|
||||
for(uint32 i = nHashStart; i <= nHashEnd; i++)
|
||||
{
|
||||
m_Subroutines.insert(SubroutineList::value_type(i, Subroutine));
|
||||
}
|
||||
void CMIPSAnalysis::ChangeSubroutineStart(uint32 currStart, uint32 newStart)
|
||||
{
|
||||
auto subroutineIterator = m_subroutines.find(currStart);
|
||||
assert(subroutineIterator != std::end(m_subroutines));
|
||||
|
||||
SUBROUTINE subroutine(subroutineIterator->second);
|
||||
subroutine.nStart = newStart;
|
||||
|
||||
m_subroutines.erase(subroutineIterator);
|
||||
m_subroutines.insert(SubroutineList::value_type(newStart, subroutine));
|
||||
}
|
||||
|
||||
void CMIPSAnalysis::Analyse(uint32 nStart, uint32 nEnd)
|
||||
{
|
||||
uint32 nCandidate, nOp, nTemp;
|
||||
uint32 nStackAmount, nReturnAddr;
|
||||
int nFound;
|
||||
nStart &= ~0x3;
|
||||
nEnd &= ~0x3;
|
||||
|
||||
nStart &= ~0x3;
|
||||
nEnd &= ~0x3;
|
||||
int nFound = 0;
|
||||
|
||||
nFound = 0;
|
||||
nCandidate = nStart;
|
||||
nReturnAddr = 0;
|
||||
|
||||
while(nCandidate != nEnd)
|
||||
//First pass : Find stack alloc/release ranges
|
||||
{
|
||||
nOp = m_pCtx->m_pMemoryMap->GetInstruction(nCandidate);
|
||||
if((nOp & 0xFFFF0000) == 0x27BD0000)
|
||||
uint32 nCandidate = nStart;
|
||||
while(nCandidate != nEnd)
|
||||
{
|
||||
//Found the head of a routine (stack allocation)
|
||||
nStackAmount = 0 - (int16)(nOp & 0xFFFF);
|
||||
//Look for a JR RA
|
||||
nTemp = nCandidate;
|
||||
while(nTemp != nEnd)
|
||||
uint32 nReturnAddr = 0;
|
||||
uint32 nOp = m_pCtx->m_pMemoryMap->GetInstruction(nCandidate);
|
||||
if((nOp & 0xFFFF0000) == 0x27BD0000)
|
||||
{
|
||||
nOp = m_pCtx->m_pMemoryMap->GetInstruction(nTemp);
|
||||
|
||||
//Check SD RA, 0x0000(SP)
|
||||
if((nOp & 0xFFFF0000) == 0xFFBF0000)
|
||||
//Found the head of a routine (stack allocation)
|
||||
uint32 nStackAmount = 0 - (int16)(nOp & 0xFFFF);
|
||||
//Look for a JR RA
|
||||
uint32 nTemp = nCandidate;
|
||||
while(nTemp != nEnd)
|
||||
{
|
||||
nReturnAddr = (nOp & 0xFFFF);
|
||||
nOp = m_pCtx->m_pMemoryMap->GetInstruction(nTemp);
|
||||
|
||||
//Check SD RA, 0x0000(SP)
|
||||
if((nOp & 0xFFFF0000) == 0xFFBF0000)
|
||||
{
|
||||
nReturnAddr = (nOp & 0xFFFF);
|
||||
}
|
||||
|
||||
//Check for JR RA or J
|
||||
if((nOp == 0x03E00008) || ((nOp & 0xFC000000) == 0x08000000))
|
||||
{
|
||||
//Check if there's a stack unwinding instruction above or below
|
||||
|
||||
//Check above
|
||||
//ADDIU SP, SP, 0x????
|
||||
//JR RA
|
||||
|
||||
nOp = m_pCtx->m_pMemoryMap->GetInstruction(nTemp - 4);
|
||||
if((nOp & 0xFFFF0000) == 0x27BD0000)
|
||||
{
|
||||
if(nStackAmount == (int16)(nOp & 0xFFFF))
|
||||
{
|
||||
//That's good...
|
||||
InsertSubroutine(nCandidate, nTemp + 4, nCandidate, nTemp - 4, nStackAmount, nReturnAddr);
|
||||
nCandidate = nTemp + 4;
|
||||
nFound++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Check below
|
||||
//JR RA
|
||||
//ADDIU SP, SP, 0x????
|
||||
|
||||
nOp = m_pCtx->m_pMemoryMap->GetInstruction(nTemp + 4);
|
||||
if((nOp & 0xFFFF0000) == 0x27BD0000)
|
||||
{
|
||||
if(nStackAmount == (int16)(nOp & 0xFFFF))
|
||||
{
|
||||
//That's good
|
||||
InsertSubroutine(nCandidate, nTemp + 4, nCandidate, nTemp + 4, nStackAmount, nReturnAddr);
|
||||
nCandidate = nTemp + 4;
|
||||
nFound++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
//No stack unwinding was found... just forget about this one
|
||||
//break;
|
||||
}
|
||||
nTemp += 4;
|
||||
}
|
||||
}
|
||||
nCandidate += 4;
|
||||
}
|
||||
}
|
||||
|
||||
//Second pass : Search for all JAL targets then scan for functions
|
||||
{
|
||||
std::set<uint32> subroutineAddresses;
|
||||
for(uint32 address = nStart; address <= nEnd; address += 4)
|
||||
{
|
||||
uint32 nOp = m_pCtx->m_pMemoryMap->GetInstruction(address);
|
||||
if(
|
||||
(nOp & 0xFC000000) == 0x0C000000 ||
|
||||
(nOp & 0xFC000000) == 0x08000000)
|
||||
{
|
||||
uint32 jumpTarget = (nOp & 0x03FFFFFF) * 4;
|
||||
subroutineAddresses.insert(jumpTarget);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto subroutineAddressIterator(std::begin(subroutineAddresses));
|
||||
subroutineAddressIterator != std::end(subroutineAddresses); ++subroutineAddressIterator)
|
||||
{
|
||||
uint32 subroutineAddress = *subroutineAddressIterator;
|
||||
if(subroutineAddress == 0) continue;
|
||||
|
||||
//Don't bother if we already found it
|
||||
if(FindSubroutine(subroutineAddress)) continue;
|
||||
|
||||
//Otherwise, try to find a function that already exists
|
||||
for(uint32 address = subroutineAddress; address <= nEnd; address += 4)
|
||||
{
|
||||
uint32 nOp = m_pCtx->m_pMemoryMap->GetInstruction(address);
|
||||
|
||||
//Check for JR RA or J
|
||||
if((nOp == 0x03E00008) || ((nOp & 0xFC000000) == 0x08000000))
|
||||
{
|
||||
//Check if there's a stack unwinding instruction above or below
|
||||
|
||||
//Check above
|
||||
//ADDIU SP, SP, 0x????
|
||||
//JR RA
|
||||
|
||||
nOp = m_pCtx->m_pMemoryMap->GetInstruction(nTemp - 4);
|
||||
if((nOp & 0xFFFF0000) == 0x27BD0000)
|
||||
{
|
||||
if(nStackAmount == (int16)(nOp & 0xFFFF))
|
||||
{
|
||||
//That's good...
|
||||
InsertSubroutine(nCandidate, nTemp + 4, nCandidate, nTemp - 4, nStackAmount, nReturnAddr);
|
||||
nCandidate = nTemp + 4;
|
||||
nFound++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Check below
|
||||
//JR RA
|
||||
//ADDIU SP, SP, 0x????
|
||||
|
||||
nOp = m_pCtx->m_pMemoryMap->GetInstruction(nTemp + 4);
|
||||
if((nOp & 0xFFFF0000) == 0x27BD0000)
|
||||
{
|
||||
if(nStackAmount == (int16)(nOp & 0xFFFF))
|
||||
{
|
||||
//That's good
|
||||
InsertSubroutine(nCandidate, nTemp + 4, nCandidate, nTemp + 4, nStackAmount, nReturnAddr);
|
||||
nCandidate = nTemp + 4;
|
||||
nFound++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
//No stack unwinding was found... just forget about this one
|
||||
//break;
|
||||
InsertSubroutine(subroutineAddress, address + 4, 0, 0, 0, 0);
|
||||
nFound++;
|
||||
break;
|
||||
}
|
||||
|
||||
auto subroutine = FindSubroutine(address);
|
||||
if(subroutine)
|
||||
{
|
||||
//Function already exists, merge.
|
||||
ChangeSubroutineStart(subroutine->nStart, subroutineAddress);
|
||||
break;
|
||||
}
|
||||
nTemp += 4;
|
||||
}
|
||||
}
|
||||
nCandidate += 4;
|
||||
}
|
||||
printf("CMIPSAnalysis: Found %i subroutines in the range [0x%0.8X, 0x%0.8X].\r\n", nFound, nStart, nEnd);
|
||||
|
||||
printf("CMIPSAnalysis: Found %d subroutines in the range [0x%0.8X, 0x%0.8X].\r\n", nFound, nStart, nEnd);
|
||||
}
|
||||
|
||||
CMIPSAnalysis::SUBROUTINE* CMIPSAnalysis::FindSubroutine(uint32 nAddress)
|
||||
const CMIPSAnalysis::SUBROUTINE* CMIPSAnalysis::FindSubroutine(uint32 nAddress) const
|
||||
{
|
||||
pair<SubroutineList::iterator, SubroutineList::iterator> Iterators;
|
||||
Iterators = m_Subroutines.equal_range(HASH(nAddress));
|
||||
|
||||
for(SubroutineList::iterator itSubroutine(Iterators.first);
|
||||
itSubroutine != Iterators.second; itSubroutine++)
|
||||
auto subroutineIterator = m_subroutines.lower_bound(nAddress);
|
||||
if(subroutineIterator == std::end(m_subroutines)) return nullptr;
|
||||
|
||||
auto& subroutine = subroutineIterator->second;
|
||||
if(nAddress >= subroutine.nStart && nAddress <= subroutine.nEnd)
|
||||
{
|
||||
SUBROUTINE& Subroutine = (*itSubroutine).second;
|
||||
|
||||
if(nAddress >= Subroutine.nStart)
|
||||
{
|
||||
if(nAddress <= Subroutine.nEnd)
|
||||
{
|
||||
return &Subroutine;
|
||||
}
|
||||
}
|
||||
return &subroutine;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -22,14 +22,17 @@ public:
|
||||
CMIPSAnalysis(CMIPS*);
|
||||
~CMIPSAnalysis();
|
||||
void Analyse(uint32, uint32);
|
||||
SUBROUTINE* FindSubroutine(uint32);
|
||||
const SUBROUTINE* FindSubroutine(uint32) const;
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
typedef std::multimap<uint32, SUBROUTINE> SubroutineList;
|
||||
typedef std::map<uint32, SUBROUTINE, std::greater<uint32>> SubroutineList;
|
||||
|
||||
void InsertSubroutine(uint32, uint32, uint32, uint32, uint32, uint32);
|
||||
void ChangeSubroutineStart(uint32, uint32);
|
||||
|
||||
CMIPS* m_pCtx;
|
||||
SubroutineList m_Subroutines;
|
||||
SubroutineList m_subroutines;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -328,6 +328,7 @@ std::pair<uint32, uint32> CPS2OS::GetExecutableRange() const
|
||||
if(p != NULL)
|
||||
{
|
||||
uint32 end = p->nVAddress + p->nFileSize;
|
||||
if(end >= PS2::EERAMSIZE) continue;
|
||||
nMinAddr = std::min<uint32>(nMinAddr, p->nVAddress);
|
||||
nMaxAddr = std::max<uint32>(nMaxAddr, end);
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ void CCallStackWnd::Update()
|
||||
|
||||
m_pList->DeleteAllItems();
|
||||
|
||||
CMIPSAnalysis::SUBROUTINE* pRoutine = m_pCtx->m_pAnalysis->FindSubroutine(nPC);
|
||||
const CMIPSAnalysis::SUBROUTINE* pRoutine = m_pCtx->m_pAnalysis->FindSubroutine(nPC);
|
||||
if(pRoutine == NULL)
|
||||
{
|
||||
//Cannot go further
|
||||
|
@ -763,7 +763,7 @@ void CDisAsm::Paint(HDC hDC)
|
||||
TextOut(hDC, 20, nY, sTemp, (int)_tcslen(sTemp));
|
||||
}
|
||||
|
||||
CMIPSAnalysis::SUBROUTINE* pSub = m_pCtx->m_pAnalysis->FindSubroutine(nAddress);
|
||||
const CMIPSAnalysis::SUBROUTINE* pSub = m_pCtx->m_pAnalysis->FindSubroutine(nAddress);
|
||||
if(pSub != NULL)
|
||||
{
|
||||
SelectObject(hDC, nLtGrayPen);
|
||||
|
Loading…
Reference in New Issue
Block a user