Merge pull request #1702 from CarlKenner/DebugSymbols

Fix many bugs with the Symbols menu (when run with -d argument).
This commit is contained in:
skidau 2014-12-17 22:04:56 +11:00
commit 5313dd1b8c
10 changed files with 329 additions and 29 deletions

View File

@ -67,7 +67,8 @@ void CBoot::UpdateDebugger_MapLoaded()
}
bool CBoot::FindMapFile(std::string* existing_map_file,
std::string* writable_map_file)
std::string* writable_map_file,
std::string* title_id)
{
std::string title_id_str;
size_t name_begin_index;
@ -109,6 +110,9 @@ bool CBoot::FindMapFile(std::string* existing_map_file,
if (writable_map_file)
*writable_map_file = File::GetUserPath(D_MAPS_IDX) + title_id_str + ".map";
if (title_id)
*title_id = title_id_str;
bool found = false;
static const std::string maps_directories[] = {
File::GetUserPath(D_MAPS_IDX),

View File

@ -36,9 +36,12 @@ public:
// If writable_map_file is not nullptr, it is set to the path to where a map
// file should be saved.
//
// If title_id is not nullptr, it is set to the title id
//
// Returns true if a map file exists, false if none could be found.
static bool FindMapFile(std::string* existing_map_file,
std::string* writable_map_file);
std::string* writable_map_file,
std::string* title_id = nullptr);
private:
static void RunFunction(u32 _iAddr);

View File

@ -63,7 +63,7 @@ void PPCSymbolDB::AddKnownSymbol(u32 startAddr, u32 size, const std::string& nam
// already got it, let's just update name, checksum & size to be sure.
Symbol *tempfunc = &iter->second;
tempfunc->name = name;
tempfunc->hash = SignatureDB::ComputeCodeChecksum(startAddr, startAddr + size);
tempfunc->hash = SignatureDB::ComputeCodeChecksum(startAddr, startAddr + size - 4);
tempfunc->type = type;
tempfunc->size = size;
}
@ -195,22 +195,49 @@ void PPCSymbolDB::LogFunctionCall(u32 addr)
}
}
// The use case for handling bad map files is when you have a game with a map file on the disc,
// but you can't tell whether that map file is for the particular release version used in that game,
// or when you know that the map file is not for that build, but perhaps half the functions in the
// map file are still at the correct locations. Which are both common situations. It will load any
// function names and addresses that have a BLR before the start and at the end, but ignore any that
// don't, and then tell you how many were good and how many it ignored. That way you either find out
// it is all good and use it, find out it is partly good and use the good part, or find out that only
// a handful of functions lined up by coincidence and then you can clear the symbols. In the future I
// want to make it smarter, so it checks that there are no BLRs in the middle of the function
// (by checking the code length), and also make it cope with added functions in the middle or work
// based on the order of the functions and their approximate length. Currently that process has to be
// done manually and is very tedious.
// The use case for separate handling of map files that aren't bad is that you usually want to also
// load names that aren't functions(if included in the map file) without them being rejected as invalid.
// You can see discussion about these kinds of issues here : https://forums.oculus.com/viewtopic.php?f=42&t=11241&start=580
// https://m2k2.taigaforum.com/post/metroid_prime_hacking_help_25.html#metroid_prime_hacking_help_25
// This one can load both leftover map files on game discs (like Zelda), and mapfiles
// produced by SaveSymbolMap below.
bool PPCSymbolDB::LoadMap(const std::string& filename)
// bad=true means carefully load map files that might not be from exactly the right version
bool PPCSymbolDB::LoadMap(const std::string& filename, bool bad)
{
File::IOFile f(filename, "r");
if (!f)
return false;
bool started = false;
// four columns are used in American Mensa Academy map files and perhaps other games
bool started = false, four_columns = false;
int good_count = 0, bad_count = 0;
char line[512];
while (fgets(line, 512, f.GetHandle()))
{
if (strlen(line) < 4)
size_t length = strlen(line);
if (length < 4)
continue;
if (length == 34 && strcmp(line, " address Size address offset\n") == 0)
{
four_columns = true;
continue;
}
char temp[256];
sscanf(line, "%255s", temp);
@ -233,9 +260,58 @@ bool PPCSymbolDB::LoadMap(const std::string& filename)
if (!started) continue;
u32 address, vaddress, size, unknown;
char name[512];
u32 address, vaddress, size, offset, unknown;
char name[512], container[512];
if (four_columns)
{
// sometimes there is no unknown number, and sometimes it is because it is an entry of something else
if (length > 37 && line[37]==' ')
{
unknown = 0;
sscanf(line, "%08x %08x %08x %08x %511s", &address, &size, &vaddress, &offset, name);
char *s = strstr(line, "(entry of ");
if (s)
{
sscanf(s + 10, "%511s", container);
char *s2 = (strchr(container, ')'));
if (s2 && container[0]!='.')
{
s2[0] = '\0';
strcat(container, "::");
strcat(container, name);
strcpy(name, container);
}
}
}
else
{
sscanf(line, "%08x %08x %08x %08x %i %511s", &address, &size, &vaddress, &offset, &unknown, name);
}
}
// some entries in the table have a function name followed by " (entry of " followed by a container name, followed by ")"
// instead of a space followed by a number followed by a space followed by a name
else if (length > 27 && line[27] != ' ' && strstr(line, "(entry of "))
{
unknown = 0;
sscanf(line, "%08x %08x %08x %511s", &address, &size, &vaddress, name);
char *s = strstr(line, "(entry of ");
if (s)
{
sscanf(s + 10, "%511s", container);
char *s2 = (strchr(container, ')'));
if (s2 && container[0] != '.')
{
s2[0] = '\0';
strcat(container, "::");
strcat(container, name);
strcpy(name, container);
}
}
}
else
{
sscanf(line, "%08x %08x %08x %i %511s", &address, &size, &vaddress, &unknown, name);
}
const char *namepos = strstr(line, name);
if (namepos != nullptr) //would be odd if not :P
@ -250,13 +326,37 @@ bool PPCSymbolDB::LoadMap(const std::string& filename)
}
// Check if this is a valid entry.
if (strcmp(name, ".text") != 0 || strcmp(name, ".init") != 0 || strlen(name) > 0)
if (strcmp(name, ".text") != 0 && strcmp(name, ".init") != 0 && strlen(name) > 0)
{
vaddress |= 0x80000000;
bool good = !bad;
if (!good)
{
// check for BLR before function
u32 opcode = Memory::Read_Instruction(vaddress - 4);
if (opcode == 0x4e800020)
{
// check for BLR at end of function
opcode = Memory::Read_Instruction(vaddress + size - 4);
if (opcode == 0x4e800020)
good = true;
}
}
if (good)
{
++good_count;
AddKnownSymbol(vaddress | 0x80000000, size, name); // ST_FUNCTION
}
else
{
++bad_count;
}
}
}
Index();
if (bad)
SuccessAlertT("Loaded %d good functions, ignored %d bad functions.", good_count, bad_count);
return true;
}

View File

@ -34,7 +34,7 @@ public:
void FillInCallers();
bool LoadMap(const std::string& filename);
bool LoadMap(const std::string& filename, bool bad = false);
bool SaveMap(const std::string& filename, bool WithCodes = false) const;
void PrintCalls(u32 funcAddr) const;

View File

@ -77,7 +77,7 @@ bool SignatureDB::Save(const std::string& filename)
//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);
u32 hash = ComputeCodeChecksum(startAddr, startAddr + size - 4);
DBFunc temp_dbfunc;
temp_dbfunc.size = size;
@ -134,7 +134,8 @@ 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())
if ((prefix.empty() && (!symbol.second.name.empty()) && symbol.second.name.substr(0, 3) != "zz_" && symbol.second.name.substr(0, 1) != ".") ||
((!prefix.empty()) && symbol.second.name.substr(0, prefix.size()) == prefix))
{
DBFunc temp_dbfunc;
temp_dbfunc.name = symbol.second.name;

View File

@ -752,8 +752,13 @@ void CCodeWindow::UpdateButtonStates()
GetMenuBar()->Enable(IDM_SCANFUNCTIONS, Initialized);
GetMenuBar()->Enable(IDM_LOADMAPFILE, Initialized);
GetMenuBar()->Enable(IDM_SAVEMAPFILE, Initialized);
GetMenuBar()->Enable(IDM_LOADMAPFILEAS, Initialized);
GetMenuBar()->Enable(IDM_SAVEMAPFILEAS, Initialized);
GetMenuBar()->Enable(IDM_LOADBADMAPFILE, Initialized);
GetMenuBar()->Enable(IDM_SAVEMAPFILEWITHCODES, Initialized);
GetMenuBar()->Enable(IDM_CREATESIGNATUREFILE, Initialized);
GetMenuBar()->Enable(IDM_APPENDSIGNATUREFILE, Initialized);
GetMenuBar()->Enable(IDM_COMBINESIGNATUREFILES, Initialized);
GetMenuBar()->Enable(IDM_RENAME_SYMBOLS, Initialized);
GetMenuBar()->Enable(IDM_USESIGNATUREFILE, Initialized);
GetMenuBar()->Enable(IDM_PATCHHLEFUNCTIONS, Initialized);

View File

@ -146,11 +146,22 @@ void CCodeWindow::Save()
void CCodeWindow::CreateMenuSymbols(wxMenuBar *pMenuBar)
{
wxMenu *pSymbolsMenu = new wxMenu;
pSymbolsMenu->Append(IDM_CLEARSYMBOLS, _("&Clear symbols"));
pSymbolsMenu->Append(IDM_SCANFUNCTIONS, _("&Generate symbol map"));
pSymbolsMenu->Append(IDM_CLEARSYMBOLS, _("&Clear symbols"),
_("Remove names from all functions and variables."));
pSymbolsMenu->Append(IDM_SCANFUNCTIONS, _("&Generate symbol map"),
_("Recognise standard functions from sys\\totaldb.dsy, and use generic zz_ names for other functions."));
pSymbolsMenu->AppendSeparator();
pSymbolsMenu->Append(IDM_LOADMAPFILE, _("&Load symbol map"));
pSymbolsMenu->Append(IDM_SAVEMAPFILE, _("&Save symbol map"));
pSymbolsMenu->Append(IDM_LOADMAPFILE, _("&Load symbol map"),
_("Try to load this game's function names automatically - but doesn't check .map files stored on the disc image yet."));
pSymbolsMenu->Append(IDM_SAVEMAPFILE, _("&Save symbol map"),
_("Save the function names for each address to a .map file in your user settings map folder, named after the title id."));
pSymbolsMenu->AppendSeparator();
pSymbolsMenu->Append(IDM_LOADMAPFILEAS, _("Load &other map file..."),
_("Load any .map file containing the function names and addresses for this game."));
pSymbolsMenu->Append(IDM_LOADBADMAPFILE, _("Load &bad map file..."),
_("Try to load a .map file that might be from a slightly different version."));
pSymbolsMenu->Append(IDM_SAVEMAPFILEAS, _("Save symbol map &as..."),
_("Save the function names and addresses for this game as a .map file. If you want to open it in IDA pro, use the .idc script."));
pSymbolsMenu->AppendSeparator();
pSymbolsMenu->Append(IDM_SAVEMAPFILEWITHCODES, _("Save code"),
_("Save the entire disassembled code. This may take a several seconds"
@ -161,8 +172,14 @@ void CCodeWindow::CreateMenuSymbols(wxMenuBar *pMenuBar)
);
pSymbolsMenu->AppendSeparator();
pSymbolsMenu->Append(IDM_CREATESIGNATUREFILE, _("&Create signature file..."));
pSymbolsMenu->Append(IDM_USESIGNATUREFILE, _("&Use signature file..."));
pSymbolsMenu->Append(IDM_CREATESIGNATUREFILE, _("&Create signature file..."),
_("Create a .dsy file that can be used to recognise these same functions in other games."));
pSymbolsMenu->Append(IDM_APPENDSIGNATUREFILE, _("Append to &existing signature file..."),
_("Add any named functions missing from a .dsy file, so it can also recognise these additional functions in other games."));
pSymbolsMenu->Append(IDM_COMBINESIGNATUREFILES, _("Combine two signature files..."),
_("Make a new .dsy file which can recognise more functions, by combining two existing files. The first input file has priority."));
pSymbolsMenu->Append(IDM_USESIGNATUREFILE, _("Apply signat&ure file..."),
_("Must use Generate symbol map first! Recognise names of any standard library functions used in multiple games, by loading them from a .dsy file."));
pSymbolsMenu->AppendSeparator();
pSymbolsMenu->Append(IDM_PATCHHLEFUNCTIONS, _("&Patch HLE functions"));
pSymbolsMenu->Append(IDM_RENAME_SYMBOLS, _("&Rename symbols from file..."));
@ -222,9 +239,10 @@ void CCodeWindow::OnSymbolsMenu(wxCommandEvent& event)
if (!Core::IsRunning()) return;
std::string existing_map_file, writable_map_file;
std::string existing_map_file, writable_map_file, title_id_str;
bool map_exists = CBoot::FindMapFile(&existing_map_file,
&writable_map_file);
&writable_map_file,
&title_id_str);
switch (event.GetId())
{
case IDM_CLEARSYMBOLS:
@ -240,6 +258,7 @@ void CCodeWindow::OnSymbolsMenu(wxCommandEvent& event)
{
db.Apply(&g_symbolDB);
Parent->StatusBarMessage("Generated symbol names from '%s'", TOTALDB);
db.List();
}
else
{
@ -268,9 +287,55 @@ void CCodeWindow::OnSymbolsMenu(wxCommandEvent& event)
HLE::PatchFunctions();
NotifyMapLoaded();
break;
case IDM_LOADMAPFILEAS:
{
const wxString path = wxFileSelector(
_("Load map file"), File::GetUserPath(D_MAPS_IDX),
title_id_str + ".map", ".map",
"Dolphin Map File (*.map)|*.map|All files (*.*)|*.*",
wxFD_OPEN | wxFD_FILE_MUST_EXIST, this);
if (!path.IsEmpty())
{
g_symbolDB.LoadMap(WxStrToStr(path));
Parent->StatusBarMessage("Loaded symbols from '%s'", WxStrToStr(path).c_str());
}
HLE::PatchFunctions();
NotifyMapLoaded();
}
break;
case IDM_LOADBADMAPFILE:
{
const wxString path = wxFileSelector(
_("Load bad map file"), File::GetUserPath(D_MAPS_IDX),
title_id_str + ".map", ".map",
"Dolphin Map File (*.map)|*.map|All files (*.*)|*.*",
wxFD_OPEN | wxFD_FILE_MUST_EXIST, this);
if (!path.IsEmpty())
{
g_symbolDB.LoadMap(WxStrToStr(path), true);
Parent->StatusBarMessage("Loaded symbols from '%s'", WxStrToStr(path).c_str());
}
HLE::PatchFunctions();
NotifyMapLoaded();
}
break;
case IDM_SAVEMAPFILE:
g_symbolDB.SaveMap(writable_map_file);
break;
case IDM_SAVEMAPFILEAS:
{
const wxString path = wxFileSelector(
_("Save map file as"), File::GetUserPath(D_MAPS_IDX),
title_id_str + ".map", ".map",
"Dolphin Map File (*.map)|*.map|All files (*.*)|*.*",
wxFD_SAVE | wxFD_OVERWRITE_PROMPT, this);
if (!path.IsEmpty())
g_symbolDB.SaveMap(WxStrToStr(path));
}
break;
case IDM_SAVEMAPFILEWITHCODES:
g_symbolDB.SaveMap(writable_map_file, true);
break;
@ -323,14 +388,43 @@ void CCodeWindow::OnSymbolsMenu(wxCommandEvent& event)
std::string prefix(WxStrToStr(input_prefix.GetValue()));
wxString path = wxFileSelector(
_("Save signature as"), wxEmptyString, wxEmptyString, wxEmptyString,
"Dolphin Signature File (*.dsy)|*.dsy;", wxFD_SAVE,
_("Save signature as"), File::GetSysDirectory(), wxEmptyString, wxEmptyString,
"Dolphin Signature File (*.dsy)|*.dsy;", wxFD_SAVE | wxFD_OVERWRITE_PROMPT,
this);
if (!path.IsEmpty())
{
SignatureDB db;
db.Initialize(&g_symbolDB, prefix);
db.Save(WxStrToStr(path));
db.List();
}
}
}
break;
case IDM_APPENDSIGNATUREFILE:
{
wxTextEntryDialog input_prefix(
this,
_("Only export symbols with prefix:\n(Blank for all symbols)"),
wxGetTextFromUserPromptStr,
wxEmptyString);
if (input_prefix.ShowModal() == wxID_OK)
{
std::string prefix(WxStrToStr(input_prefix.GetValue()));
wxString path = wxFileSelector(
_("Append signature to"), File::GetSysDirectory(), wxEmptyString, wxEmptyString,
"Dolphin Signature File (*.dsy)|*.dsy;", wxFD_SAVE,
this);
if (!path.IsEmpty())
{
SignatureDB db;
db.Initialize(&g_symbolDB, prefix);
db.List();
db.Load(WxStrToStr(path));
db.Save(WxStrToStr(path));
db.List();
}
}
}
@ -338,7 +432,7 @@ void CCodeWindow::OnSymbolsMenu(wxCommandEvent& event)
case IDM_USESIGNATUREFILE:
{
wxString path = wxFileSelector(
_("Apply signature file"), wxEmptyString, wxEmptyString, wxEmptyString,
_("Apply signature file"), File::GetSysDirectory(), wxEmptyString, wxEmptyString,
"Dolphin Signature File (*.dsy)|*.dsy;", wxFD_OPEN | wxFD_FILE_MUST_EXIST,
this);
if (!path.IsEmpty())
@ -346,9 +440,38 @@ void CCodeWindow::OnSymbolsMenu(wxCommandEvent& event)
SignatureDB db;
db.Load(WxStrToStr(path));
db.Apply(&g_symbolDB);
}
}
db.List();
NotifyMapLoaded();
}
}
break;
case IDM_COMBINESIGNATUREFILES:
{
wxString path1 = wxFileSelector(
_("Choose priority input file"), File::GetSysDirectory(), wxEmptyString, wxEmptyString,
"Dolphin Signature File (*.dsy)|*.dsy;", wxFD_OPEN | wxFD_FILE_MUST_EXIST,
this);
if (!path1.IsEmpty())
{
SignatureDB db;
wxString path2 = wxFileSelector(
_("Choose secondary input file"), File::GetSysDirectory(), wxEmptyString, wxEmptyString,
"Dolphin Signature File (*.dsy)|*.dsy;", wxFD_OPEN | wxFD_FILE_MUST_EXIST,
this);
if (!path2.IsEmpty())
{
db.Load(WxStrToStr(path2));
db.Load(WxStrToStr(path1));
path2 = wxFileSelector(
_("Save combined output file as"), File::GetSysDirectory(), wxEmptyString, ".dsy",
"Dolphin Signature File (*.dsy)|*.dsy;", wxFD_SAVE | wxFD_OVERWRITE_PROMPT,
this);
db.Save(WxStrToStr(path2));
db.List();
}
}
}
break;
case IDM_PATCHHLEFUNCTIONS:
HLE::PatchFunctions();

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!--For some stupid reason this has to be in the .user file...-->

View File

@ -214,9 +214,11 @@ enum
// Symbols
IDM_CLEARSYMBOLS,
IDM_SCANFUNCTIONS,
IDM_LOADMAPFILE,
IDM_SAVEMAPFILE, IDM_SAVEMAPFILEWITHCODES,
IDM_LOADMAPFILE, IDM_LOADMAPFILEAS, IDM_LOADBADMAPFILE,
IDM_SAVEMAPFILE, IDM_SAVEMAPFILEWITHCODES, IDM_SAVEMAPFILEAS,
IDM_CREATESIGNATUREFILE,
IDM_APPENDSIGNATUREFILE,
IDM_COMBINESIGNATUREFILES,
IDM_RENAME_SYMBOLS,
IDM_USESIGNATUREFILE,
IDM_PATCHHLEFUNCTIONS,

62
Tools/ReadDolphinMap.idc Normal file
View File

@ -0,0 +1,62 @@
/* ReadDolphinMap.idc
Loads Dolphin .map files into IDA Pro.
Carl Kenner, 2014
*/
#include <idc.idc>
static main(void)
{
auto fh;
auto fname;
auto pusha, popa;
auto start, end;
auto success;
auto count;
auto line;
auto ea; // column 1
auto name; // column 30
auto p;
auto code;
fname = AskFile(0,"*.map","Load a .map file from Dolphin emulator...");
fh = fopen(fname, "r");
if (fh == 0) {
Message("Can't open %s\n", fname);
return;
}
Message("Loading %s dolphin map file:\n", fname);
for (count = 0; 1; count++) {
line = readstr(fh);
if (line == -1)
break;
if (strlen(line)>30 && line[0]!=" ") {
ea = xtol(substr(line,0,8));
name = substr(line,29,strlen(line)-1);
if (substr(name,0,3)!="zz_") {
if (!MakeNameEx(ea,name,SN_NOCHECK | SN_PUBLIC | SN_NON_AUTO |SN_NOWARN)) {
MakeNameEx(ea,name+"_2",SN_NOCHECK | SN_PUBLIC | SN_NON_AUTO );
}
Message("ea='%x', name='%s'\n", ea, name);
} else {
MakeNameEx(ea,name,SN_NOCHECK | SN_PUBLIC | SN_AUTO | SN_WEAK | SN_NOWARN);
}
} else if (strlen(line)>30) {
ea = xtol(substr(line,18,18+8));
p = strstr(line, " \t");
if (p>=30 && ea!=0) {
name = substr(line,30,p);
code = substr(line,p+2,strlen(line));
SetFunctionCmt(ea, code, 0);
if (!MakeNameEx(ea,name,SN_NOCHECK | SN_PUBLIC | SN_NON_AUTO |SN_NOWARN)) {
MakeNameEx(ea,name+"_2",SN_NOCHECK | SN_PUBLIC | SN_NON_AUTO );
}
}
}
}
Message("Dolphin map file done.\n");
}