@ -49,28 +49,14 @@ static u8 CalculateChecksum(std::string_view data) {
static std : : string EscapeGDB ( std : : string_view data ) {
std : : string escaped ;
escaped . reserve ( data . size ( ) ) ;
for ( char c : data ) {
for ( char const c : data )
switch ( c ) {
case ' # ' :
escaped + = " } \x03 " ;
break ;
case ' $ ' :
escaped + = " } \x04 " ;
break ;
case ' * ' :
escaped + = " } \x0a " ;
break ;
case ' } ' :
escaped + = " } \x5d " ;
break ;
default :
escaped + = c ;
break ;
}
case ' # ' : escaped + = " } \x03 " ; break ;
case ' $ ' : escaped + = " } \x04 " ; break ;
case ' * ' : escaped + = " } \x0a " ; break ;
case ' } ' : escaped + = " } \x5d " ; break ;
default : escaped + = c ; break ;
}
return escaped ;
}
@ -82,38 +68,26 @@ static std::string EscapeXML(std::string_view data) {
}
std : : string escaped ;
escaped . reserve ( data . size ( ) ) ;
for ( char32_t c : converted ) {
for ( char32_t const c : converted )
switch ( c ) {
case ' & ' :
escaped + = " & " ;
break ;
case ' " ' :
escaped + = " " " ;
break ;
case ' < ' :
escaped + = " < " ;
break ;
case ' > ' :
escaped + = " > " ;
break ;
case ' & ' : escaped + = " & " ; break ;
case ' " ' : escaped + = " " " ; break ;
case ' < ' : escaped + = " < " ; break ;
case ' > ' : escaped + = " > " ; break ;
default :
if ( c > 0x7f ) {
escaped + = fmt : : format ( " &#{}; " , static_cast < u32 > ( c ) ) ;
escaped + = fmt : : format ( " &#{}; " , u32 ( c ) ) ;
} else {
escaped + = static_cast < char > ( c ) ;
escaped + = char ( c ) ;
}
break ;
}
}
return escaped ;
}
GDBStub : : GDBStub ( DebuggerBackend & backend_ , Core : : System & system_ , Kernel : : KProcess * debug_process_ )
: DebuggerFrontend ( backend_ ) , system { system_ } , debug_process { debug_process_ } {
if ( GetProcess ( ) - > Is64Bit ( ) ) {
if ( debug_process - > Is64Bit ( ) ) {
arch = std : : make_unique < GDBStubA64 > ( ) ;
} else {
arch = std : : make_unique < GDBStubA32 > ( ) ;
@ -148,57 +122,43 @@ void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint&
}
std : : vector < DebuggerAction > GDBStub : : ClientData ( std : : span < const u8 > data ) {
std : : vector < DebuggerAction > actions ;
current_command . insert ( current_command . end ( ) , data . begin ( ) , data . end ( ) ) ;
while ( current_command . size ( ) ! = 0 ) {
std : : vector < DebuggerAction > actions ;
while ( ! current_command . empty ( ) )
ProcessData ( actions ) ;
}
return actions ;
}
void GDBStub : : ProcessData ( std : : vector < DebuggerAction > & actions ) {
const char c { current_command [ 0 ] } ;
const char c = current_command [ 0 ] ;
// Acknowledgement
if ( c = = GDB_STUB_ACK | | c = = GDB_STUB_NACK ) {
current_command . erase ( current_command . begin ( ) ) ;
return ;
}
// Interrupt
if ( c = = GDB_STUB_INT3 ) {
} else if ( c = = GDB_STUB_INT3 ) {
LOG_INFO ( Debug_GDBStub , " Received interrupt " ) ;
current_command . erase ( current_command . begin ( ) ) ;
actions . push_back ( DebuggerAction : : Interrupt ) ;
SendStatus ( GDB_STUB_ACK ) ;
return ;
}
// Otherwise, require the data to be the start of a command
if ( c ! = GDB_STUB_START ) {
} else if ( c ! = GDB_STUB_START ) {
LOG_ERROR ( Debug_GDBStub , " Invalid command buffer contents: {} " , current_command . data ( ) ) ;
current_command . clear ( ) ;
SendStatus ( GDB_STUB_NACK ) ;
return ;
}
} else {
// Continue reading until command is complete
while ( CommandEnd ( ) = = current_command . end ( ) ) {
const auto new_data { backend . ReadFromClient ( ) } ;
current_command . insert ( current_command . end ( ) , new_data . begin ( ) , new_data . end ( ) ) ;
}
// Execute and respond to GDB
const auto command { DetachCommand ( ) } ;
if ( command ) {
if ( auto const cmd = DetachCommand ( ) ; cmd ) {
SendStatus ( GDB_STUB_ACK ) ;
ExecuteCommand ( * co mman d , actions ) ;
ExecuteCommand ( * cmd , actions ) ;
} else {
SendStatus ( GDB_STUB_NACK ) ;
}
}
}
void GDBStub : : ExecuteCommand ( std : : string_view packet , std : : vector < DebuggerAction > & actions ) {
@ -207,9 +167,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
if ( packet . length ( ) = = 0 ) {
SendReply ( GDB_STUB_REPLY_ERR ) ;
return ;
}
if ( packet . starts_with ( " vCont " ) ) {
} else if ( packet . starts_with ( " vCont " ) ) {
HandleVCont ( packet . substr ( 5 ) , actions ) ;
return ;
}
@ -218,14 +176,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
switch ( packet [ 0 ] ) {
case ' H ' : {
Kernel : : KThread * thread { nullptr } ;
s64 thread_id { strtoll ( command . data ( ) + 1 , nullptr , 16 ) } ;
if ( thread_id > = 1 ) {
thread = GetThreadByID ( thread_id ) ;
} else {
thread = backend . GetActiveThread ( ) ;
}
s64 thread_id = strtoll ( command . data ( ) + 1 , nullptr , 16 ) ;
Kernel : : KThread * thread = thread_id > = 1 ? GetThreadByID ( thread_id ) : backend . GetActiveThread ( ) ;
if ( thread ) {
SendReply ( GDB_STUB_REPLY_OK ) ;
backend . SetActiveThread ( thread ) ;
@ -235,7 +187,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
break ;
}
case ' T ' : {
s64 thread_id { strtoll ( command . data ( ) , nullptr , 16 ) } ;
s64 thread_id = strtoll ( command . data ( ) , nullptr , 16 ) ;
if ( GetThreadByID ( thread_id ) ) {
SendReply ( GDB_STUB_REPLY_OK ) ;
} else {
@ -262,27 +214,26 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
SendReply ( GDB_STUB_REPLY_OK ) ;
break ;
case ' p ' : {
const size_t reg { static_cast < size_t > ( strtoll ( command . data ( ) , nullptr , 16 ) ) } ;
const size_t reg = size_t ( strtoll ( command . data ( ) , nullptr , 16 ) ) ;
SendReply ( arch - > RegRead ( backend . GetActiveThread ( ) , reg ) ) ;
break ;
}
case ' P ' : {
const auto sep { std : : find ( command . begin ( ) , command . end ( ) , ' = ' ) - command . begin ( ) + 1 } ;
const size_t reg { static_cast < size_t > ( strtoll ( command . data ( ) , nullptr , 16 ) ) } ;
const auto sep = std : : find ( command . begin ( ) , command . end ( ) , ' = ' ) - command . begin ( ) + 1 ;
const size_t reg = size_t ( strtoll ( command . data ( ) , nullptr , 16 ) ) ;
arch - > RegWrite ( backend . GetActiveThread ( ) , reg , std : : string_view ( command ) . substr ( sep ) ) ;
SendReply ( GDB_STUB_REPLY_OK ) ;
break ;
}
case ' m ' : {
const auto sep { std : : find ( command . begin ( ) , command . end ( ) , ' , ' ) - command . begin ( ) + 1 } ;
const size_t addr { static_cast < size_t > ( strtoll ( command . data ( ) , nullptr , 16 ) ) } ;
const size_t size { static_cast < size_t > ( strtoll ( command . data ( ) + sep , nullptr , 16 ) ) } ;
const size_t addr = size_t ( strtoll ( command . data ( ) , nullptr , 16 ) ) ;
const size_t size = size_t ( strtoll ( command . data ( ) + sep , nullptr , 16 ) ) ;
std : : vector < u8 > mem ( size ) ;
if ( GetMemory ( ) . ReadBlock ( addr , mem . data ( ) , size ) ) {
if ( debug_process - > GetMemory ( ) . ReadBlock ( addr , mem . data ( ) , size ) ) {
// Restore any bytes belonging to replaced instructions.
auto it = replaced_instructions . lower_bound ( addr ) ;
for ( ; it ! = replaced_instructions . end ( ) & & it - > first < addr + size ; it + + ) {
for ( auto it = replaced_instructions . lower_bound ( addr ) ; it ! = replaced_instructions . end ( ) & & it - > first < addr + size ; it + + ) {
// Get the bytes of the instruction we previously replaced.
const u32 original_bytes = it - > second ;
@ -307,14 +258,14 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
const auto size_sep { std : : find ( command . begin ( ) , command . end ( ) , ' , ' ) - command . begin ( ) + 1 } ;
const auto mem_sep { std : : find ( command . begin ( ) , command . end ( ) , ' : ' ) - command . begin ( ) + 1 } ;
const size_t addr { static_cast < size_t > ( strtoll ( command . data ( ) , nullptr , 16 ) ) } ;
const size_t size { static_cast < size_t > ( strtoll ( command . data ( ) + size_sep , nullptr , 16 ) ) } ;
const size_t addr { size_t ( strtoll ( command . data ( ) , nullptr , 16 ) ) } ;
const size_t size { size_t ( strtoll ( command . data ( ) + size_sep , nullptr , 16 ) ) } ;
const auto mem_substr { std : : string_view ( command ) . substr ( mem_sep ) } ;
const auto mem { Common : : HexStringToVector ( mem_substr , false ) } ;
if ( GetMemory ( ) . WriteBlock ( addr , mem . data ( ) , size ) ) {
Core : : InvalidateInstructionCacheRange ( GetProcess ( ) , addr , size ) ;
if ( debug_process - > GetMemory ( ) . WriteBlock ( addr , mem . data ( ) , size ) ) {
Core : : InvalidateInstructionCacheRange ( debug_process , addr , size ) ;
SendReply ( GDB_STUB_REPLY_OK ) ;
} else {
SendReply ( GDB_STUB_REPLY_ERR ) ;
@ -349,14 +300,13 @@ enum class BreakpointType {
} ;
void GDBStub : : HandleBreakpointInsert ( std : : string_view command ) {
const auto type { static_cast < BreakpointType > ( strtoll ( command . data ( ) , nullptr , 16 ) ) } ;
const auto addr_sep { std : : find ( command . begin ( ) , command . end ( ) , ' , ' ) - command . begin ( ) + 1 } ;
const auto size_sep { std : : find ( command . begin ( ) + addr_sep , command . end ( ) , ' , ' ) -
command . begin ( ) + 1 } ;
const size_t addr { static_cast < size_t > ( strtoll ( command . data ( ) + addr_sep , nullptr , 16 ) ) } ;
const size_t size { static_cast < size_t > ( strtoll ( command . data ( ) + size_sep , nullptr , 16 ) ) } ;
if ( ! GetMemory ( ) . IsValidVirtualAddressRange ( addr , size ) ) {
const auto type = BreakpointType ( strtoll ( command . data ( ) , nullptr , 16 ) ) ;
const auto addr_sep = std : : find ( command . begin ( ) , command . end ( ) , ' , ' ) - command . begin ( ) + 1 ;
const auto size_sep = std : : find ( command . begin ( ) + addr_sep , command . end ( ) , ' , ' ) - command . begin ( ) + 1 ;
const size_t addr = size_t ( strtoll ( command . data ( ) + addr_sep , nullptr , 16 ) ) ;
const size_t size = size_t ( strtoll ( command . data ( ) + size_sep , nullptr , 16 ) ) ;
if ( ! debug_process - > GetMemory ( ) . IsValidVirtualAddressRange ( addr , size ) ) {
SendReply ( GDB_STUB_REPLY_ERR ) ;
return ;
}
@ -365,20 +315,19 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
switch ( type ) {
case BreakpointType : : Software :
replaced_instructions [ addr ] = GetMemory ( ) . Read32 ( addr ) ;
GetMemory ( ) . Write32 ( addr , arch - > BreakpointInstruction ( ) ) ;
Core : : InvalidateInstructionCacheRange ( GetProcess ( ) , addr , sizeof ( u32 ) ) ;
replaced_instructions [ addr ] = debug_process - > GetMemory ( ) . Read32 ( addr ) ;
debug_process - > GetMemory ( ) . Write32 ( addr , arch - > BreakpointInstruction ( ) ) ;
Core : : InvalidateInstructionCacheRange ( debug_process , addr , sizeof ( u32 ) ) ;
success = true ;
break ;
case BreakpointType : : WriteWatch :
success = GetProcess ( ) - > InsertWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : Write ) ;
success = debug_process - > InsertWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : Write ) ;
break ;
case BreakpointType : : ReadWatch :
success = GetProcess ( ) - > InsertWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : Read ) ;
success = debug_process - > InsertWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : Read ) ;
break ;
case BreakpointType : : AccessWatch :
success =
GetProcess ( ) - > InsertWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : ReadOrWrite ) ;
success = debug_process - > InsertWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : ReadOrWrite ) ;
break ;
case BreakpointType : : Hardware :
default :
@ -393,41 +342,37 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
}
}
void GDBStub : : HandleBreakpointRemove ( std : : string_view command ) {
const auto type { static_cast < BreakpointType > ( strtoll ( command . data ( ) , nullptr , 16 ) ) } ;
const auto addr_sep { std : : find ( command . begin ( ) , command . end ( ) , ' , ' ) - command . begin ( ) + 1 } ;
const auto size_sep { std : : find ( command . begin ( ) + addr_sep , command . end ( ) , ' , ' ) -
command . begin ( ) + 1 } ;
const size_t addr { static_cast < size_t > ( strtoll ( command . data ( ) + addr_sep , nullptr , 16 ) ) } ;
const size_t size { static_cast < size_t > ( strtoll ( command . data ( ) + size_sep , nullptr , 16 ) ) } ;
void GDBStub : : HandleBreakpointRemove ( std : : string_view sv ) {
const auto type = BreakpointType ( strtoll ( sv . data ( ) , nullptr , 16 ) ) ;
const auto addr_sep = std : : find ( sv . begin ( ) , sv . end ( ) , ' , ' ) - sv . begin ( ) + 1 ;
const auto size_sep = std : : find ( sv . begin ( ) + addr_sep , sv . end ( ) , ' , ' ) - sv . begin ( ) + 1 ;
const size_t addr = size_t ( strtoll ( sv . data ( ) + addr_sep , nullptr , 16 ) ) ;
const size_t size = size_t ( strtoll ( sv . data ( ) + size_sep , nullptr , 16 ) ) ;
if ( ! GetMemory ( ) . IsValidVirtualAddressRange ( addr , size ) ) {
if ( ! debug_process - > GetMemory ( ) . IsValidVirtualAddressRange ( addr , size ) ) {
SendReply ( GDB_STUB_REPLY_ERR ) ;
return ;
}
bool success { } ;
bool success = false ;
switch ( type ) {
case BreakpointType : : Software : {
const auto orig_insn { replaced_instructions . find ( addr ) } ;
if ( orig_insn ! = replaced_instructions . end ( ) ) {
GetMemory ( ) . Write32 ( addr , orig_insn - > second ) ;
Core : : InvalidateInstructionCacheRange ( GetProcess ( ) , addr , sizeof ( u32 ) ) ;
if ( auto const orig_insn = replaced_instructions . find ( addr ) ; orig_insn ! = replaced_instructions . end ( ) ) {
debug_process - > GetMemory ( ) . Write32 ( addr , orig_insn - > second ) ;
Core : : InvalidateInstructionCacheRange ( debug_process , addr , sizeof ( u32 ) ) ;
replaced_instructions . erase ( addr ) ;
success = true ;
}
break ;
}
case BreakpointType : : WriteWatch :
success = GetProcess ( ) - > RemoveWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : Write ) ;
success = debug_process - > RemoveWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : Write ) ;
break ;
case BreakpointType : : ReadWatch :
success = GetProcess ( ) - > RemoveWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : Read ) ;
success = debug_process - > RemoveWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : Read ) ;
break ;
case BreakpointType : : AccessWatch :
success =
GetProcess ( ) - > RemoveWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : ReadOrWrite ) ;
success = debug_process - > RemoveWatchpoint ( addr , size , Kernel : : DebugWatchpointType : : ReadOrWrite ) ;
break ;
case BreakpointType : : Hardware :
default :
@ -454,22 +399,21 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ
}
}
void GDBStub : : HandleQuery ( std : : string_view command ) {
if ( command . starts_with ( " TStatus " ) ) {
void GDBStub : : HandleQuery ( std : : string_view sv ) {
if ( sv . starts_with ( " TStatus " ) ) {
// no tracepoint support
SendReply ( " T0 " ) ;
} else if ( command . starts_with ( " Supported " ) ) {
} else if ( sv . starts_with ( " Supported " ) ) {
SendReply ( " PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+; "
" vContSupported+;QStartNoAckMode+ " ) ;
} else if ( command . starts_with ( " Xfer:features:read:target.xml: " ) ) {
} else if ( sv . starts_with ( " Xfer:features:read:target.xml: " ) ) {
const auto target_xml { arch - > GetTargetXML ( ) } ;
SendReply ( PaginateBuffer ( target_xml , command . substr ( 30 ) ) ) ;
} else if ( command . starts_with ( " Offsets " ) ) {
const auto main_offset = Core : : FindMainModuleEntrypoint ( GetProcess ( ) ) ;
SendReply ( PaginateBuffer ( target_xml , sv . substr ( 30 ) ) ) ;
} else if ( sv . starts_with ( " Offsets " ) ) {
const auto main_offset = Core : : FindMainModuleEntrypoint ( debug_process ) ;
SendReply ( fmt : : format ( " TextSeg={:x} " , GetInteger ( main_offset ) ) ) ;
} else if ( command . starts_with ( " Xfer:libraries:read:: " ) ) {
auto modules = Core : : FindModules ( GetProcess ( ) ) ;
} else if ( sv . starts_with ( " Xfer:libraries:read:: " ) ) {
auto modules = Core : : FindModules ( debug_process ) ;
std : : string buffer ;
buffer + = R " (<?xml version= " 1.0 " ?>) " ;
buffer + = " <library-list> " ;
@ -479,24 +423,23 @@ void GDBStub::HandleQuery(std::string_view command) {
}
buffer + = " </library-list> " ;
SendReply ( PaginateBuffer ( buffer , command . substr ( 21 ) ) ) ;
} else if ( command . starts_with ( " fThreadInfo " ) ) {
SendReply ( PaginateBuffer ( buffer , sv . substr ( 21 ) ) ) ;
} else if ( sv . starts_with ( " fThreadInfo " ) ) {
// beginning of list
const auto & threads = GetProcess ( ) - > GetThreadList ( ) ;
const auto & threads = debug_process - > GetThreadList ( ) ;
std : : vector < std : : string > thread_ids ;
for ( const auto & thread : threads ) {
for ( const auto & thread : threads )
thread_ids . push_back ( fmt : : format ( " {:x} " , thread . GetThreadId ( ) ) ) ;
}
SendReply ( fmt : : format ( " m{} " , fmt : : join ( thread_ids , " , " ) ) ) ;
} else if ( command . starts_with ( " sThreadInfo " ) ) {
} else if ( sv . starts_with ( " sThreadInfo " ) ) {
// end of list
SendReply ( " l " ) ;
} else if ( command . starts_with ( " Xfer:threads:read:: " ) ) {
} else if ( sv . starts_with ( " Xfer:threads:read:: " ) ) {
std : : string buffer ;
buffer + = R " (<?xml version= " 1.0 " ?>) " ;
buffer + = " <threads> " ;
const auto & threads = GetProcess ( ) - > GetThreadList ( ) ;
const auto & threads = debug_process - > GetThreadList ( ) ;
for ( const auto & thread : threads ) {
auto thread_name { Core : : GetThreadName ( & thread ) } ;
if ( ! thread_name ) {
@ -510,109 +453,95 @@ void GDBStub::HandleQuery(std::string_view command) {
buffer + = " </threads> " ;
SendReply ( PaginateBuffer ( buffer , command . substr ( 19 ) ) ) ;
} else if ( command . starts_with ( " Attached " ) ) {
SendReply ( PaginateBuffer ( buffer , sv . substr ( 19 ) ) ) ;
} else if ( sv . starts_with ( " Attached " ) ) {
SendReply ( " 0 " ) ;
} else if ( command . starts_with ( " StartNoAckMode " ) ) {
} else if ( sv . starts_with ( " StartNoAckMode " ) ) {
no_ack = true ;
SendReply ( GDB_STUB_REPLY_OK ) ;
} else if ( command . starts_with ( " Rcmd, " ) ) {
HandleRcmd ( Common : : HexStringToVector ( command . substr ( 5 ) , false ) ) ;
} else if ( sv . starts_with ( " Rcmd, " ) ) {
HandleRcmd ( Common : : HexStringToVector ( sv . substr ( 5 ) , false ) ) ;
} else {
SendReply ( GDB_STUB_REPLY_EMPTY ) ;
}
}
void GDBStub : : HandleVCont ( std : : string_view command , std : : vector < DebuggerAction > & actions ) {
if ( command = = " ? " ) {
// Continuing and stepping are supported
// (signal is ignored, but required for GDB to use vCont)
void GDBStub : : HandleVCont ( std : : string_view sv , std : : vector < DebuggerAction > & actions ) {
// Continuing and stepping are supported (signal is ignored, but required for GDB to use vCont)
if ( sv = = " ? " ) {
SendReply ( " vCont;c;C;s;S " ) ;
return ;
}
Kernel : : KThread * stepped_thread { nullptr } ;
bool lock_execution { true } ;
} else {
Kernel : : KThread * stepped_thread = nullptr ;
bool lock_execution = true ;
std : : vector < std : : string > entries ;
boost : : split ( entries , command . substr ( 1 ) , boost : : is_any_of ( " ; " ) ) ;
for ( const auto & thread_action : entries ) {
boost : : split ( entries , sv . substr ( 1 ) , boost : : is_any_of ( " ; " ) ) ;
for ( auto const & thread_action : entries ) {
std : : vector < std : : string > parts ;
boost : : split ( parts , thread_action , boost : : is_any_of ( " : " ) ) ;
if ( parts . size ( ) = = 1 & & ( parts [ 0 ] = = " c " | | parts [ 0 ] . starts_with ( " C " ) ) ) {
if ( parts . size ( ) = = 1 & & ( parts [ 0 ] = = " c " | | parts [ 0 ] . starts_with ( " C " ) ) )
lock_execution = false ;
}
if ( parts . size ( ) = = 2 & & ( parts [ 0 ] = = " s " | | parts [ 0 ] . starts_with ( " S " ) ) ) {
if ( parts . size ( ) = = 2 & & ( parts [ 0 ] = = " s " | | parts [ 0 ] . starts_with ( " S " ) ) )
stepped_thread = GetThreadByID ( strtoll ( parts [ 1 ] . data ( ) , nullptr , 16 ) ) ;
}
}
if ( stepped_thread ) {
backend . SetActiveThread ( stepped_thread ) ;
actions . push_back ( lock_execution ? DebuggerAction : : StepThreadLocked
: DebuggerAction : : StepThreadUnlocked ) ;
actions . push_back ( lock_execution ? DebuggerAction : : StepThreadLocked : DebuggerAction : : StepThreadUnlocked ) ;
} else {
actions . push_back ( DebuggerAction : : Continue ) ;
}
}
}
static constexpr const char * GetMemoryStateName ( Kernel : : Svc : : MemoryState state ) {
constexpr std : : array < std : : pair < const char * , Kernel : : Svc : : MemoryState > , 22 > MemoryStateNames { {
{ " ----- Free ------ " , Kernel : : Svc : : MemoryState : : Free } ,
{ " Io " , Kernel : : Svc : : MemoryState : : Io } ,
{ " Static " , Kernel : : Svc : : MemoryState : : Static } ,
{ " Code " , Kernel : : Svc : : MemoryState : : Code } ,
{ " CodeData " , Kernel : : Svc : : MemoryState : : CodeData } ,
{ " Normal " , Kernel : : Svc : : MemoryState : : Normal } ,
{ " Shared " , Kernel : : Svc : : MemoryState : : Shared } ,
{ " AliasCode " , Kernel : : Svc : : MemoryState : : AliasCode } ,
{ " AliasCodeData " , Kernel : : Svc : : MemoryState : : AliasCodeData } ,
{ " Ipc " , Kernel : : Svc : : MemoryState : : Ipc } ,
{ " Stack " , Kernel : : Svc : : MemoryState : : Stack } ,
{ " ThreadLocal " , Kernel : : Svc : : MemoryState : : ThreadLocal } ,
{ " Transferred " , Kernel : : Svc : : MemoryState : : Transferred } ,
{ " SharedTransferred " , Kernel : : Svc : : MemoryState : : SharedTransferred } ,
{ " SharedCode " , Kernel : : Svc : : MemoryState : : SharedCode } ,
{ " Inaccessible " , Kernel : : Svc : : MemoryState : : Inaccessible } ,
{ " NonSecureIpc " , Kernel : : Svc : : MemoryState : : NonSecureIpc } ,
{ " NonDeviceIpc " , Kernel : : Svc : : MemoryState : : NonDeviceIpc } ,
{ " Kernel " , Kernel : : Svc : : MemoryState : : Kernel } ,
{ " GeneratedCode " , Kernel : : Svc : : MemoryState : : GeneratedCode } ,
{ " CodeOut " , Kernel : : Svc : : MemoryState : : CodeOut } ,
{ " Coverage " , Kernel : : Svc : : MemoryState : : Coverage } ,
} } ;
for ( size_t i = 0 ; i < MemoryStateNames . size ( ) ; i + + ) {
if ( std : : get < 1 > ( MemoryStateNames [ i ] ) = = state ) {
return std : : get < 0 > ( MemoryStateNames [ i ] ) ;
}
}
return " Unknown " ;
# define MEMORY_STATE_LIST \
MEMORY_STATE_ELEM ( Free ) \
MEMORY_STATE_ELEM ( Io ) \
MEMORY_STATE_ELEM ( Static ) \
MEMORY_STATE_ELEM ( Code ) \
MEMORY_STATE_ELEM ( CodeData ) \
MEMORY_STATE_ELEM ( Normal ) \
MEMORY_STATE_ELEM ( Shared ) \
MEMORY_STATE_ELEM ( AliasCode ) \
MEMORY_STATE_ELEM ( AliasCodeData ) \
MEMORY_STATE_ELEM ( Ipc ) \
MEMORY_STATE_ELEM ( Stack ) \
MEMORY_STATE_ELEM ( ThreadLocal ) \
MEMORY_STATE_ELEM ( Transferred ) \
MEMORY_STATE_ELEM ( SharedTransferred ) \
MEMORY_STATE_ELEM ( SharedCode ) \
MEMORY_STATE_ELEM ( Inaccessible ) \
MEMORY_STATE_ELEM ( NonSecureIpc ) \
MEMORY_STATE_ELEM ( NonDeviceIpc ) \
MEMORY_STATE_ELEM ( Kernel ) \
MEMORY_STATE_ELEM ( GeneratedCode ) \
MEMORY_STATE_ELEM ( CodeOut ) \
MEMORY_STATE_ELEM ( Coverage )
switch ( state ) {
# define MEMORY_STATE_ELEM(elem) case Kernel::Svc::MemoryState::elem: return #elem;
MEMORY_STATE_LIST
# undef MEMORY_STATE_LIST
default : return " Unknown " ;
}
}
static constexpr const char * GetMemoryPermissionString ( const Kernel : : Svc : : MemoryInfo & info ) {
if ( info . state = = Kernel : : Svc : : MemoryState : : Free ) {
return " " ;
}
} else {
switch ( info . permission ) {
case Kernel : : Svc : : MemoryPermission : : ReadExecute :
return " r-x " ;
case Kernel : : Svc : : MemoryPermission : : Read :
return " r-- " ;
case Kernel : : Svc : : MemoryPermission : : ReadWrite :
return " rw- " ;
default :
return " --- " ;
case Kernel : : Svc : : MemoryPermission : : ReadExecute : return " r-x " ;
case Kernel : : Svc : : MemoryPermission : : Read : return " r-- " ;
case Kernel : : Svc : : MemoryPermission : : ReadWrite : return " rw- " ;
default : return " --- " ;
}
}
}
void GDBStub : : HandleRcmd ( const std : : vector < u8 > & command ) {
std : : string_view command_str { reinterpret_cast < const char * > ( & command [ 0 ] ) , command . size ( ) } ;
std : : string reply ;
auto * process = GetProcess ( ) ;
auto & page_table = process - > GetPageTable ( ) ;
auto & page_table = debug_process - > GetPageTable ( ) ;
if ( command_str = = " fastmem " | | command_str = = " get fastmem " ) {
if ( Settings : : IsFastmemEnabled ( ) ) {
const auto & impl = page_table . GetImpl ( ) ;
@ -627,11 +556,13 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
reply = " Fastmem is not enabled. \n " ;
}
} else if ( command_str = = " info " | | command_str = = " get info " ) {
auto modules = Core : : FindModules ( process ) ;
auto modules = Core : : FindModules ( debug_ process) ;
reply = fmt : : format ( " Process: {:#x} ({}) \n "
" Program Id: {:#018x} \n " ,
process - > GetProcessId ( ) , process - > GetName ( ) , process - > GetProgramId ( ) ) ;
debug_process - > GetProcessId ( ) ,
debug_process - > GetName ( ) ,
debug_process - > GetProgramId ( ) ) ;
reply + = fmt : : format (
" Layout: \n "
" Alias: {:#012x} - {:#012x} \n "
@ -648,10 +579,8 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
GetInteger ( page_table . GetStackRegionStart ( ) ) ,
GetInteger ( page_table . GetStackRegionStart ( ) ) + page_table . GetStackRegionSize ( ) - 1 ) ;
for ( const auto & [ vaddr , name ] : modules ) {
reply + = fmt : : format ( " {:#012x} - {:#012x} {} \n " , vaddr ,
GetInteger ( Core : : GetModuleEnd ( process , vaddr ) ) , name ) ;
}
for ( const auto & [ vaddr , name ] : modules )
reply + = fmt : : format ( " {:#012x} - {:#012x} {} \n " , vaddr , GetInteger ( Core : : GetModuleEnd ( debug_process , vaddr ) ) , name ) ;
} else if ( command_str = = " mappings " | | command_str = = " get mappings " ) {
reply = " Mappings: \n " ;
VAddr cur_addr = 0 ;
@ -683,10 +612,8 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
}
const uintptr_t next_address = svc_mem_info . base_address + svc_mem_info . size ;
if ( next_address < = cur_addr ) {
if ( next_address < = cur_addr )
break ;
}
cur_addr = next_address ;
}
} else {
@ -698,20 +625,16 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
}
Kernel : : KThread * GDBStub : : GetThreadByID ( u64 thread_id ) {
auto & threads { GetProcess ( ) - > GetThreadList ( ) } ;
for ( auto & thread : threads ) {
if ( thread . GetThreadId ( ) = = thread_id ) {
auto & threads = debug_process - > GetThreadList ( ) ;
for ( auto & thread : threads )
if ( thread . GetThreadId ( ) = = thread_id )
return std : : addressof ( thread ) ;
}
}
return nullptr ;
}
std : : vector < char > : : const_iterator GDBStub : : CommandEnd ( ) const {
// Find the end marker
const auto end { std : : find ( current_command . begin ( ) , current_command . end ( ) , GDB_STUB_END ) } ;
const auto end = std : : find ( current_command . begin ( ) , current_command . end ( ) , GDB_STUB_END ) ;
// Require the checksum to be present
return ( std : : min ) ( end + 2 , current_command . end ( ) ) ;
}
@ -737,8 +660,7 @@ std::optional<std::string> GDBStub::DetachCommand() {
// Verify checksum
if ( calculated ! = received ) {
LOG_ERROR ( Debug_GDBStub , " Checksum mismatch: calculated {:02x}, received {:02x} " ,
calculated , received ) ;
LOG_ERROR ( Debug_GDBStub , " Checksum mismatch: calculated {:02x}, received {:02x} " , calculated , received ) ;
return std : : nullopt ;
}
@ -746,9 +668,8 @@ std::optional<std::string> GDBStub::DetachCommand() {
}
void GDBStub : : SendReply ( std : : string_view data ) {
const auto escaped { EscapeGDB ( data ) } ;
const auto output { fmt : : format ( " {}{}{}{:02x} " , GDB_STUB_START , escaped , GDB_STUB_END ,
CalculateChecksum ( escaped ) ) } ;
const auto escaped = EscapeGDB ( data ) ;
const auto output = fmt : : format ( " {}{}{}{:02x} " , GDB_STUB_START , escaped , GDB_STUB_END , CalculateChecksum ( escaped ) ) ;
LOG_TRACE ( Debug_GDBStub , " Writing reply: {} " , output ) ;
// C++ string support is complete rubbish
@ -758,21 +679,11 @@ void GDBStub::SendReply(std::string_view data) {
}
void GDBStub : : SendStatus ( char status ) {
if ( no_ack ) {
return ;
}
std : : array < u8 , 1 > buf = { static_cast < u8 > ( status ) } ;
if ( ! no_ack ) {
std : : array < u8 , 1 > buf = { u8 ( status ) } ;
LOG_TRACE ( Debug_GDBStub , " Writing status: {} " , status ) ;
backend . WriteToClient ( buf ) ;
}
Kernel : : KProcess * GDBStub : : GetProcess ( ) {
return debug_process ;
}
Core : : Memory : : Memory & GDBStub : : GetMemory ( ) {
return GetProcess ( ) - > GetMemory ( ) ;
}
}
} // namespace Core