// Copyright (c) 2012- PPSSPP Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0 or later versions. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #ifdef _WIN32 #include "Common/CommonWindows.h" #include #else #include #endif #include #include "Common/FileUtil.h" #include "Globals.h" #include "Core/MemMap.h" #include "Core/Debugger/SymbolMap.h" SymbolMap symbolMap; //need improvement static u32 hasher(u32 last, u32 value) { return __rotl(last,3) ^ value; } //#define BWLINKS // TODO: This should ignore immediates of many instructions, in order to be less sensitive. If it did, // this could work okay. static u32 ComputeHash(u32 start, u32 size) { u32 hash=0; for (unsigned int i=start; i> 26) { case 18: { if (LK) { u32 addr; if(AA) addr = SignExt26(LI << 2); else addr = ptr + SignExt26(LI << 2); int funNum = GetSymbolNum(addr); if (funNum >= 0) entries[funNum].backwardLinks.push_back(ptr); } break; } default: ; } ptr += 4; } } #endif } void SymbolMap::Clear() { lock_guard guard(lock_); #ifdef BWLINKS for (int i=0; i 0; i--) { if (line[i] == '\r' || line[i] == '\n') { line[i] = '\0'; } } if (strlen(line) < 4 || sscanf(line, "%s", temp) != 1) continue; if (strcmp(temp,"UNUSED")==0) continue; if (strcmp(temp,".text")==0) {started=true;continue;}; if (strcmp(temp,".init")==0) {started=true;continue;}; if (strcmp(temp,"Starting")==0) continue; if (strcmp(temp,"extab")==0) continue; if (strcmp(temp,".ctors")==0) break; if (strcmp(temp,".dtors")==0) break; if (strcmp(temp,".rodata")==0) continue; if (strcmp(temp,".data")==0) continue; if (strcmp(temp,".sbss")==0) continue; if (strcmp(temp,".sdata")==0) continue; if (strcmp(temp,".sdata2")==0) continue; if (strcmp(temp,"address")==0) continue; if (strcmp(temp,"-----------------------")==0) continue; if (strcmp(temp,".sbss2")==0) break; if (temp[1]==']') continue; if (!started) continue; MapEntry e; memset(&e, 0, sizeof(e)); sscanf(line,"%08x %08x %08x %i %127c",&e.address,&e.size,&e.vaddress,(int*)&e.type,e.name); if (e.type == ST_DATA && e.size==0) e.size=4; //e.vaddress|=0x80000000; if (strcmp(e.name,".text")==0 || strcmp(e.name,".init")==0 || strlen(e.name)<=1) { ; } else { e.UndecorateName(); entries.push_back(e); uniqueEntries.insert((const MapEntryUniqueInfo)e); entryRanges[e.vaddress + e.size] = e.vaddress; } } fclose(f); SortSymbols(); // SymbolMap::AnalyzeBackwards(); return true; } void SymbolMap::SaveSymbolMap(const char *filename) const { lock_guard guard(lock_); FILE *f = File::OpenCFile(filename, "w"); if (!f) return; fprintf(f,".text\n"); for (auto it = entries.begin(), end = entries.end(); it != end; ++it) { const MapEntry &e = *it; fprintf(f,"%08x %08x %08x %i %s\n",e.address,e.size,e.vaddress,e.type,e.name); } fclose(f); } bool SymbolMap::LoadNocashSym(const char *filename) { lock_guard guard(lock_); FILE *f = File::OpenCFile(filename, "r"); if (!f) return false; while (!feof(f)) { char line[256], value[256] = {0}; char *p = fgets(line,256,f); if(p == NULL) break; u32 address; if (sscanf(line,"%08X %s",&address,value) != 2) continue; if (address == 0 && strcmp(value,"0") == 0) continue; if (value[0] == '.') // data directives { continue; // not supported yet } else { // labels int size = 1; char* seperator = strchr(value,','); if (seperator != NULL) { *seperator = 0; sscanf(seperator+1,"%08X",&size); } AddSymbol(value,address,size,ST_FUNCTION); } } fclose(f); return true; } int SymbolMap::GetSymbolNum(unsigned int address, SymbolType symmask) const { lock_guard guard(lock_); for (size_t i = 0, n = entries.size(); i < n; i++) { const MapEntry &entry = entries[i]; unsigned int addr = entry.vaddress; if (address >= addr) { if (address < addr + entry.size) { if (entries[i].type & symmask) return (int) i; else return -1; } } else break; } return -1; } bool SymbolMap::GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask) const { lock_guard guard(lock_); // entryRanges is indexed by end. The first entry after address should contain address. // Otherwise, we have no entry that contains it, unless things overlap (which they shouldn't.) const auto containingEntry = entryRanges.upper_bound(address); if (containingEntry == entryRanges.end()) return false; // The next most common case is a single symbol by start address. // So we optimize for that by looking in our uniqueEntry set. u32 start_address = containingEntry->second; if (start_address <= address) { const MapEntryUniqueInfo searchKey = {start_address, start_address}; const auto entry = uniqueEntries.find(searchKey); // In case there were duplicates at some point, double check the end address. if (entry != uniqueEntries.end() && entry->vaddress + entry->size > address && (entry->type & symmask) != 0) { info->address = entry->vaddress; info->size = entry->size; return true; } } // Fall back to a slower scan. int n = GetSymbolNum(address, symmask); if (n != -1) { info->address = GetSymbolAddr(n); info->size = GetSymbolSize(n); return true; } return false; } const char* SymbolMap::getDirectSymbol(u32 address) { lock_guard guard(lock_); SymbolInfo info; if (GetSymbolInfo(&info,address) == false) return NULL; if (info.address != address) return NULL; // has to be the START of the function // now we need the name... which we can't just get from GetSymbolInfo because of the // unique entries. But, there are so many less instances where there actually IS a // label that the speed up is still massive for (auto it = entries.begin(), end = entries.end(); it != end; ++it) { const MapEntry &entry = *it; unsigned int addr = entry.vaddress; if (addr == address) return entry.name; } return NULL; } bool SymbolMap::getSymbolValue(char* symbol, u32& dest) { lock_guard guard(lock_); for (auto it = entries.begin(), end = entries.end(); it != end; ++it) { const MapEntry &entry = *it; #ifdef _WIN32 if (_stricmp(entry.name,symbol) == 0) #else if (strcasecmp(entry.name,symbol) == 0) #endif { dest = entry.vaddress; return true; } } return false; } static char descriptionTemp[256]; const char *SymbolMap::GetDescription(unsigned int address) const { int fun = SymbolMap::GetSymbolNum(address); //if (address == entries[fun].vaddress) //{ if (fun!=-1) return entries[fun].name; else { sprintf(descriptionTemp, "(%08x)", address); return descriptionTemp; } //} //else // return ""; } #ifdef _WIN32 static const int defaultSymbolsAddresses[] = { 0x08800000, 0x08804000, 0x04000000, 0x88000000, 0x00010000 }; static const char* defaultSymbolsNames[] = { "User memory", "Default load address", "VRAM","Kernel memory","Scratchpad" }; static const int defaultSymbolsAmount = sizeof(defaultSymbolsAddresses)/sizeof(const int); void SymbolMap::FillSymbolListBox(HWND listbox,SymbolType symmask) const { BOOL visible = ShowWindow(listbox,SW_HIDE); ListBox_ResetContent(listbox); if (symmask & ST_DATA) { for (int i = 0; i < defaultSymbolsAmount; i++) { wchar_t temp[256]; wsprintf(temp, L"0x%08X (%S)", defaultSymbolsAddresses[i], defaultSymbolsNames[i]); int index = ListBox_AddString(listbox,temp); ListBox_SetItemData(listbox,index,defaultSymbolsAddresses[i]); } } lock_guard guard(lock_); SendMessage(listbox, WM_SETREDRAW, FALSE, 0); SendMessage(listbox, LB_INITSTORAGE, (WPARAM)entries.size(), (LPARAM)entries.size() * 30); for (auto it = entries.begin(), end = entries.end(); it != end; ++it) { const MapEntry &entry = *it; if (entry.type & symmask) { wchar_t temp[256]; if (entry.type & ST_FUNCTION || !(entry.type & ST_DATA)) { wsprintf(temp, L"%S", entry.name); } else { wsprintf(temp, L"0x%08X (%S)", entry.vaddress, entry.name); } int index = ListBox_AddString(listbox,temp); ListBox_SetItemData(listbox,index,entry.vaddress); } } SendMessage(listbox, WM_SETREDRAW, TRUE, 0); RedrawWindow(listbox, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); if (visible) ShowWindow(listbox,SW_SHOW); } void SymbolMap::FillSymbolComboBox(HWND listbox,SymbolType symmask) const { ShowWindow(listbox,SW_HIDE); ComboBox_ResetContent(listbox); //int style = GetWindowLong(listbox,GWL_STYLE); ComboBox_AddString(listbox, L"(0x02000000)"); ComboBox_SetItemData(listbox, 0, 0x02000000); //ListBox_AddString(listbox, L"(0x80002000)"); //ListBox_SetItemData(listbox, 1, 0x80002000); lock_guard guard(lock_); SendMessage(listbox, WM_SETREDRAW, FALSE, 0); SendMessage(listbox, CB_INITSTORAGE, (WPARAM)entries.size(), (LPARAM)entries.size() * 30 * sizeof(wchar_t)); for (size_t i = 0, end = entries.size(); i < end; ++i) { const MapEntry &entry = entries[i]; if (entry.type & symmask) { wchar_t temp[256]; wsprintf(temp, L"%S (%d)", entry.name, entry.size); int index = ComboBox_AddString(listbox,temp); ComboBox_SetItemData(listbox,index,entry.vaddress); } } SendMessage(listbox, WM_SETREDRAW, TRUE, 0); RedrawWindow(listbox, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); ShowWindow(listbox,SW_SHOW); } void SymbolMap::FillListBoxBLinks(HWND listbox, int num) const { ListBox_ResetContent(listbox); lock_guard guard(lock_); int style = GetWindowLong(listbox,GWL_STYLE); const MapEntry &e = entries[num]; #ifdef BWLINKS for (int i=0; i::iterator it = uniqueEntries.find((const MapEntryUniqueInfo) e); if (it != uniqueEntries.end()){ MapEntryUniqueInfo temp = *it; temp.size = newSize; uniqueEntries.erase(it); uniqueEntries.insert(temp); } entryRanges.erase(e.vaddress + e.size); entryRanges.insert(std::pair(e.vaddress+newSize,e.vaddress)); e.size = newSize; } u32 SymbolMap::GetSymbolAddr(int i) const { return entries[i].vaddress; } u32 SymbolMap::GetSymbolSize(int i) const { return entries[i].size; } int SymbolMap::FindSymbol(const char *name) const { lock_guard guard(lock_); for (size_t i = 0; i < entries.size(); i++) if (strcmp(entries[i].name,name)==0) return (int) i; return -1; } u32 SymbolMap::GetAddress(int num) const { return entries[num].vaddress; } void SymbolMap::IncreaseRunCount(int num) { entries[num].runCount++; } unsigned int SymbolMap::GetRunCount(int num) const { if (num>=0) return entries[num].runCount; else return 0; } // Load an elf with symbols, use SymbolMap::compilefuncsignaturesfile // to make a symbol map, load a dol or somethin without symbols, then apply // the map with SymbolMap::usefuncsignaturesfile. void SymbolMap::CompileFuncSignaturesFile(const char *filename) const { // Store name,length,first instruction,hash into file FILE *f = File::OpenCFile(filename, "w"); fprintf(f,"00000000\n"); int count=0; for (auto it = entries.begin(), end = entries.end(); it != end; ++it) { const MapEntry &entry = *it; int size = entry.size; if (size >= 16 && entry.type == ST_FUNCTION) { u32 inst = Memory::Read_Instruction(entry.vaddress).encoding; //try to make a bigger number of different vals sometime if (inst != 0) { char temp[64]; strncpy(temp,entry.name,63); fprintf(f, "%08x\t%08x\t%08x\t%s\n", inst, size, ComputeHash(entry.vaddress,size), temp); count++; } } } fseek(f,0,SEEK_SET); fprintf(f,"%08x",count); fclose(f); } struct Sig { u32 inst; u32 size; u32 hash; char name[64]; Sig(){} Sig(u32 _inst, u32 _size, u32 _hash, char *_name) : inst(_inst), size(_size), hash(_hash) { strncpy(name,_name,63); } bool operator <(const Sig &other) const { return inst < other.inst; } }; std::vector sigs; typedef std::map Sigmap; Sigmap sigmap; void SymbolMap::UseFuncSignaturesFile(const char *filename, u32 maxAddress) { sigs.clear(); // symbolMap.Clear(); //#1: Read the signature file and put them in a fast data structure FILE *f = File::OpenCFile(filename, "r"); int count; if (fscanf(f, "%08x\n", &count) != 1) count = 0; char name[256]; for (int a=0; asecond; while (true) { if (sig->inst != inst) break; u32 hash = ComputeHash(addr,sig->size); if (hash==sig->hash) { //MATCH!!!! MapEntry e; e.address=addr; e.size= sig->size; e.vaddress = addr; e.type=ST_FUNCTION; strcpy(e.name,sig->name); addr+=sig->size-4; //don't need to check function interior entries.push_back(e); uniqueEntries.insert((const MapEntryUniqueInfo)e); entryRanges[e.vaddress + e.size] = e.vaddress; break; } sig++; } } } //ensure code coloring even if symbols were loaded before SymbolMap::SortSymbols(); }