aboutsummaryrefslogtreecommitdiff
path: root/gdb/i386-nlmstub.c
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@airs.com>1993-09-06 19:48:43 +0000
committerIan Lance Taylor <ian@airs.com>1993-09-06 19:48:43 +0000
commit72dd16ea2c15565d5f67ff4d6db8e87fa5940eb4 (patch)
treedd2743a7df96110d4173957b467c873a8fce544c /gdb/i386-nlmstub.c
parent758aeb93fcf83641a18805e0637a3b403cb8fabc (diff)
downloadgdb-72dd16ea2c15565d5f67ff4d6db8e87fa5940eb4.zip
gdb-72dd16ea2c15565d5f67ff4d6db8e87fa5940eb4.tar.gz
gdb-72dd16ea2c15565d5f67ff4d6db8e87fa5940eb4.tar.bz2
Snapshot of stand alone i386 NLM debugging stub. Currently sanitized
out because it contains Novell proprietary code.
Diffstat (limited to 'gdb/i386-nlmstub.c')
-rw-r--r--gdb/i386-nlmstub.c1057
1 files changed, 1057 insertions, 0 deletions
diff --git a/gdb/i386-nlmstub.c b/gdb/i386-nlmstub.c
new file mode 100644
index 0000000..5604ed6
--- /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
+ *
+ * $<packet info>#<checksum>.
+ *
+ * where
+ * <packet info> :: <characters representing the command or response>
+ * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
+ *
+ * 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 <dfs.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+#include <aio.h>
+#include <conio.h>
+#include <advanced.h>
+#include <debugapi.h>
+#include <process.h>
+
+/************************************************************************/
+/*****************************************************************************
+ *
+ * (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 $<data>#<checksum>. 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;
+
+ /* $<packet info>#<checksum>. */
+ 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<count;i++)
+ {
+ ch = hex(*buf++) << 4;
+ ch = ch + hex(*buf++);
+ set_char (mem++, ch);
+ if (may_fault && mem_err)
+ return (mem);
+ }
+ if (may_fault)
+ mem_fault_routine = NULL;
+ return(mem);
+}
+
+/* This function takes the 386 exception vector and attempts to
+ translate this number into a unix compatible signal value. */
+
+static int
+computeSignal (exceptionVector)
+ int exceptionVector;
+{
+ int sigval;
+ switch (exceptionVector)
+ {
+ case 0 : sigval = 8; break; /* divide by zero */
+ case 1 : sigval = 5; break; /* debug exception */
+ case 3 : sigval = 5; break; /* breakpoint */
+ case 4 : sigval = 16; break; /* into instruction (overflow) */
+ case 5 : sigval = 16; break; /* bound instruction */
+ case 6 : sigval = 4; break; /* Invalid opcode */
+ case 7 : sigval = 8; break; /* coprocessor not available */
+ case 8 : sigval = 7; break; /* double fault */
+ case 9 : sigval = 11; break; /* coprocessor segment overrun */
+ case 10 : sigval = 11; break; /* Invalid TSS */
+ case 11 : sigval = 11; break; /* Segment not present */
+ case 12 : sigval = 11; break; /* stack exception */
+ case 13 : sigval = 11; break; /* general protection */
+ case 14 : sigval = 11; break; /* page fault */
+ case 16 : sigval = 7; break; /* coprocessor error */
+ default:
+ sigval = 7; /* "software generated"*/
+ }
+ return (sigval);
+}
+
+/**********************************************/
+/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */
+/* RETURN NUMBER OF CHARS PROCESSED */
+/**********************************************/
+static int
+hexToInt(ptr, intValue)
+ char **ptr;
+ int *intValue;
+{
+ int numChars = 0;
+ int hexValue;
+
+ *intValue = 0;
+
+ while (**ptr)
+ {
+ hexValue = hex(**ptr);
+ if (hexValue >=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);
+}