1 // Copyright (c) 2012- PPSSPP Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
22 #include "ext/xxhash.h"
24 #include "Common/CommonTypes.h"
25 #include "Core/MemMap.h"
26 #include "Core/System.h"
27 #include "Core/MIPS/MIPSCodeUtils.h"
28 #include "Core/MIPS/MIPSTables.h"
29 #include "Core/Debugger/DebugInterface.h"
30 #include "Core/Debugger/SymbolMap.h"
31 #include "Core/Debugger/DisassemblyManager.h"
33 std::map
<u32
, DisassemblyEntry
*> DisassemblyManager::entries
;
34 DebugInterface
* DisassemblyManager::cpu
;
35 int DisassemblyManager::maxParamChars
= 29;
37 bool isInInterval(u32 start
, u32 size
, u32 value
)
39 return start
<= value
&& value
<= (start
+size
-1);
43 static u32
computeHash(u32 address
, u32 size
)
46 return XXH64(Memory::GetPointer(address
), size
, 0xBACD7814BACD7814LL
);
48 return XXH32(Memory::GetPointer(address
), size
, 0xBACD7814);
53 void parseDisasm(const char* disasm
, char* opcode
, char* arguments
, bool insertSymbols
)
56 while (*disasm
!= 0 && *disasm
!= '\t')
58 *opcode
++ = *disasm
++;
68 const char* jumpAddress
= strstr(disasm
,"->$");
69 const char* jumpRegister
= strstr(disasm
,"->");
73 if (disasm
== jumpAddress
)
76 sscanf(disasm
+3,"%08x",&branchTarget
);
78 const std::string addressSymbol
= symbolMap
.GetLabelString(branchTarget
);
79 if (!addressSymbol
.empty() && insertSymbols
)
81 arguments
+= sprintf(arguments
,"%s",addressSymbol
.c_str());
83 arguments
+= sprintf(arguments
,"0x%08X",branchTarget
);
90 if (disasm
== jumpRegister
)
98 *arguments
++ = *disasm
++;
104 std::map
<u32
,DisassemblyEntry
*>::iterator
findDisassemblyEntry(std::map
<u32
,DisassemblyEntry
*>& entries
, u32 address
, bool exact
)
107 return entries
.find(address
);
109 if (entries
.size() == 0)
110 return entries
.end();
112 // find first elem that's >= address
113 auto it
= entries
.lower_bound(address
);
114 if (it
!= entries
.end())
116 // it may be an exact match
117 if (isInInterval(it
->second
->getLineAddress(0),it
->second
->getTotalSize(),address
))
120 // otherwise it may point to the next
121 if (it
!= entries
.begin())
124 if (isInInterval(it
->second
->getLineAddress(0),it
->second
->getTotalSize(),address
))
129 // check last entry manually
130 auto rit
= entries
.rbegin();
131 if (isInInterval(rit
->second
->getLineAddress(0),rit
->second
->getTotalSize(),address
))
133 return (++rit
).base();
136 // no match otherwise
137 return entries
.end();
140 void DisassemblyManager::analyze(u32 address
, u32 size
= 1024)
142 u32 end
= address
+size
;
147 while (address
< end
&& start
<= address
)
152 auto it
= findDisassemblyEntry(entries
,address
,false);
153 if (it
!= entries
.end())
155 DisassemblyEntry
* entry
= it
->second
;
157 address
= entry
->getLineAddress(0)+entry
->getTotalSize();
162 if (!symbolMap
.GetSymbolInfo(&info
,address
,ST_ALL
))
166 u32 next
= std::min
<u32
>((address
+3) & ~3,symbolMap
.GetNextSymbolAddress(address
,ST_ALL
));
167 DisassemblyData
* data
= new DisassemblyData(address
,next
-address
,DATATYPE_BYTE
);
168 entries
[address
] = data
;
173 u32 next
= symbolMap
.GetNextSymbolAddress(address
,ST_ALL
);
175 if ((next
% 4) && next
!= (u32
)-1)
177 u32 alignedNext
= next
& ~3;
179 if (alignedNext
!= address
)
181 DisassemblyOpcode
* opcode
= new DisassemblyOpcode(address
,(alignedNext
-address
)/4);
182 entries
[address
] = opcode
;
185 DisassemblyData
* data
= new DisassemblyData(address
,next
-alignedNext
,DATATYPE_BYTE
);
186 entries
[alignedNext
] = data
;
188 DisassemblyOpcode
* opcode
= new DisassemblyOpcode(address
,(next
-address
)/4);
189 entries
[address
] = opcode
;
200 DisassemblyFunction
* function
= new DisassemblyFunction(info
.address
,info
.size
);
201 entries
[info
.address
] = function
;
202 address
= info
.address
+info
.size
;
207 DisassemblyData
* data
= new DisassemblyData(info
.address
,info
.size
,symbolMap
.GetDataType(info
.address
));
208 entries
[info
.address
] = data
;
209 address
= info
.address
+info
.size
;
219 std::vector
<BranchLine
> DisassemblyManager::getBranchLines(u32 start
, u32 size
)
221 std::vector
<BranchLine
> result
;
223 auto it
= findDisassemblyEntry(entries
,start
,false);
224 if (it
!= entries
.end())
228 it
->second
->getBranchLines(start
,size
,result
);
230 } while (it
!= entries
.end() && start
+size
> it
->second
->getLineAddress(0));
236 void DisassemblyManager::getLine(u32 address
, bool insertSymbols
, DisassemblyLineInfo
& dest
)
238 auto it
= findDisassemblyEntry(entries
,address
,false);
239 if (it
== entries
.end())
242 it
= findDisassemblyEntry(entries
,address
,false);
244 if (it
== entries
.end())
247 dest
.totalSize
= ((address
+3) & ~3)-address
;
251 dest
.params
= "Disassembly failure";
256 DisassemblyEntry
* entry
= it
->second
;
257 if (entry
->disassemble(address
,dest
,insertSymbols
))
261 dest
.totalSize
= ((address
+3) & ~3)-address
;
265 dest
.params
= "Disassembly failure";
268 u32
DisassemblyManager::getStartAddress(u32 address
)
270 auto it
= findDisassemblyEntry(entries
,address
,false);
271 if (it
== entries
.end())
274 it
= findDisassemblyEntry(entries
,address
,false);
275 if (it
== entries
.end())
279 DisassemblyEntry
* entry
= it
->second
;
280 int line
= entry
->getLineNum(address
,true);
281 return entry
->getLineAddress(line
);
284 u32
DisassemblyManager::getNthPreviousAddress(u32 address
, int n
)
286 while (Memory::IsValidAddress(address
))
288 auto it
= findDisassemblyEntry(entries
,address
,false);
290 while (it
!= entries
.end())
292 DisassemblyEntry
* entry
= it
->second
;
293 int oldLineNum
= entry
->getLineNum(address
,true);
294 int oldNumLines
= entry
->getNumLines();
297 return entry
->getLineAddress(oldLineNum
-n
);
300 address
= entry
->getLineAddress(0)-1;
302 it
= findDisassemblyEntry(entries
,address
,false);
305 analyze(address
-127,128);
311 u32
DisassemblyManager::getNthNextAddress(u32 address
, int n
)
313 while (Memory::IsValidAddress(address
))
315 auto it
= findDisassemblyEntry(entries
,address
,false);
317 while (it
!= entries
.end())
319 DisassemblyEntry
* entry
= it
->second
;
320 int oldLineNum
= entry
->getLineNum(address
,true);
321 int oldNumLines
= entry
->getNumLines();
322 if (oldLineNum
+n
< oldNumLines
)
324 return entry
->getLineAddress(oldLineNum
+n
);
327 address
= entry
->getLineAddress(0)+entry
->getTotalSize();
328 n
-= (oldNumLines
-oldLineNum
);
329 it
= findDisassemblyEntry(entries
,address
,false);
338 void DisassemblyManager::clear()
340 for (auto it
= entries
.begin(); it
!= entries
.end(); it
++)
347 DisassemblyFunction::DisassemblyFunction(u32 _address
, u32 _size
): address(_address
), size(_size
)
349 auto memLock
= Memory::Lock();
353 hash
= computeHash(address
,size
);
357 void DisassemblyFunction::recheck()
359 auto memLock
= Memory::Lock();
363 HashType newHash
= computeHash(address
,size
);
372 int DisassemblyFunction::getNumLines()
374 return (int) lineAddresses
.size();
377 int DisassemblyFunction::getLineNum(u32 address
, bool findStart
)
381 int last
= (int)lineAddresses
.size() - 1;
382 for (int i
= 0; i
< last
; i
++)
384 u32 next
= lineAddresses
[i
+ 1];
385 if (lineAddresses
[i
] <= address
&& next
> address
)
388 if (lineAddresses
[last
] <= address
&& this->address
+ this->size
> address
)
393 int last
= (int)lineAddresses
.size() - 1;
394 for (int i
= 0; i
< last
; i
++)
396 u32 next
= lineAddresses
[i
+ 1];
397 if (lineAddresses
[i
] == address
)
400 if (lineAddresses
[last
] == address
)
407 u32
DisassemblyFunction::getLineAddress(int line
)
409 return lineAddresses
[line
];
412 bool DisassemblyFunction::disassemble(u32 address
, DisassemblyLineInfo
& dest
, bool insertSymbols
)
414 auto it
= findDisassemblyEntry(entries
,address
,false);
415 if (it
== entries
.end())
418 return it
->second
->disassemble(address
,dest
,insertSymbols
);
421 void DisassemblyFunction::getBranchLines(u32 start
, u32 size
, std::vector
<BranchLine
>& dest
)
423 u32 end
= start
+size
;
425 for (size_t i
= 0; i
< lines
.size(); i
++)
427 BranchLine
& line
= lines
[i
];
429 u32 first
= line
.first
;
430 u32 second
= line
.second
;
432 // skip branches that are entirely before or entirely after the window
433 if ((first
< start
&& second
< start
) ||
434 (first
> end
&& second
> end
))
437 dest
.push_back(line
);
443 void DisassemblyFunction::generateBranchLines()
451 LaneInfo lanes
[NUM_LANES
];
452 for (int i
= 0; i
< NUM_LANES
; i
++)
453 lanes
[i
].used
= false;
455 u32 end
= address
+size
;
457 DebugInterface
* cpu
= DisassemblyManager::getCpu();
458 for (u32 funcPos
= address
; funcPos
< end
; funcPos
+= 4)
460 MIPSAnalyst::MipsOpcodeInfo opInfo
= MIPSAnalyst::GetOpcodeInfo(cpu
,funcPos
);
462 bool inFunction
= (opInfo
.branchTarget
>= address
&& opInfo
.branchTarget
< end
);
463 if (opInfo
.isBranch
&& !opInfo
.isBranchToRegister
&& !opInfo
.isLinkedBranch
&& inFunction
)
466 if (opInfo
.branchTarget
< funcPos
)
468 line
.first
= opInfo
.branchTarget
;
469 line
.second
= funcPos
;
472 line
.first
= funcPos
;
473 line
.second
= opInfo
.branchTarget
;
474 line
.type
= LINE_DOWN
;
477 lines
.push_back(line
);
481 std::sort(lines
.begin(),lines
.end());
482 for (size_t i
= 0; i
< lines
.size(); i
++)
484 for (int l
= 0; l
< NUM_LANES
; l
++)
486 if (lines
[i
].first
> lanes
[l
].end
)
487 lanes
[l
].used
= false;
491 for (int l
= 0; l
< NUM_LANES
; l
++)
493 if (lanes
[l
].used
== false)
506 lanes
[lane
].end
= lines
[i
].second
;
507 lanes
[lane
].used
= true;
508 lines
[i
].laneIndex
= lane
;
512 void DisassemblyFunction::addOpcodeSequence(u32 start
, u32 end
)
514 DisassemblyOpcode
* opcode
= new DisassemblyOpcode(start
,(end
-start
)/4);
515 entries
[start
] = opcode
;
516 for (u32 pos
= start
; pos
< end
; pos
+= 4)
518 lineAddresses
.push_back(pos
);
522 void DisassemblyFunction::load()
524 generateBranchLines();
526 // gather all branch targets
527 std::set
<u32
> branchTargets
;
528 for (size_t i
= 0; i
< lines
.size(); i
++)
530 switch (lines
[i
].type
)
533 branchTargets
.insert(lines
[i
].second
);
536 branchTargets
.insert(lines
[i
].first
);
543 DebugInterface
* cpu
= DisassemblyManager::getCpu();
544 u32 funcPos
= address
;
545 u32 funcEnd
= address
+size
;
547 u32 nextData
= symbolMap
.GetNextSymbolAddress(funcPos
-1,ST_DATA
);
548 u32 opcodeSequenceStart
= funcPos
;
549 while (funcPos
< funcEnd
)
551 if (funcPos
== nextData
)
553 if (opcodeSequenceStart
!= funcPos
)
554 addOpcodeSequence(opcodeSequenceStart
,funcPos
);
556 DisassemblyData
* data
= new DisassemblyData(funcPos
,symbolMap
.GetDataSize(funcPos
),symbolMap
.GetDataType(funcPos
));
557 entries
[funcPos
] = data
;
558 lineAddresses
.push_back(funcPos
);
559 funcPos
+= data
->getTotalSize();
561 nextData
= symbolMap
.GetNextSymbolAddress(funcPos
-1,ST_DATA
);
562 opcodeSequenceStart
= funcPos
;
569 u32 nextPos
= (funcPos
+3) & ~3;
571 DisassemblyComment
* comment
= new DisassemblyComment(funcPos
,nextPos
-funcPos
,".align","4");
572 entries
[funcPos
] = comment
;
573 lineAddresses
.push_back(funcPos
);
576 opcodeSequenceStart
= funcPos
;
580 MIPSAnalyst::MipsOpcodeInfo opInfo
= MIPSAnalyst::GetOpcodeInfo(cpu
,funcPos
);
581 u32 opAddress
= funcPos
;
584 // skip branches and their delay slots
592 if (MIPS_GET_OP(opInfo
.encodedOpcode
) == 0x0F && funcPos
< funcEnd
&& funcPos
!= nextData
)
594 MIPSOpcode next
= Memory::Read_Instruction(funcPos
);
595 MIPSInfo nextInfo
= MIPSGetInfo(next
);
597 u32 immediate
= ((opInfo
.encodedOpcode
& 0xFFFF) << 16) + (s16
)(next
.encoding
& 0xFFFF);
598 int rt
= MIPS_GET_RT(opInfo
.encodedOpcode
);
600 int nextRs
= MIPS_GET_RS(next
.encoding
);
601 int nextRt
= MIPS_GET_RT(next
.encoding
);
603 // both rs and rt of the second op have to match rt of the first,
604 // otherwise there may be hidden consequences if the macro is displayed.
605 // also, don't create a macro if something branches into the middle of it
606 if (nextRs
== rt
&& nextRt
== rt
&& branchTargets
.find(funcPos
) == branchTargets
.end())
608 DisassemblyMacro
* macro
= NULL
;
609 switch (MIPS_GET_OP(next
.encoding
))
612 macro
= new DisassemblyMacro(opAddress
);
613 macro
->setMacroLi(immediate
,rt
);
624 macro
= new DisassemblyMacro(opAddress
);
627 switch (nextInfo
& MEMTYPE_MASK
) {
646 macro
->setMacroMemory(MIPSGetName(next
),immediate
,rt
,dataSize
);
653 if (opcodeSequenceStart
!= opAddress
)
654 addOpcodeSequence(opcodeSequenceStart
,opAddress
);
656 entries
[opAddress
] = macro
;
657 for (int i
= 0; i
< macro
->getNumLines(); i
++)
659 lineAddresses
.push_back(macro
->getLineAddress(i
));
662 opcodeSequenceStart
= funcPos
;
668 // just a normal opcode
671 if (opcodeSequenceStart
!= funcPos
)
672 addOpcodeSequence(opcodeSequenceStart
,funcPos
);
675 void DisassemblyFunction::clear()
677 for (auto it
= entries
.begin(); it
!= entries
.end(); it
++)
684 lineAddresses
.clear();
688 bool DisassemblyOpcode::disassemble(u32 address
, DisassemblyLineInfo
& dest
, bool insertSymbols
)
690 char opcode
[64],arguments
[256];
691 const char *dizz
= DisassemblyManager::getCpu()->disasm(address
,4);
692 parseDisasm(dizz
,opcode
,arguments
,insertSymbols
);
693 dest
.type
= DISTYPE_OPCODE
;
695 dest
.params
= arguments
;
697 dest
.info
= MIPSAnalyst::GetOpcodeInfo(DisassemblyManager::getCpu(),address
);
701 void DisassemblyOpcode::getBranchLines(u32 start
, u32 size
, std::vector
<BranchLine
>& dest
)
705 size
= start
+size
-address
;
709 if (start
+size
> address
+num
*4)
710 size
= address
+num
*4-start
;
713 for (u32 pos
= start
; pos
< start
+size
; pos
+= 4)
715 MIPSAnalyst::MipsOpcodeInfo info
= MIPSAnalyst::GetOpcodeInfo(DisassemblyManager::getCpu(),pos
);
716 if (info
.isBranch
&& !info
.isBranchToRegister
&& !info
.isLinkedBranch
)
719 line
.laneIndex
= lane
++;
721 if (info
.branchTarget
< pos
)
723 line
.first
= info
.branchTarget
;
728 line
.second
= info
.branchTarget
;
729 line
.type
= LINE_DOWN
;
732 dest
.push_back(line
);
738 void DisassemblyMacro::setMacroLi(u32 _immediate
, u8 _rt
)
742 immediate
= _immediate
;
747 void DisassemblyMacro::setMacroMemory(std::string _name
, u32 _immediate
, u8 _rt
, int _dataSize
)
749 type
= MACRO_MEMORYIMM
;
751 immediate
= _immediate
;
753 dataSize
= _dataSize
;
757 bool DisassemblyMacro::disassemble(u32 address
, DisassemblyLineInfo
& dest
, bool insertSymbols
)
760 dest
.type
= DISTYPE_MACRO
;
761 dest
.info
= MIPSAnalyst::GetOpcodeInfo(DisassemblyManager::getCpu(),address
);
763 std::string addressSymbol
;
769 addressSymbol
= symbolMap
.GetLabelString(immediate
);
770 if (!addressSymbol
.empty() && insertSymbols
)
772 sprintf(buffer
,"%s,%s",DisassemblyManager::getCpu()->GetRegName(0,rt
),addressSymbol
.c_str());
774 sprintf(buffer
,"%s,0x%08X",DisassemblyManager::getCpu()->GetRegName(0,rt
),immediate
);
777 dest
.params
= buffer
;
779 dest
.info
.hasRelevantAddress
= true;
780 dest
.info
.relevantAddress
= immediate
;
782 case MACRO_MEMORYIMM
:
785 addressSymbol
= symbolMap
.GetLabelString(immediate
);
786 if (!addressSymbol
.empty() && insertSymbols
)
788 sprintf(buffer
,"%s,%s",DisassemblyManager::getCpu()->GetRegName(0,rt
),addressSymbol
.c_str());
790 sprintf(buffer
,"%s,0x%08X",DisassemblyManager::getCpu()->GetRegName(0,rt
),immediate
);
793 dest
.params
= buffer
;
795 dest
.info
.isDataAccess
= true;
796 dest
.info
.dataAddress
= immediate
;
797 dest
.info
.dataSize
= dataSize
;
799 dest
.info
.hasRelevantAddress
= true;
800 dest
.info
.relevantAddress
= immediate
;
806 dest
.totalSize
= getTotalSize();
811 DisassemblyData::DisassemblyData(u32 _address
, u32 _size
, DataType _type
): address(_address
), size(_size
), type(_type
)
813 auto memLock
= Memory::Lock();
817 hash
= computeHash(address
,size
);
821 void DisassemblyData::recheck()
823 auto memLock
= Memory::Lock();
827 HashType newHash
= computeHash(address
,size
);
835 bool DisassemblyData::disassemble(u32 address
, DisassemblyLineInfo
& dest
, bool insertSymbols
)
837 dest
.type
= DISTYPE_DATA
;
844 case DATATYPE_HALFWORD
:
851 dest
.name
= ".ascii";
857 auto it
= lines
.find(address
);
858 if (it
== lines
.end())
861 dest
.params
= it
->second
.text
;
862 dest
.totalSize
= it
->second
.size
;
866 int DisassemblyData::getLineNum(u32 address
, bool findStart
)
868 auto it
= lines
.upper_bound(address
);
869 if (it
!= lines
.end())
871 if (it
== lines
.begin())
874 return it
->second
.lineNum
;
877 return lines
.rbegin()->second
.lineNum
;
880 void DisassemblyData::createLines()
883 lineAddresses
.clear();
886 u32 end
= address
+size
;
887 u32 maxChars
= DisassemblyManager::getMaxParamChars();
889 std::string currentLine
;
890 u32 currentLineStart
= pos
;
893 if (type
== DATATYPE_ASCII
)
895 bool inString
= false;
898 u8 b
= Memory::Read_U8(pos
++);
899 if (b
>= 0x20 && b
<= 0x7F)
901 if (currentLine
.size()+1 >= maxChars
)
903 if (inString
== true)
906 DataEntry entry
= {currentLine
,pos
-1-currentLineStart
,lineCount
++};
907 lines
[currentLineStart
] = entry
;
908 lineAddresses
.push_back(currentLineStart
);
911 currentLineStart
= pos
-1;
915 if (inString
== false)
917 currentLine
+= (char)b
;
921 if (pos
== end
&& b
== 0)
924 sprintf(buffer
,"0x%02X",b
);
926 if (currentLine
.size()+strlen(buffer
) >= maxChars
)
928 if (inString
== true)
931 DataEntry entry
= {currentLine
,pos
-1-currentLineStart
,lineCount
++};
932 lines
[currentLineStart
] = entry
;
933 lineAddresses
.push_back(currentLineStart
);
936 currentLineStart
= pos
-1;
941 if (currentLine
.size() != 0)
950 currentLine
+= buffer
;
955 if (inString
== true)
958 if (currentLine
.size() != 0)
960 DataEntry entry
= {currentLine
,pos
-currentLineStart
,lineCount
++};
961 lines
[currentLineStart
] = entry
;
962 lineAddresses
.push_back(currentLineStart
);
970 u32 currentPos
= pos
;
975 value
= Memory::Read_U8(pos
);
976 snprintf(buffer
, sizeof(buffer
), "0x%02X", value
);
979 case DATATYPE_HALFWORD
:
980 value
= Memory::Read_U16(pos
);
981 snprintf(buffer
, sizeof(buffer
), "0x%04X", value
);
986 value
= Memory::Read_U32(pos
);
987 const std::string label
= symbolMap
.GetLabelString(value
);
989 snprintf(buffer
, sizeof(buffer
), "%s", label
.c_str());
991 snprintf(buffer
, sizeof(buffer
), "0x%08X", value
);
999 size_t len
= strlen(buffer
);
1000 if (currentLine
.size() != 0 && currentLine
.size()+len
>= maxChars
)
1002 DataEntry entry
= {currentLine
,currentPos
-currentLineStart
,lineCount
++};
1003 lines
[currentLineStart
] = entry
;
1004 lineAddresses
.push_back(currentLineStart
);
1007 currentLineStart
= currentPos
;
1010 if (currentLine
.size() != 0)
1012 currentLine
+= buffer
;
1015 if (currentLine
.size() != 0) {
1016 DataEntry entry
= {currentLine
,pos
-currentLineStart
,lineCount
++};
1017 lines
[currentLineStart
] = entry
;
1018 lineAddresses
.push_back(currentLineStart
);
1024 DisassemblyComment::DisassemblyComment(u32 _address
, u32 _size
, std::string _name
, std::string _param
)
1025 : address(_address
), size(_size
), name(_name
), param(_param
)
1030 bool DisassemblyComment::disassemble(u32 address
, DisassemblyLineInfo
& dest
, bool insertSymbols
)
1032 dest
.type
= DISTYPE_OTHER
;
1034 dest
.params
= param
;
1035 dest
.totalSize
= size
;