dolphin/Source/Core/Core/PowerPC/SignatureDB.cpp
2014-09-08 15:39:58 -04:00

206 lines
4.4 KiB
C++

// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <string>
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Core/HW/Memmap.h"
#include "Core/PowerPC/PPCAnalyst.h"
#include "Core/PowerPC/PPCSymbolDB.h"
#include "Core/PowerPC/SignatureDB.h"
namespace
{
// On-disk format for SignatureDB entries.
struct FuncDesc
{
u32 checkSum;
u32 size;
char name[128];
};
} // namespace
bool SignatureDB::Load(const std::string& filename)
{
File::IOFile f(filename, "rb");
if (!f)
return false;
u32 fcount = 0;
f.ReadArray(&fcount, 1);
for (size_t i = 0; i < fcount; i++)
{
FuncDesc temp;
memset(&temp, 0, sizeof(temp));
f.ReadArray(&temp, 1);
temp.name[sizeof(temp.name)-1] = 0;
DBFunc dbf;
dbf.name = temp.name;
dbf.size = temp.size;
database[temp.checkSum] = dbf;
}
return true;
}
bool SignatureDB::Save(const std::string& filename)
{
File::IOFile f(filename, "wb");
if (!f)
{
ERROR_LOG(OSHLE, "Database save failed");
return false;
}
u32 fcount = (u32)database.size();
f.WriteArray(&fcount, 1);
for (const auto& entry : database)
{
FuncDesc temp;
memset(&temp, 0, sizeof(temp));
temp.checkSum = entry.first;
temp.size = entry.second.size;
strncpy(temp.name, entry.second.name.c_str(), 127);
f.WriteArray(&temp, 1);
}
INFO_LOG(OSHLE, "Database save successful");
return true;
}
//Adds a known function to the hash database
u32 SignatureDB::Add(u32 startAddr, u32 size, const std::string& name)
{
u32 hash = ComputeCodeChecksum(startAddr, startAddr + size);
DBFunc temp_dbfunc;
temp_dbfunc.size = size;
temp_dbfunc.name = name;
FuncDB::iterator iter = database.find(hash);
if (iter == database.end())
database[hash] = temp_dbfunc;
return hash;
}
void SignatureDB::List()
{
for (const auto& entry : database)
{
INFO_LOG(OSHLE, "%s : %i bytes, hash = %08x", entry.second.name.c_str(), entry.second.size, entry.first);
}
INFO_LOG(OSHLE, "%lu functions known in current database.",
(unsigned long)database.size());
}
void SignatureDB::Clear()
{
database.clear();
}
void SignatureDB::Apply(PPCSymbolDB *symbol_db)
{
for (const auto& entry : database)
{
u32 hash = entry.first;
Symbol *function = symbol_db->GetSymbolFromHash(hash);
if (function)
{
// Found the function. Let's rename it according to the symbol file.
if (entry.second.size == (unsigned int)function->size)
{
function->name = entry.second.name;
INFO_LOG(OSHLE, "Found %s at %08x (size: %08x)!", entry.second.name.c_str(), function->address, function->size);
}
else
{
function->name = entry.second.name;
ERROR_LOG(OSHLE, "Wrong size! Found %s at %08x (size: %08x instead of %08x)!",
entry.second.name.c_str(), function->address, function->size, entry.second.size);
}
}
}
symbol_db->Index();
}
void SignatureDB::Initialize(PPCSymbolDB *symbol_db, const std::string& prefix)
{
for (const auto& symbol : symbol_db->Symbols())
{
if ((symbol.second.name.substr(0, prefix.size()) == prefix) || prefix.empty())
{
DBFunc temp_dbfunc;
temp_dbfunc.name = symbol.second.name;
temp_dbfunc.size = symbol.second.size;
database[symbol.second.hash] = temp_dbfunc;
}
}
}
/*static*/ u32 SignatureDB::ComputeCodeChecksum(u32 offsetStart, u32 offsetEnd)
{
u32 sum = 0;
for (u32 offset = offsetStart; offset <= offsetEnd; offset += 4)
{
u32 opcode = Memory::Read_Instruction(offset);
u32 op = opcode & 0xFC000000;
u32 op2 = 0;
u32 op3 = 0;
u32 auxop = op >> 26;
switch (auxop)
{
case 4: //PS instructions
op2 = opcode & 0x0000003F;
switch ( op2 )
{
case 0:
case 8:
case 16:
case 21:
case 22:
op3 = opcode & 0x000007C0;
}
break;
case 7: //addi muli etc
case 8:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
op2 = opcode & 0x03FF0000;
break;
case 19: // MCRF??
case 31: //integer
case 63: //fpu
op2 = opcode & 0x000007FF;
break;
case 59: //fpu
op2 = opcode & 0x0000003F;
if (op2 < 16)
op3 = opcode & 0x000007C0;
break;
default:
if (auxop >= 32 && auxop < 56)
op2 = opcode & 0x03FF0000;
break;
}
// Checksum only uses opcode, not opcode data, because opcode data changes
// in all compilations, but opcodes don't!
sum = ( ( (sum << 17 ) & 0xFFFE0000 ) | ( (sum >> 15) & 0x0001FFFF ) );
sum = sum ^ (op | op2 | op3);
}
return sum;
}