225 lines
6.8 KiB
C++
225 lines
6.8 KiB
C++
/*
|
|
IDA remote debugger server
|
|
*/
|
|
|
|
#ifdef _WIN32
|
|
// We use the deprecated inet_ntoa() function for Windows XP compatibility.
|
|
//lint -e750 local macro '' not referenced
|
|
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
|
#endif
|
|
|
|
#include <pro.h>
|
|
#include <expr.hpp>
|
|
#include "server.h"
|
|
|
|
// Provide dummy versions for tinfo copy/clear. Debugger servers do not use them
|
|
#if !defined(__NT__)
|
|
void ida_export copy_tinfo_t(tinfo_t *, const tinfo_t &) {}
|
|
void ida_export clear_tinfo_t(tinfo_t *) {}
|
|
#endif
|
|
|
|
// We don't have a kernel. Provide envvar-based debug file directory retrieval.
|
|
#if defined(__LINUX__)
|
|
static bool _elf_debug_file_directory_resolved = false;
|
|
static qstring _elf_debug_file_directory;
|
|
idaman const char *ida_export get_elf_debug_file_directory()
|
|
{
|
|
if ( !_elf_debug_file_directory_resolved )
|
|
{
|
|
if ( !qgetenv("DEBUG_FILE_DIRECTORY", &_elf_debug_file_directory) )
|
|
qgetenv("ELF_DEBUG_FILE_DIRECTORY", &_elf_debug_file_directory);
|
|
if ( _elf_debug_file_directory.empty() )
|
|
_elf_debug_file_directory = "/usr/lib/debug";
|
|
_elf_debug_file_directory_resolved = true;
|
|
}
|
|
return _elf_debug_file_directory.c_str();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
#ifdef TESTABLE_BUILD
|
|
static std::map<int,qstring> _pid_elf_debug_file_directories;
|
|
void set_elf_debug_file_directory_for_pid(int pid, const char *path)
|
|
{
|
|
_pid_elf_debug_file_directories[pid] = path;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
const char *get_elf_debug_file_directory_for_pid(int pid)
|
|
{
|
|
const char *found = NULL;
|
|
std::map<int,qstring>::const_iterator it = _pid_elf_debug_file_directories.find(pid);
|
|
if ( it != _pid_elf_debug_file_directories.end() )
|
|
found = it->second.begin();
|
|
else
|
|
found = get_elf_debug_file_directory();
|
|
return found;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
//lint -esym(714, dump_udt) not referenced
|
|
void dump_udt(const char *, const struct udt_type_data_t &) {}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// SERVER GLOBAL VARIABLES
|
|
#ifdef __SINGLE_THREADED_SERVER__
|
|
dbgsrv_dispatcher_t dispatcher(false);
|
|
|
|
static bool init_lock(void) { return true; }
|
|
bool lock_begin(void) { return true; }
|
|
bool lock_end(void) { return true; }
|
|
#else
|
|
dbgsrv_dispatcher_t dispatcher(true);
|
|
|
|
static qmutex_t g_mutex = NULL;
|
|
static bool init_lock(void) { g_mutex = qmutex_create(); return g_mutex != NULL; }
|
|
bool lock_begin(void) { return qmutex_lock(g_mutex); }
|
|
bool lock_end(void) { return qmutex_unlock(g_mutex); }
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------
|
|
dbg_rpc_handler_t *g_global_server = NULL;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// perform an action (func) on all debuggers
|
|
int for_all_debuggers(debmod_visitor_t &v)
|
|
{
|
|
int code = 0;
|
|
dispatcher.clients_list->lock();
|
|
{
|
|
client_handlers_list_t::storage_t::iterator it;
|
|
for ( it = dispatcher.clients_list->storage.begin();
|
|
it != dispatcher.clients_list->storage.end();
|
|
++it )
|
|
{
|
|
dbg_rpc_handler_t *h = (dbg_rpc_handler_t *) it->first;
|
|
code = v.visit(h->get_debugger_instance());
|
|
if ( code != 0 )
|
|
break;
|
|
}
|
|
}
|
|
dispatcher.clients_list->unlock();
|
|
return code;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
dbgsrv_dispatcher_t::dbgsrv_dispatcher_t(bool multi_threaded)
|
|
: base_dispatcher_t(multi_threaded),
|
|
broken_conns_supported(false),
|
|
on_broken_conn(BCH_DEFAULT)
|
|
{
|
|
port_number = DEBUGGER_PORT_NUMBER;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void dbgsrv_dispatcher_t::collect_cliopts(cliopts_t *out)
|
|
{
|
|
struct ida_local ns_t
|
|
{
|
|
static void _set_dpassword(const char *value, void *ud)
|
|
{
|
|
((dbgsrv_dispatcher_t *) ud)->server_password = value;
|
|
}
|
|
static void _set_broken_connections_keep_debmod(const char *, void *ud)
|
|
{
|
|
((dbgsrv_dispatcher_t *) ud)->on_broken_conn = BCH_KEEP_DEBMOD;
|
|
}
|
|
static void _set_closing_session_kill_debuggee(const char *, void *ud)
|
|
{
|
|
((dbgsrv_dispatcher_t *) ud)->on_broken_conn = BCH_KILL_PROCESS;
|
|
}
|
|
};
|
|
|
|
static const cliopt_t cliopts[] =
|
|
{
|
|
{ 'P', "password", "Password", ns_t::_set_dpassword, 1 },
|
|
};
|
|
// the following options are valid only if broken connections are supported
|
|
static const cliopt_t bc_cliopts[] =
|
|
{
|
|
{
|
|
'k',
|
|
"on-broken-connection-keep-session",
|
|
"Keep debugger session alive when connection breaks",
|
|
ns_t::_set_broken_connections_keep_debmod,
|
|
0,
|
|
},
|
|
{
|
|
'K',
|
|
"on-stop-kill-process",
|
|
"Kill debuggee when closing session",
|
|
ns_t::_set_closing_session_kill_debuggee,
|
|
0,
|
|
},
|
|
};
|
|
|
|
base_dispatcher_t::collect_cliopts(out);
|
|
for ( size_t i = 0; i < qnumber(cliopts); ++i )
|
|
out->push_back(cliopts[i]);
|
|
|
|
if ( broken_conns_supported )
|
|
for ( size_t i = 0; i < qnumber(bc_cliopts); ++i )
|
|
out->push_back(bc_cliopts[i]);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
client_handler_t *dbgsrv_dispatcher_t::new_client_handler(idarpc_stream_t *_irs)
|
|
{
|
|
dbg_rpc_handler_t *h = new dbg_rpc_handler_t(_irs, this);
|
|
h->verbose = verbose;
|
|
void *params = NULL;
|
|
#if defined(__LINUX__) && defined(TESTABLE_BUILD)
|
|
params = (void *) get_elf_debug_file_directory_for_pid; //lint !e611 cast between pointer to function type '' and pointer to object type 'void *'
|
|
#endif
|
|
h->set_debugger_instance(create_debug_session(params));
|
|
g_global_server = h;
|
|
return h;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void dbgsrv_dispatcher_t::shutdown_gracefully(int signum)
|
|
{
|
|
base_dispatcher_t::shutdown_gracefully(signum);
|
|
term_subsystem();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// debugger remote server - TCP/IP mode
|
|
int NT_CDECL main(int argc, const char *argv[])
|
|
{
|
|
#ifdef ENABLE_LOWCNDS
|
|
init_idc();
|
|
#endif
|
|
|
|
// call the debugger module to initialize its subsystem once
|
|
if ( !init_lock() || !init_subsystem() )
|
|
{
|
|
lprintf("Could not initialize subsystem!");
|
|
return -1;
|
|
}
|
|
|
|
qstring password;
|
|
if ( qgetenv("IDA_DBGSRV_PASSWD", &password) )
|
|
dispatcher.server_password = password;
|
|
|
|
int ida_major = IDA_SDK_VERSION / 100;
|
|
#if (IDA_SDK_VERSION % 10) == 0
|
|
int ida_minor = (IDA_SDK_VERSION % 100)/10; // 740 -> 4
|
|
#else
|
|
int ida_minor = IDA_SDK_VERSION % 100; // 741 -> 41
|
|
#endif
|
|
|
|
lprintf("IDA " SYSTEM SYSBITS " remote debug server(" __SERVER_TYPE__ ") "
|
|
"v%d.%d.%d. Hex-Rays (c) 2004-2021\n",
|
|
ida_major, ida_minor, IDD_INTERFACE_VERSION);
|
|
|
|
dispatcher.broken_conns_supported = debmod_t::reuse_broken_connections;
|
|
cliopts_t cliopts(lprintf);
|
|
dispatcher.collect_cliopts(&cliopts);
|
|
cliopts.apply(argc, argv, &dispatcher);
|
|
dispatcher.install_signal_handlers();
|
|
dispatcher.dispatch();
|
|
}
|