diff --git a/gdb/.Sanitize b/gdb/.Sanitize index ba68f539866..0242d95de74 100644 --- a/gdb/.Sanitize +++ b/gdb/.Sanitize @@ -267,6 +267,8 @@ z8k-tdep.c # energize-patches - Part of Lucid support. # energize.c - Part of Lucid support. # energize.h - Part of Lucid support. +# i386-nlmstub.c - i386 NLM stub, not yet distributable because +# it contains Novell proprietary code. # infrun.hacked.c - An old version of infrun.c that had some # wait_for_inferior improvements by gnu, # which were never fully debugged. @@ -287,6 +289,7 @@ energize energize-patches energize.c energize.h +i386-nlmstub.c infrun.hacked.c remote-sa.sparc.c state.c diff --git a/gdb/i386-nlmstub.c b/gdb/i386-nlmstub.c new file mode 100644 index 00000000000..5604ed60981 --- /dev/null +++ b/gdb/i386-nlmstub.c @@ -0,0 +1,1057 @@ +/* i386-nlmstub.c -- NLM debugging stub for the i386. + + This is originally based on an m68k software stub written by Glenn + Engel at HP, but has changed quite a bit. It was modified for the + i386 by Jim Kingdon, Cygnus Support. It was modified to run under + NetWare by Ian Lance Taylor, Cygnus Support. + + This code is intended to produce an NLM (a NetWare Loadable Module) + to run under NetWare on an i386 platform. To create the NLM, + compile this code into an object file using the NLM SDK on any i386 + host, and use the nlmconv program (available in the GNU binutils) + to transform the resulting object file into an NLM. */ + +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or it's performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/************************************************************************/ +/***************************************************************************** + * + * (C) Copyright 1988-1993 Novell, Inc. + * All Rights Reserved. + * + * This program is an unpublished copyrighted work which is proprietary + * to Novell, Inc. and contains confidential information that is not + * to be reproduced or disclosed to any other person or entity without + * prior written consent from Novell, Inc. in each and every instance. + * + * WARNING: Unauthorized reproduction of this program as well as + * unauthorized preparation of derivative works based upon the + * program or distribution of copies by sale, rental, lease or + * lending are violations of federal copyright laws and state trade + * secret laws, punishable by civil and criminal penalties. + * + * $release$ + * $modname: loadstuff.h$ + * $version: 1.37$ + * $date: Fri, Jan 15, 1993$ + * + ****************************************************************************/ + + +/* WARNING: THIS IS NOT A COMPLETE OS HEADER FILE - DON'T GET CONFUSED + *********************************************************************** + * The information is this file is a subset of the OS LOADER.H. + * This file was created to reveal the LoadDefinitionStrucutre and some + * associated information to Cygnus Support to assist them in their + * efforts to develop GNU netware utilities. Don't confuse this file + * with LOADER.H or any other actually supported NetWare header. + +************************************************************************/ + +struct LoadDefinitionStructure +{ + struct LoadDefinitionStructure *LDLink; + struct LoadDefinitionStructure *LDKillLink; + struct LoadDefinitionStructure *LDScanLink; + struct ResourceTagStructure *LDResourceList; + LONG LDIdentificationNumber; + LONG LDCodeImageOffset; + LONG LDCodeImageLength; + LONG LDDataImageOffset; + LONG LDDataImageLength; + LONG LDUninitializedDataLength; + LONG LDCustomDataOffset; + LONG LDCustomDataSize; + LONG LDFlags; + LONG LDType; + LONG (*LDInitializationProcedure)( + struct LoadDefinitionStructure *LoadRecord, + struct ScreenStruct *screenID, + BYTE *CommandLine, + BYTE *loadDirectoryPath, + LONG uninitializedDataLength, + LONG fileHandle, + LONG (*ReadRoutine)( + LONG fileHandle, + LONG offset, + void *buffer, + LONG numberOfBytes), + LONG customDataOffset, + LONG customDataSize); + void (*LDExitProcedure)(void); + LONG (*LDCheckUnloadProcedure)( + struct ScreenStruct *screenID); + struct ExternalPublicDefinitionStructure *LDPublics; + BYTE LDFileName[36]; + BYTE LDName[128]; + LONG *LDCLIBLoadStructure; + LONG *LDNLMDebugger; + LONG LDParentID; + LONG LDReservedForCLIB; + LONG Reserved0; + LONG Reserved1; + void *LDModuleObjectHandle; /* If Instrumented BEW 10/16/90 */ + LONG LDMajorVersion; + LONG LDMinorVersion; + LONG LDRevision; + LONG LDYear; + LONG LDMonth; + LONG LDDay; + BYTE *LDCopyright; + LONG LDAllocAvailBytes; + LONG LDAllocFreeCount; + LONG LDLastGarbCollect; + LONG LDAlloc16Lists[64]; + LONG LDAlloc256Lists[12]; + LONG LDAlloc4kList; + struct DomainStructure *LDDomainID; /* This must be non-zero for the Alloc Hunt code to work right. */ + /* It also points to the domain structure. */ + struct LoadDefinitionStructure *LDEnvLink; + void *LDAllocPagesListHead; + struct ExternalPublicDefinitionStructure *LDTempPublicList; + LONG LDMessageLanguage; /* for enabling */ + BYTE **LDMessages; /* for enabling */ + LONG LDMessageCount; /* for enabling */ + BYTE *LDHelpFile; /* for enabling */ + LONG LDMessageBufferSize; /* for enabling */ + LONG LDHelpBufferSize; /* for enabling */ + LONG LDSharedCodeOffset; /* for protection */ + LONG LDSharedCodeLength; /* for protection */ + LONG LDSharedDataOffset; /* for protection */ + LONG LDSharedDataLength; /* for protection */ + LONG (*LDSharedInitProcedure)( + struct LoadDefinitionStructure *LoadRecord, + struct ScreenStruct *screenID, + BYTE *CommandLine); + void (*LDSharedExitProcedure)(void); + LONG LDRPCDataTable; + LONG LDRealRPCDataTable; + LONG LDRPCDataTableSize; + LONG LDNumberOfReferencedPublics; + struct ExternalPublicDefinitionStructure **LDReferencedPublics; + LONG LDNumberOfReferencedExports; +}; + + +/* define the LDFlags. */ + +#define LDModuleIsReEntrantBit 0x00000001 +#define LDModuleCanBeMultiplyLoadedBit 0x00000002 +#define LDSynchronizeStart 0x00000004 +#define LDPseudoPreemptionBit 0x00000008 +#define LDLoadInOSDomain 0x00000010 +#define LDDontUnloadBit 0x20000000 +#define LDModuleIsBeingDebugged 0x40000000 +#define LDMemoryOn4KBoundriesBit 0x80000000 + +/* LoadModule load options */ +#define LO_NORMAL 0x0000 +#define LO_STARTUP 0x0001 +#define LO_PROTECT 0x0002 +#define LO_DEBUG 0x0004 +#define LO_AUTO_LOAD 0x0008 +#define LO_DONT_PROMPT 0x0010 +#define LO_LOAD_LOW 0x0020 +#define LO_RETURN_HANDLE 0x0040 +#define LO_LOAD_SILENT 0x0080 + +/* Loader returned error codes */ +#define LOAD_COULD_NOT_FIND_FILE 1 +#define LOAD_ERROR_READING_FILE 2 +#define LOAD_NOT_NLM_FILE_FORMAT 3 +#define LOAD_WRONG_NLM_FILE_VERSION 4 +#define LOAD_REENTRANT_INITIALIZE_FAILURE 5 +#define LOAD_CAN_NOT_LOAD_MULTIPLE_COPIES 6 +#define LOAD_ALREADY_IN_PROGRESS 7 +#define LOAD_NOT_ENOUGH_MEMORY 8 +#define LOAD_INITIALIZE_FAILURE 9 +#define LOAD_INCONSISTENT_FILE_FORMAT 10 +#define LOAD_CAN_NOT_LOAD_AT_STARTUP 11 +#define LOAD_AUTO_LOAD_MODULES_NOT_LOADED 12 +#define LOAD_UNRESOLVED_EXTERNAL 13 +#define LOAD_PUBLIC_ALREADY_DEFINED 14 +#define LOAD_XDC_DATA_ERROR 15 +#define LOAD_NOT_OS_DOMAIN 16 + +/****************************************************************************/ + +/* The main thread ID. */ +static int mainthread; + +/* The debug server thread ID. */ +static int debugthread; + +/* The LoadDefinitionStructure of the NLM being debugged. */ +static struct LoadDefinitionStructure *handle; + +/* Whether we have connected to gdb. */ +static int talking; + +/* The actual first instruction in the program. */ +static unsigned char first_insn; + +/* An error message for the main thread to print. */ +static char *error_message; + +/* The AIO port handle. */ +static int AIOhandle; + +/* The console screen. */ +static int console_screen; + +/* BUFMAX defines the maximum number of characters in inbound/outbound + buffers. At least NUMREGBYTES*2 are needed for register packets */ +#define BUFMAX 400 + +/* remote_debug > 0 prints ill-formed commands in valid packets and + checksum errors. */ +static int remote_debug = 1; + +static const char hexchars[] = "0123456789abcdef"; + +/* Number of bytes of registers. */ +#define NUMREGBYTES 64 +enum regnames {EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI, + PC /* also known as eip */, + PS /* also known as eflags */, + CS, SS, DS, ES, FS, GS}; + +/* Register values. */ +static int registers[NUMREGBYTES/4]; + +/* Read a character from the serial port. This must busy wait, but + that's OK because we will be the only thread running anyhow. */ + +static int +getDebugChar () +{ + int err; + LONG got; + unsigned char ret; + + do + { + err = AIOReadData (AIOhandle, (char *) &ret, 1, &got); + if (err != 0) + { + error_message = "AIOReadData failed"; + ResumeThread (mainthread); + return -1; + } + } + while (got == 0); + + return ret; +} + +/* Write a character to the serial port. Returns 0 on failure, + non-zero on success. */ + +static int +putDebugChar (c) + unsigned char c; +{ + int err; + LONG put; + + err = AIOWriteData (AIOhandle, (char *) &c, 1, &put); + if (err != 0 || put != 1) + { + error_message = "AIOWriteData failed"; + ResumeThread (mainthread); + return 0; + } + return 1; +} + +/* Get the registers out of the frame information. */ + +static void +frame_to_registers (frame, regs) + T_TSS_StackFrame *frame; + int *regs; +{ + regs[EAX] = frame->ExceptionEAX; + regs[ECX] = frame->ExceptionECX; + regs[EDX] = frame->ExceptionEDX; + regs[EBX] = frame->ExceptionEBX; + regs[ESP] = frame->ExceptionESP; + regs[EBP] = frame->ExceptionEBP; + regs[ESI] = frame->ExceptionESI; + regs[EDI] = frame->ExceptionEDI; + regs[PC] = frame->ExceptionEIP; + regs[PS] = frame->ExceptionSystemFlags; + regs[CS] = frame->ExceptionCS[0]; + regs[SS] = frame->ExceptionSS[0]; + regs[DS] = frame->ExceptionDS[0]; + regs[ES] = frame->ExceptionES[0]; + regs[FS] = frame->ExceptionFS[0]; + regs[GS] = frame->ExceptionGS[0]; +} + +/* Put the registers back into the frame information. */ + +static void +registers_to_frame (regs, frame) + int *regs; + T_TSS_StackFrame *frame; +{ + frame->ExceptionEAX = regs[EAX]; + frame->ExceptionECX = regs[ECX]; + frame->ExceptionEDX = regs[EDX]; + frame->ExceptionEBX = regs[EBX]; + frame->ExceptionESP = regs[ESP]; + frame->ExceptionEBP = regs[EBP]; + frame->ExceptionESI = regs[ESI]; + frame->ExceptionEDI = regs[EDI]; + frame->ExceptionEIP = regs[PC]; + frame->ExceptionSystemFlags = regs[PS]; + frame->ExceptionCS[0] = regs[CS]; + frame->ExceptionSS[0] = regs[SS]; + frame->ExceptionDS[0] = regs[DS]; + frame->ExceptionES[0] = regs[ES]; + frame->ExceptionFS[0] = regs[FS]; + frame->ExceptionGS[0] = regs[GS]; +} + +/* Turn a hex character into a number. */ + +static int +hex (ch) + char ch; +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch-'a'+10); + if ((ch >= '0') && (ch <= '9')) + return (ch-'0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch-'A'+10); + return (-1); +} + +/* Scan for the sequence $#. Returns 0 on failure, + non-zero on success. */ + +static int +getpacket (buffer) + char * buffer; +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + int ch; + + do + { + /* wait around for the start character, ignore all other characters */ + while ((ch = getDebugChar()) != '$') + if (ch == -1) + return 0; + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) + { + ch = getDebugChar(); + if (ch == -1) + return 0; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') + { + ch = getDebugChar (); + if (ch == -1) + return 0; + xmitcsum = hex(ch) << 4; + ch = getDebugChar (); + if (ch == -1) + return 0; + xmitcsum += hex(ch); + if ((remote_debug ) && (checksum != xmitcsum)) + { + fprintf(stderr,"bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n", + checksum,xmitcsum,buffer); + } + + if (checksum != xmitcsum) + { + /* failed checksum */ + if (! putDebugChar('-')) + return 0; + } + else + { + /* successful transfer */ + if (! putDebugChar('+')) + return 0; + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') + { + if (! putDebugChar (buffer[0]) + || ! putDebugChar (buffer[1])) + return 0; + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); + + return 1; +} + +/* Send the packet in buffer. Returns 0 on failure, non-zero on + success. */ + +static int +putpacket (buffer) + char * buffer; +{ + unsigned char checksum; + int count; + int ch; + + /* $#. */ + do + { + if (! putDebugChar('$')) + return 0; + checksum = 0; + count = 0; + + while (ch=buffer[count]) + { + if (! putDebugChar(ch)) + return 0; + checksum += ch; + count += 1; + } + + if (! putDebugChar('#') + || ! putDebugChar(hexchars[checksum >> 4]) + || ! putDebugChar(hexchars[checksum % 16])) + return 0; + + ch = getDebugChar (); + if (ch == -1) + return 0; + } + while (ch != '+'); + + return 1; +} + +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static short error; + +static void +debug_error (format, parm) + char *format; + char *parm; +{ + if (remote_debug) + fprintf (stderr, format, parm); +} + +/* Address of a routine to RTE to if we get a memory fault. */ +static volatile void (*mem_fault_routine)() = NULL; + +/* Indicate to caller of mem2hex or hex2mem that there has been an + error. */ +static volatile int mem_err = 0; + +static void +set_mem_err () +{ + mem_err = 1; +} + +/* These are separate functions so that they are so short and sweet + that the compiler won't save any registers (if there is a fault + to mem_fault, they won't get restored, so there better not be any + saved). */ + +static int +get_char (addr) + char *addr; +{ + return *addr; +} + +static void +set_char (addr, val) + char *addr; + int val; +{ + *addr = val; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +/* If MAY_FAULT is non-zero, then we should set mem_err in response to + a fault; if zero treat a fault like any other fault in the stub. */ + +static char * +mem2hex (mem, buf, count, may_fault) + char *mem; + char *buf; + int count; + int may_fault; +{ + int i; + unsigned char ch; + + if (may_fault) + mem_fault_routine = set_mem_err; + for (i = 0; i < count; i++) + { + ch = get_char (mem++); + if (may_fault && mem_err) + return (buf); + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + if (may_fault) + mem_fault_routine = NULL; + return(buf); +} + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return a pointer to the character AFTER the last byte written */ + +static char * +hex2mem (buf, mem, count, may_fault) + char *buf; + char *mem; + int count; + int may_fault; +{ + int i; + unsigned char ch; + + if (may_fault) + mem_fault_routine = set_mem_err; + for (i=0;i=0) + { + *intValue = (*intValue <<4) | hexValue; + numChars ++; + } + else + break; + + (*ptr)++; + } + + return (numChars); +} + +/* This function does all command processing for interfacing to gdb. + It is called whenever an exception occurs in the module being + debugged. */ + +static LONG +handle_exception (T_StackFrame *old_frame) +{ + T_TSS_StackFrame *frame = (T_TSS_StackFrame *) old_frame; + int first = 0; + int sigval; + int addr, length; + char * ptr; + int newPC; + + /* Apparently the bell can sometimes be ringing at this point, and + should be stopped. */ + StopBell (); + + if (remote_debug) + { + ConsolePrintf ("vector=%d: %s, sr=0x%x, pc=0x%x, thread=%d\r\n", + frame->ExceptionNumber, + frame->ExceptionDescription, + frame->ExceptionSystemFlags, + frame->ExceptionEIP, + GetThreadID ()); + } + + /* If the NLM just started, we record the module load information + and the thread ID, and set a breakpoint at the first instruction + in the program. */ + if (frame->ExceptionNumber == START_NLM_EVENT + && handle == NULL) + { + debugthread = GetThreadID (); + handle = (struct LoadDefinitionStructure *) frame->ExceptionErrorCode; + first_insn = *(char *) handle->LDInitializationProcedure; + *(unsigned char *) handle->LDInitializationProcedure = 0xcc; + return RETURN_TO_PROGRAM; + } + + /* At the moment, we don't care about most of the unusual NetWare + exceptions. */ + if (frame->ExceptionNumber != TERMINATE_NLM_EVENT + && frame->ExceptionNumber > 31) + return RETURN_TO_PROGRAM; + + /* Reset the initial breakpoint if necessary. */ + if (frame->ExceptionNumber == 3 + && frame->ExceptionEIP == (LONG) handle->LDInitializationProcedure + 1 + && *(unsigned char *) handle->LDInitializationProcedure == 0xcc) + { + *(char *) handle->LDInitializationProcedure = first_insn; + frame->ExceptionEIP = (LONG) handle->LDInitializationProcedure; + first = 1; + } + + /* FIXME: How do we know that this exception has anything to do with + the program we are debugging? We can check whether the PC is in + the range of the module we are debugging, but that doesn't help + much since an error could occur in a library routine. */ + + frame_to_registers (frame, registers); + + /* reply to host that an exception has occurred */ + if (frame->ExceptionNumber == TERMINATE_NLM_EVENT) + { + /* There is no way to get the exit status. */ + remcomOutBuffer[0] = 'W'; + remcomOutBuffer[1] = hexchars[0]; + remcomOutBuffer[2] = hexchars[0]; + remcomOutBuffer[3] = 0; + } + else + { + sigval = computeSignal (frame->ExceptionNumber); + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[sigval >> 4]; + remcomOutBuffer[2] = hexchars[sigval % 16]; + remcomOutBuffer[3] = 0; + if (first) + { + remcomOutBuffer[0] = 'N'; + sprintf (remcomOutBuffer + 3, "0x%x;0x%x;0x%x", + handle->LDCodeImageOffset, + handle->LDDataImageOffset, + handle->LDDataImageOffset + handle->LDDataImageLength); + } + } + + if (! putpacket(remcomOutBuffer)) + return RETURN_TO_NEXT_DEBUGGER; + + if (frame->ExceptionNumber == TERMINATE_NLM_EVENT) + { + ResumeThread (mainthread); + return RETURN_TO_PROGRAM; + } + + while (1) + { + error = 0; + remcomOutBuffer[0] = 0; + if (! getpacket (remcomInBuffer)) + return RETURN_TO_NEXT_DEBUGGER; + talking = 1; + switch (remcomInBuffer[0]) + { + case '?': + sigval = computeSignal (frame->ExceptionNumber); + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[sigval >> 4]; + remcomOutBuffer[2] = hexchars[sigval % 16]; + remcomOutBuffer[3] = 0; + if (first) + { + remcomOutBuffer[0] = 'N'; + sprintf (remcomOutBuffer + 3, "0x%x;0x%x;0x%x", + handle->LDCodeImageOffset, + handle->LDDataImageOffset, + (handle->LDDataImageOffset + + handle->LDDataImageLength)); + } + break; + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + break; + case 'g': + /* return the value of the CPU registers */ + mem2hex((char*) registers, remcomOutBuffer, NUMREGBYTES, 0); + break; + case 'G': + /* set the value of the CPU registers - return OK */ + hex2mem(&remcomInBuffer[1], (char*) registers, NUMREGBYTES, 0); + strcpy(remcomOutBuffer,"OK"); + break; + + case 'm': + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr,&addr)) + if (*(ptr++) == ',') + if (hexToInt(&ptr,&length)) + { + ptr = 0; + mem_err = 0; + mem2hex((char*) addr, remcomOutBuffer, length, 1); + if (mem_err) + { + strcpy (remcomOutBuffer, "E03"); + debug_error ("memory fault"); + } + } + + if (ptr) + { + strcpy(remcomOutBuffer,"E01"); + debug_error("malformed read memory command: %s",remcomInBuffer); + } + break; + + case 'M': + /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr,&addr)) + if (*(ptr++) == ',') + if (hexToInt(&ptr,&length)) + if (*(ptr++) == ':') + { + mem_err = 0; + hex2mem(ptr, (char*) addr, length, 1); + + if (mem_err) + { + strcpy (remcomOutBuffer, "E03"); + debug_error ("memory fault"); + } + else + { + strcpy(remcomOutBuffer,"OK"); + } + + ptr = 0; + } + if (ptr) + { + strcpy(remcomOutBuffer,"E02"); + debug_error("malformed write memory command: %s",remcomInBuffer); + } + break; + + case 'c': + case 's': + /* cAA..AA Continue at address AA..AA(optional) */ + /* sAA..AA Step one instruction from AA..AA(optional) */ + /* try to read optional parameter, pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr,&addr)) + registers[ PC ] = addr; + + newPC = registers[ PC]; + + /* clear the trace bit */ + registers[ PS ] &= 0xfffffeff; + + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') registers[ PS ] |= 0x100; + + registers_to_frame (registers, frame); + return RETURN_TO_PROGRAM; + + case 'k': + /* kill the program */ + KillMe (handle); + ResumeThread (mainthread); + return RETURN_TO_PROGRAM; + } + + /* reply to the request */ + if (! putpacket(remcomOutBuffer)) + return RETURN_TO_NEXT_DEBUGGER; + } +} + +/* Start up. The main thread opens the named serial I/O port, loads + the named NLM module and then goes to sleep. The serial I/O port + is named as a board number and a port number. It would be more DOS + like to provide a menu of available serial ports, but I don't want + to have to figure out how to do that. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int hardware, board, port; + LONG err; + struct debuggerStructure s; + char *cmdlin; + int i; + + /* Create a screen for the debugger. */ + console_screen = CreateScreen ("System Console", 0); + if (DisplayScreen (console_screen) != ESUCCESS) + fprintf (stderr, "DisplayScreen failed\n"); + + if (argc < 4) + { + fprintf (stderr, + "Usage: load gdbserver board port program [arguments]\n"); + exit (1); + } + + hardware = -1; + board = strtol (argv[1], (char **) NULL, 0); + port = strtol (argv[2], (char **) NULL, 0); + + err = AIOAcquirePort (&hardware, &board, &port, &AIOhandle); + if (err != AIO_SUCCESS) + { + switch (err) + { + case AIO_PORT_NOT_AVAILABLE: + fprintf (stderr, "Port not available\n"); + break; + + case AIO_BOARD_NUMBER_INVALID: + case AIO_PORT_NUMBER_INVALID: + fprintf (stderr, "No such port\n"); + break; + + default: + fprintf (stderr, "Could not open port: %d\n", err); + break; + } + + exit (1); + } + + err = AIOConfigurePort (AIOhandle, AIO_BAUD_9600, AIO_DATA_BITS_8, + AIO_STOP_BITS_1, AIO_PARITY_NONE, + AIO_HARDWARE_FLOW_CONTROL_OFF); + if (err != AIO_SUCCESS) + { + fprintf (stderr, "Could not configure port: %d\n", err); + AIOReleasePort (AIOhandle); + exit (1); + } + + /* Register ourselves as an alternate debugger. */ + memset (&s, 0, sizeof s); + s.DDSResourceTag = ((struct ResourceTagStructure *) + AllocateResourceTag (GetNLMHandle (), + "gdbserver", + DebuggerSignature)); + if (s.DDSResourceTag == 0) + { + fprintf (stderr, "AllocateResourceTag failed\n"); + AIOReleasePort (AIOhandle); + exit (1); + } + s.DDSdebuggerEntry = handle_exception; + s.DDSFlags = TSS_FRAME_BIT; + + err = RegisterDebuggerRTag (&s, AT_FIRST); + if (err != 0) + { + fprintf (stderr, "RegisterDebuggerRTag failed\n"); + AIOReleasePort (AIOhandle); + exit (1); + } + + /* Get the command line we were invoked with, and advance it past + our name and the board and port arguments. */ + cmdlin = getcmd ((char *) NULL); + for (i = 0; i < 2; i++) + { + while (! isspace (*cmdlin)) + ++cmdlin; + while (isspace (*cmdlin)) + ++cmdlin; + } + + /* In case GDB is started before us, ack any packets (presumably + "$?#xx") sitting there. */ + if (! putDebugChar ('+')) + { + fprintf (stderr, "putDebugChar failed\n"); + UnRegisterDebugger (&s); + AIOReleasePort (AIOhandle); + exit (1); + } + + mainthread = GetThreadID (); + handle = NULL; + talking = 0; + + if (remote_debug > 0) + ConsolePrintf ("About to call LoadModule with \"%s\" %d %d\r\n", + cmdlin, console_screen, __GetScreenID (console_screen)); + + /* Start up the module to be debugged. */ + err = LoadModule ((struct ScreenStruct *) __GetScreenID (console_screen), + cmdlin, LO_DEBUG); + if (err != 0) + { + fprintf (stderr, "LoadModule failed: %d\n", err); + UnRegisterDebugger (&s); + AIOReleasePort (AIOhandle); + exit (1); + } + + /* By the time we reach this point in the code the debugger thread + should have received a START_NLM_EVENT and gone to sleep. */ + if (remote_debug > 0) + ConsolePrintf ("Resuming %d, suspending %d\r\n", debugthread, mainthread); + ResumeThread (debugthread); + SuspendThread (mainthread); + + /* If we are woken up, print an optional error message, deregister + ourselves and exit. */ + if (error_message != NULL) + fprintf (stderr, "%s\n", error_message); + UnRegisterDebugger (&s); + AIOReleasePort (AIOhandle); + exit (0); +}