/* ********************************************************************** */
/* ** server.c ** */
/* ** Functions for tethering to applications. ** */
/* ** ---------------------------------------------------------------- ** */
/* ** Author: Paul Howard, Copyright: Functional Objects, Inc. 1996 ** */
/* ** All Rights Reserved ** */
/* ********************************************************************** */
#include "nub-core.h"
#include "psapi.h"
#include "tlhelp32.h"
// Quick header...
void initialize_process_exception_information (LPDBGPROCESS process);
void initialize_debug_event_queue (LPDBGPROCESS process);
int get_os_wall_clock_time (LPDBGPROCESS process);
MODULED *module_enumeration_descriptor_from_base_address
(MODULED *list, DWORD base);
#define ENVIRONMENT_VARIABLE_BUFFER_SIZE 32768
#define ENVIRONMENT_PATH "PATH"
void extend_path_environment_variable
(char *original_path, char *extension, char *extended_path)
{
BOOL status_query_variable;
BOOL status_set_variable;
int i = 0;
int j = 0;
int k = 0;
// Get the current value of the path.
status_query_variable =
GetEnvironmentVariable
(ENVIRONMENT_PATH, original_path, ENVIRONMENT_VARIABLE_BUFFER_SIZE);
// Speedy in-place string copying.
while (extension[j] != '\0')
{extended_path[i] = extension[j]; i++; j++;}
extended_path[i] = ';'; i++;
while (original_path[k] != '\0')
{extended_path[i] = original_path[k]; i++; k++;}
extended_path[i] = '\0';
// Set the extended path.
status_set_variable =
SetEnvironmentVariable
(ENVIRONMENT_PATH, extended_path);
if (!(status_query_variable && status_set_variable))
OutputDebugString("NUB: Warning - Extension of search path failed.");
}
void restore_path_environment_variable
(char *original_path)
{
BOOL status_set_variable;
status_set_variable =
SetEnvironmentVariable(ENVIRONMENT_PATH, original_path);
if (!status_set_variable)
OutputDebugString("NUB: Warning - Restoration of search path failed.");
}
MODULED *update_process_module_list_windows_nt
(DWORD process_id, HANDLE ph, MODULED *orig)
{
HINSTANCE psapi = LoadLibrary("PSAPI.DLL");
HMODULE handles_array[MODULE_DESCRIPTOR_ALLOWANCE];
DWORD size_filled;
BOOL status_enum_modules;
DWORD counter, num_modules;
MODULED *head = orig;
MODULED *current = orig;
MODULED *new = NULL;
MODULEINFO module_info;
// Declare function pointers for the PSAPI entry points that we
// need.
BOOL (WINAPI *lpfEnumProcessModules) (HANDLE, HMODULE*, DWORD, LPDWORD);
DWORD (WINAPI *lpfGetModuleFileNameEx) (HANDLE, HMODULE, LPTSTR, DWORD);
BOOL (WINAPI *lpfGetModuleInformation) (HANDLE, HMODULE, LPMODULEINFO, DWORD);
// If we cannot load PSAPI, then we are stuffed.
if (psapi == NULL) return(orig);
// Resolve the pre-declared function pointers using GetProcAddress and
// these bloody typecasts!
lpfEnumProcessModules =
(BOOL (WINAPI*) (HANDLE, HMODULE*, DWORD, LPDWORD))
GetProcAddress(psapi, "EnumProcessModules");
lpfGetModuleFileNameEx =
(DWORD (WINAPI*) (HANDLE, HMODULE, LPTSTR, DWORD))
GetProcAddress(psapi, "GetModuleFileNameExA");
lpfGetModuleInformation =
(BOOL (WINAPI*) (HANDLE, HMODULE, LPMODULEINFO, DWORD))
GetProcAddress(psapi, "GetModuleInformation");
// Any failure means we take the early train home.
if ((lpfEnumProcessModules == NULL) ||
(lpfGetModuleFileNameEx == NULL) ||
(lpfGetModuleInformation == NULL))
return(orig);
// Point CURRENT at the last descriptor in the list.
if (current != NULL) {
while (current->Next != NULL)
current = current->Next;
}
status_enum_modules =
lpfEnumProcessModules(ph,
handles_array,
MODULE_DESCRIPTOR_ALLOWANCE * sizeof(HMODULE),
&size_filled);
if (!status_enum_modules) return(orig);
num_modules = size_filled / sizeof(HMODULE);
// If this is more than we have allowed for (which is very unlikely)
// then we will truncate the list.
if (num_modules > MODULE_DESCRIPTOR_ALLOWANCE) {
num_modules = MODULE_DESCRIPTOR_ALLOWANCE;
OutputDebugString("*** DEBUGGER NUB WARNING: Truncated module list!\n");
}
for (counter = 0; counter < num_modules; counter++) {
lpfGetModuleInformation
(ph, handles_array[counter], &module_info, sizeof(MODULEINFO));
if (module_enumeration_descriptor_from_base_address
(head, (DWORD) module_info.lpBaseOfDll) == NULL) {
new = (MODULED*) malloc(sizeof(MODULED));
new->Next = NULL;
size_filled = lpfGetModuleFileNameEx(ph,
handles_array[counter],
new->ModuleName,
MAX_MODULE_NAME_LENGTH);
new->ModuleBase = (DWORD) module_info.lpBaseOfDll;
// It is convenient that the linked list maintains the iteration
// order, hence we use the following linking algorithm, with a
// special case for when the newly created node is the head node.
if (head == NULL)
head = new;
else
current->Next = new;
current = new;
}
}
return(head);
}
MODULED *update_process_module_list_windows_95
(DWORD process_id, HANDLE ph, MODULED *orig)
{
MODULEENTRY32 module_information;
HANDLE module_snapshot;
HINSTANCE kernel32 = LoadLibrary("KERNEL32.DLL");
BOOL status;
MODULED *current = orig;
MODULED *new = NULL;
MODULED *head = orig;
// Function pointers to ToolHelp routines in kernel32.
HANDLE (WINAPI *lpfCreateToolhelp32Snapshot) (DWORD, DWORD);
BOOL (WINAPI *lpfModule32First) (HANDLE, LPMODULEENTRY32);
BOOL (WINAPI *lpfModule32Next) (HANDLE, LPMODULEENTRY32);
// If we do not have the appropriate DLL, then give up immediately.
if (kernel32 == NULL)
return(orig);
// Otherwise, attempt to resolve the function pointers. Again, take an
// early failing exit if we cannot get addresses for the functions.
lpfCreateToolhelp32Snapshot =
(HANDLE (WINAPI*) (DWORD, DWORD))
GetProcAddress(kernel32, "CreateToolhelp32Snapshot");
lpfModule32First =
(BOOL (WINAPI*) (HANDLE, LPMODULEENTRY32))
GetProcAddress(kernel32, "Module32First");
lpfModule32Next =
(BOOL (WINAPI*) (HANDLE, LPMODULEENTRY32))
GetProcAddress(kernel32, "Module32Next");
if ((lpfCreateToolhelp32Snapshot == NULL) ||
(lpfModule32First == NULL) ||
(lpfModule32Next == NULL))
return(orig);
// Now call ToolHelp32. Request a snapshot of the data for loaded
// modules. Again, quit early if it fails.
module_snapshot =
lpfCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, process_id);
if (module_snapshot == INVALID_HANDLE_VALUE)
return(orig);
// Point CURRENT at the last descriptor in the list.
if (current != NULL) {
while (current->Next != NULL)
current = current->Next;
}
module_information.dwSize = sizeof(MODULEENTRY32);
status = lpfModule32First(module_snapshot, &module_information);
while (status) {
if (module_enumeration_descriptor_from_base_address
(head, (DWORD) module_information.modBaseAddr) == NULL) {
DWORD i = 0;
new = (MODULED*) malloc (sizeof(MODULED));
new->Next = NULL;
while (module_information.szExePath[i] != '\0') {
new->ModuleName[i] = module_information.szExePath[i];
i++;
}
new->ModuleName[i] = '\0';
new->ModuleBase = (DWORD) module_information.modBaseAddr;
if (head == NULL)
head = new;
else
current->Next = new;
current = new;
}
module_information.dwSize = sizeof(MODULEENTRY32);
status = lpfModule32Next(module_snapshot, &module_information);
}
CloseHandle(module_snapshot);
return(head);
}
MODULED *update_process_module_list
(DWORD process_id, HANDLE ph, MODULED *orig)
{
OSVERSIONINFO platform_data;
BOOL status_get_platform_data;
platform_data.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
status_get_platform_data = GetVersionEx(&platform_data);
if (status_get_platform_data) {
if (platform_data.dwPlatformId == VER_PLATFORM_WIN32_NT)
return(update_process_module_list_windows_nt(process_id, ph, orig));
else
return(update_process_module_list_windows_95(process_id, ph, orig));
}
else {
// We have to hope this does not happen!
return(orig);
}
}
NUB create_and_debug_process
(SERVER dummy,
char *module_name,
char *arguments,
NUBINT symbol_path_count,
char **symbol_paths,
NUBINT library_search_path_count,
char **library_search_paths,
char *working_directory,
NUBINT create_shell)
{
LPDBGPROCESS process_handle = (LPDBGPROCESS) malloc (sizeof (DBGPROCESS));
BOOL create_status;
DWORD locate_status;
DWORD creation_flags;
LPDBGTHREAD init_thread;
LPDBGLIBRARY init_library;
int i = 0;
int j = 0;
int k = 0;
char path_component[512];
char name_component[512];
char located_name[512];
char *name_starts;
char *search_path = NULL;
char *default_extension = ".exe";
char *original_path_variable;
char *extended_path_variable;
OSVERSIONINFO platform_data;
original_path_variable = malloc(ENVIRONMENT_VARIABLE_BUFFER_SIZE);
extended_path_variable = malloc(ENVIRONMENT_VARIABLE_BUFFER_SIZE);
platform_data.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&platform_data);
if (platform_data.dwPlatformId == VER_PLATFORM_WIN32_NT)
process_handle->Platform = PLATFORM_WINDOWS_NT;
else
process_handle->Platform = PLATFORM_WINDOWS_95;
// Handle some NUB functions which depend on the OS platform.
initialize_get_os_thread_cpu_time();
initialize_get_os_process_page_fault_count();
// Searching for the executable file.
// If the given executable name is path-qualified, then search in that
// path. Otherwise, adopt the Win32 conventions.
// Split the module_name into path_component and name_component. Do
// this by searching for the position of the last backslash in
// the name.
j = -1;
while (module_name[i] != '\0') {
if (module_name[i] == '\\')
j = i;
i++;
}
// And then, if necessary, copying the pathname and filename components
// to separate buffers.
if (j == -1) {
search_path = NULL;
for (i = 0; module_name[i] != '\0'; i++)
name_component[i] = module_name[i];
name_component[i] = '\0';
}
else {
search_path = path_component;
for (i = 0; i < j; i++) path_component[i] = module_name[i];
path_component[j] = '\0';
for (i = (j + 1); module_name[i] != '\0'; i++) {
name_component[k] = module_name[i];
k++;
}
name_component[k] = '\0';
}
// Call Windows to locate the executable file.
locate_status =
SearchPath(search_path,
name_component,
default_extension,
512,
located_name,
&name_starts);
if (locate_status == 0)
return(NULL);
// Decide on the creation flags
if (create_shell == 1)
creation_flags = DEBUG_PROCESS | CREATE_SUSPENDED | CREATE_NEW_CONSOLE;
else
creation_flags = DEBUG_PROCESS | CREATE_SUSPENDED;
// Make sure we remember the process name and the arguments.
(process_handle->Command) = module_name;
(process_handle->Arguments) = arguments;
if (working_directory[0] == '\0')
(process_handle->WorkingDirectory) = NULL;
else
(process_handle->WorkingDirectory) = working_directory;
// Also install the set of symbol file search paths.
process_handle->SymbolPaths =
construct_separated_string_list
(symbol_paths, (int) symbol_path_count, ';');
// ... same deal with library search paths.
process_handle->LibrarySearchPaths =
construct_separated_string_list
(library_search_paths, (int) library_search_path_count, ';');
// Fill in the startup info for our new process. This is all just
// default stuff.
(process_handle->StartupInfoContainer).cb = sizeof(STARTUPINFO);
(process_handle->StartupInfoContainer).lpReserved = NULL;
(process_handle->StartupInfoContainer).lpDesktop = NULL;
(process_handle->StartupInfoContainer).lpTitle = NULL;
(process_handle->StartupInfoContainer).dwX = 0;
(process_handle->StartupInfoContainer).dwY = 0;
(process_handle->StartupInfoContainer).dwXSize = 0;
(process_handle->StartupInfoContainer).dwYSize = 0;
(process_handle->StartupInfoContainer).dwXCountChars = 0;
(process_handle->StartupInfoContainer).dwYCountChars = 0;
(process_handle->StartupInfoContainer).dwFillAttribute = 0;
(process_handle->StartupInfoContainer).dwFlags
= STARTF_FORCEONFEEDBACK | STARTF_USESHOWWINDOW;
(process_handle->StartupInfoContainer).wShowWindow = SW_SHOWNORMAL;
(process_handle->StartupInfoContainer).cbReserved2 = 0;
(process_handle->StartupInfoContainer).lpReserved2 = NULL;
if (library_search_path_count != 0)
extend_path_environment_variable
(original_path_variable,
process_handle->LibrarySearchPaths,
extended_path_variable);
create_status =
CreateProcess
((LPCSTR) located_name,
(LPSTR) arguments,
(LPSECURITY_ATTRIBUTES) NULL,
(LPSECURITY_ATTRIBUTES) NULL,
TRUE,
creation_flags,
NULL,
process_handle->WorkingDirectory,
(LPSTARTUPINFO) &(process_handle->StartupInfoContainer),
(LPPROCESS_INFORMATION) &(process_handle->ProcessInfoContainer));
if (library_search_path_count != 0)
restore_path_environment_variable(original_path_variable);
if (!create_status)
{
// Error case: We could not create the debugee process.
free(process_handle);
return (NULL);
}
// Copy some information into the structure for the process.
process_handle->ProcessID
= (process_handle->ProcessInfoContainer).dwProcessId;
process_handle->ProcessHandle
= (process_handle->ProcessInfoContainer).hProcess;
// This is a launched debuggee.
process_handle->ProcessAttached = FALSE;
// Create the linked list entry for the main thread.
init_thread = (LPDBGTHREAD) malloc (sizeof(DBGTHREAD));
process_handle->ThreadList = init_thread;
process_handle->PendingEventThread = init_thread;
// Create the linked list entry for the main library (the exe).
init_library = (LPDBGLIBRARY) malloc (sizeof(DBGLIBRARY));
process_handle->LibraryList = init_library;
process_handle->DebugPointList = NULL;
process_handle->ModuleDescriptors = NULL;
// Initialize the interactive code segment table.
process_handle->InteractiveCodeSegments.FirstSegmentTable = NULL;
process_handle->InteractiveCodeSegments.CurrentSegmentTable = NULL;
// Initialize the hard-coded breakpoints counter to zero.
process_handle->HardCodedBreakpointCounter = 0;
// Initialize the debug event queue for the process.
initialize_debug_event_queue(process_handle);
// Initialize primary thread slots.
init_thread->Next = NULL;
init_thread->ThreadHandle = (process_handle->ProcessInfoContainer).hThread;
init_thread->ThreadContext = NULL;
init_thread->CachedThreadContext = NULL;
init_thread->Valid = TRUE;
init_thread->ThreadID = (process_handle->ProcessInfoContainer).dwThreadId;
init_thread->WaitingForDebugger = FALSE;
init_thread->SingleStepping = FALSE;
init_thread->NeedsBreakpointReplacement = FALSE;
init_thread->CurrentStackTrace = NULL;
init_thread->SteppingCaptureBreakpoints = NULL;
// Initialize primary library slots
init_library->Next = NULL;
init_library->Valid = TRUE;
init_library->CachedTypeTableBase = FALSE;
init_library->BoundaryCache.IndexNextEntry = 0;
init_library->BoundaryCache.IndexLastAddition = 0;
init_library->BoundaryCache.CacheFull = FALSE;
init_library->SymbolFile = NULL;
i = 0;
while (located_name[i] != '\0') {
init_library->DefaultImageName[i] = located_name[i];
i++;
}
init_library->DefaultImageName[i] = '\0';
// We have created the primary thread suspended.
set_application_state(process_handle, UNSTARTED);
// Initialize the data for receiving first-chance exceptions.
initialize_process_exception_information (process_handle);
// Set the base wallclock time.
process_handle->LastActivationWallClockTime =
(NUBINT) get_os_wall_clock_time(process_handle);
process_handle->CumulativeWallClockTime = (NUBINT) 0;
process_handle->ExitProcessFunction = (TARGET_ADDRESS)NULL;
process_handle->ExitingProcess = FALSE;
free(original_path_variable);
free(extended_path_variable);
// Return it as a handle.
return ((NUB) process_handle);
}
NUB debug_active_process
(SERVER dummy,
NUBPROCESS nubprocess,
NUBINT symbol_path_count,
char **symbol_paths,
char *system_JIT_information)
{
LPDBGPROCESS process_handle = (LPDBGPROCESS) malloc(sizeof(DBGPROCESS));
LPPROCESSD process_descriptor = (LPPROCESSD) nubprocess;
BOOL attach_status;
LPDBGTHREAD init_thread;
LPDBGLIBRARY init_library;
HANDLE hproc;
int i;
OSVERSIONINFO platform_data;
platform_data.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&platform_data);
if (platform_data.dwPlatformId == VER_PLATFORM_WIN32_NT)
process_handle->Platform = PLATFORM_WINDOWS_NT;
else
process_handle->Platform = PLATFORM_WINDOWS_95;
// Handle some NUB functions which depend on the OS platform.
initialize_get_os_thread_cpu_time();
initialize_get_os_process_page_fault_count();
// Also install the set of symbol file search paths.
process_handle->SymbolPaths =
construct_separated_string_list
(symbol_paths, (int) symbol_path_count, ';');
// We may also get passed a string-ized event handle from the
// system.
if (system_JIT_information[0] != '\0') {
DWORD dwid = 0;
int i = 0;
while (system_JIT_information[i] != '\0') {
dwid = (10 * dwid) + (((DWORD) system_JIT_information[i]) - (DWORD) '0');
i++;
}
process_handle->JITEventHandle = (HANDLE) dwid;
}
else {
process_handle->JITEventHandle = NULL;
}
// Go for the attachment, and open a handle on the process. If
// either operation fails, then bug out.
attach_status = DebugActiveProcess(process_descriptor->ActualProcessID);
hproc =
OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_descriptor->ActualProcessID);
if ((!attach_status) || (hproc == NULL)) {
free(process_handle);
return(NULL);
}
// Get an enumeration of loaded modules.
process_handle->ModuleDescriptors =
update_process_module_list(process_descriptor->ActualProcessID,
hproc,
NULL);
// Copy some information into the structure for the process.
process_handle->ProcessID = process_descriptor->ActualProcessID;
process_handle->ProcessHandle = hproc;
process_handle->ProcessAttached = TRUE;
// Create the linked list entry for the main thread.
init_thread = (LPDBGTHREAD) malloc (sizeof(DBGTHREAD));
process_handle->ThreadList = init_thread;
process_handle->PendingEventThread = init_thread;
// Create the linked list entry for the main library (the exe).
init_library = (LPDBGLIBRARY) malloc (sizeof(DBGLIBRARY));
process_handle->LibraryList = init_library;
process_handle->DebugPointList = NULL;
// Initialize the interactive code segment table.
process_handle->InteractiveCodeSegments.FirstSegmentTable = NULL;
process_handle->InteractiveCodeSegments.CurrentSegmentTable = NULL;
// Initialize the hard-coded breakpoints counter to zero.
process_handle->HardCodedBreakpointCounter = 0;
// Initialize the debug event queue for the process.
initialize_debug_event_queue(process_handle);
// Initialize primary thread slots.
init_thread->ThreadContext = NULL;
init_thread->CachedThreadContext = NULL;
init_thread->Next = NULL;
init_thread->Valid = TRUE;
init_thread->WaitingForDebugger = FALSE;
init_thread->SingleStepping = FALSE;
init_thread->NeedsBreakpointReplacement = FALSE;
init_thread->CurrentStackTrace = NULL;
init_thread->SteppingCaptureBreakpoints = NULL;
// Initialize primary library slots
init_library->Next = NULL;
init_library->Valid = TRUE;
init_library->CachedTypeTableBase = FALSE;
init_library->BoundaryCache.IndexNextEntry = 0;
init_library->BoundaryCache.IndexLastAddition = 0;
init_library->BoundaryCache.CacheFull = FALSE;
init_library->SymbolFile = NULL;
i = 0;
while (process_descriptor->ProcessName[i] != '\0') {
init_library->DefaultImageName[i] = process_descriptor->ProcessName[i];
i++;
}
init_library->DefaultImageName[i] = '\0';
set_application_state(process_handle, RUNNING);
// Initialize the data for receiving first-chance exceptions.
initialize_process_exception_information (process_handle);
// Set the base wallclock time.
process_handle->LastActivationWallClockTime =
(NUBINT) get_os_wall_clock_time(process_handle);
process_handle->CumulativeWallClockTime = (NUBINT) 0;
process_handle->ExitProcessFunction = (TARGET_ADDRESS)NULL;
process_handle->ExitingProcess = FALSE;
// Return it as a handle.
return ((NUB) process_handle);
}
syntax highlighted by Code2HTML, v. 0.9.1