RFA: New port: ia64-hp-openvms - the stub

Tristan Gingold
2012-02-10T13:44:22Z

Hi,

this is the debugger stub for ia64 VMS.
Because it is independent of gdb (not unlike gdbserver) and because it isn't a one file stub, I think it is worth creating a subdirectory.
I think we should also move the existing stubs there.

The interesting part is that on OpenVMS, the debugger is a shared library that is loaded with the application when debugging is needed (either from the start or during the run - like attach). Just think about catching SIGTRAP, SIGSEGV, SIGBUS,… from a LD_PRELOAD binary.

The stub was written from scratch because it is highly OpenVMS dependent and in particular the standard C library shouldn't be used.

The stub is not complete: some registers are partially or not handled, and inferior procedure call is not yet implemented (will be the funny part). But it has already be extremely useful to debug some applications.

Tristan.

gdb/stubs:
2012-02-10 Tristan Gingold <gingold@adacore.com>

* buildvms.com: New file.
* ia64vms-stub.c: New file.

diff --git a/gdb/stubs/buildvms.com b/gdb/stubs/buildvms.com
new file mode 100644
index 0000000..5ac2bf9
--- /dev/null
+++ b/gdb/stubs/buildvms.com
@@ -0,0 +1,9 @@
+$! Command to build the gdb stub
+$cc ia64vms-stub +sys$library:sys$lib_c.tlb/lib
+$ link/notraceback/sysexe/map=stub.map/full/share=gdbstub.exe ia64vms-stub,sys$input/opt
+$deck
+cluster=gdbzero
+collect=gdbzero, XFER_PSECT
+$eod
+$! Example of use.
+$ DEFINE /nolog LIB$DEBUG sys$login:gdbstub.exe
diff --git a/gdb/stubs/ia64vms-stub.c b/gdb/stubs/ia64vms-stub.c
new file mode 100644
index 0000000..256b95f
--- /dev/null
+++ b/gdb/stubs/ia64vms-stub.c
@@ -0,0 +1,1583 @@
+/* GDB stub for Itanium OpenVMS
+ Copyright (C) 2012, Free Software Foundation, Inc.
+
+ Contributed by Tristan Gingold, AdaCore.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* On VMS, the debugger (in our case the stub) is loaded in the process and
+ executed (via SYS$IMGSTA) before the main entry point of the executable.
+ In UNIX parlance, this is like using LD_PRELOAD and debug via installing
+ SIGTRAP, SIGSEGV... handlers.
+
+ This is currently a partial implementation. In particular, modifying
+ registers is currently not implemented, as well as inferior procedure
+ calls.
+
+ This is written in very low-level C, in order not to use the C runtime,
+ because it may have weird consequences on the program being debugged.
+*/
+
+#define __NEW_STARLET 1
+#include <descrip.h>
+#include <iledef.h>
+#include <efndef.h>
+#include <in.h>
+#include <inet.h>
+#include <iodef.h>
+#include <ssdef.h>
+#include <starlet.h>
+#include <stsdef.h>
+#include <tcpip$inetdef.h>
+
+#include <lib$routines.h>
+#include <ots$routines.h>
+#include <str$routines.h>
+#include <libdef.h>
+#include <clidef.h>
+#include <iosbdef.h>
+#include <dvidef.h>
+#include <lnmdef.h>
+#include <builtins.h>
+#include <prtdef.h>
+#include <psldef.h>
+#include <ssdef.h>
+#include <chfdef.h>
+
+#include <lib_c/intstkdef.h>
+#include <lib_c/psrdef.h>
+
+/* Declared in lib$ots. */
+extern void ots$fill (void *addr, size_t len, unsigned char b);
+extern int ots$strcmp_eql (const void *str1, size_t str1len,
+ const void *str2, size_t str2len);
+
+/* Stub port number. */
+static unsigned int serv_port = 1234;
+
+/* If true, display packets exchanged with gdb. */
+static int trace_pkt = 0;
+
+/* If true, display info about entry point. */
+static int trace_entry = 0;
+
+/* If true, display all exceptions. */
+static int trace_excp = 0;
+
+/* Connect inet device I/O channel. */
+static unsigned short conn_channel;
+
+/* Socket characteristics. Apparently, there are no declaration for it in
+ standard headers. */
+struct sockchar
+{
+ unsigned short prot;
+ unsigned char type;
+ unsigned char af;
+};
+
+/* IA64 integer register representation. */
+union ia64_ireg
+{
+ unsigned __int64 v;
+ unsigned char b[8];
+};
+
+/* IA64 register numbers, as defined by ia64-tdep.h. */
+#define IA64_GR0_REGNUM 0
+#define IA64_GR32_REGNUM (IA64_GR0_REGNUM + 32)
+
+/* Floating point registers; 128 82-bit wide registers. */
+#define IA64_FR0_REGNUM 128
+
+/* Predicate registers; There are 64 of these one bit registers. It'd
+ be more convenient (implementation-wise) to use a single 64 bit
+ word with all of these register in them. Note that there's also a
+ IA64_PR_REGNUM below which contains all the bits and is used for
+ communicating the actual values to the target. */
+#define IA64_PR0_REGNUM 256
+
+/* Branch registers: 8 64-bit registers for holding branch targets. */
+#define IA64_BR0_REGNUM 320
+
+/* Virtual frame pointer; this matches IA64_FRAME_POINTER_REGNUM in
+ gcc/config/ia64/ia64.h. */
+#define IA64_VFP_REGNUM 328
+
+/* Virtual return address pointer; this matches
+ IA64_RETURN_ADDRESS_POINTER_REGNUM in gcc/config/ia64/ia64.h. */
+#define IA64_VRAP_REGNUM 329
+
+/* Predicate registers: There are 64 of these 1-bit registers. We
+ define a single register which is used to communicate these values
+ to/from the target. We will somehow contrive to make it appear
+ that IA64_PR0_REGNUM thru IA64_PR63_REGNUM hold the actual values. */
+#define IA64_PR_REGNUM 330
+
+/* Instruction pointer: 64 bits wide. */
+#define IA64_IP_REGNUM 331
+
+/* Process Status Register. */
+#define IA64_PSR_REGNUM 332
+
+/* Current Frame Marker (raw form may be the cr.ifs). */
+#define IA64_CFM_REGNUM 333
+
+/* Application registers; 128 64-bit wide registers possible, but some
+ of them are reserved. */
+#define IA64_AR0_REGNUM 334
+#define IA64_KR0_REGNUM (IA64_AR0_REGNUM + 0)
+#define IA64_KR7_REGNUM (IA64_KR0_REGNUM + 7)
+
+#define IA64_RSC_REGNUM (IA64_AR0_REGNUM + 16)
+#define IA64_BSP_REGNUM (IA64_AR0_REGNUM + 17)
+#define IA64_BSPSTORE_REGNUM (IA64_AR0_REGNUM + 18)
+#define IA64_RNAT_REGNUM (IA64_AR0_REGNUM + 19)
+#define IA64_FCR_REGNUM (IA64_AR0_REGNUM + 21)
+#define IA64_EFLAG_REGNUM (IA64_AR0_REGNUM + 24)
+#define IA64_CSD_REGNUM (IA64_AR0_REGNUM + 25)
+#define IA64_SSD_REGNUM (IA64_AR0_REGNUM + 26)
+#define IA64_CFLG_REGNUM (IA64_AR0_REGNUM + 27)
+#define IA64_FSR_REGNUM (IA64_AR0_REGNUM + 28)
+#define IA64_FIR_REGNUM (IA64_AR0_REGNUM + 29)
+#define IA64_FDR_REGNUM (IA64_AR0_REGNUM + 30)
+#define IA64_CCV_REGNUM (IA64_AR0_REGNUM + 32)
+#define IA64_UNAT_REGNUM (IA64_AR0_REGNUM + 36)
+#define IA64_FPSR_REGNUM (IA64_AR0_REGNUM + 40)
+#define IA64_ITC_REGNUM (IA64_AR0_REGNUM + 44)
+#define IA64_PFS_REGNUM (IA64_AR0_REGNUM + 64)
+#define IA64_LC_REGNUM (IA64_AR0_REGNUM + 65)
+#define IA64_EC_REGNUM (IA64_AR0_REGNUM + 66)
+
+/* NAT (Not A Thing) Bits for the general registers; there are 128 of
+ these. */
+#define IA64_NAT0_REGNUM 462
+
+/* Process registers when a condition is caught. */
+struct ia64_all_regs
+{
+ union ia64_ireg gr[32];
+ union ia64_ireg br[8];
+ union ia64_ireg ip;
+ union ia64_ireg psr;
+ union ia64_ireg bsp;
+ union ia64_ireg cfm;
+ union ia64_ireg pfs;
+ union ia64_ireg pr;
+};
+
+static struct ia64_all_regs regs;
+
+/* IO channel for the terminal. */
+static unsigned short term_chan;
+
+/* Output buffer and length. */
+static char term_buf[128];
+static int term_buf_len;
+
+/* Buffer for communication with gdb. */
+static unsigned char gdb_buf[sizeof (regs) * 2 + 64];
+static unsigned int gdb_blen;
+
+/* Previous primary handler. */
+static void *prevhnd;
+
+/* Entry point address and bundle. */
+static unsigned __int64 entry_pc;
+static unsigned char entry_saved[16];
+
+/* Write on the terminal. */
+
+static void
+term_raw_write (const char *str, unsigned int len)
+{
+ unsigned short status;
+ struct _iosb iosb;
+
+ status = sys$qiow (EFN$C_ENF, /* Event flag. */
+ term_chan, /* I/O channel. */
+ IO$_WRITEVBLK, /* I/O function code. */
+ &iosb, /* I/O status block. */
+ 0, /* Ast service routine. */
+ 0, /* Ast parameter. */
+ (char *)str, /* P1 - buffer address. */
+ len, /* P2 - buffer length. */
+ 0, 0, 0, 0);
+
+ if (status & STS$M_SUCCESS)
+ status = iosb.iosb$w_status;
+ if (!(status & STS$M_SUCCESS))
+ LIB$SIGNAL (status);
+}
+
+/* Flush ther term buffer. */
+
+static void
+term_flush (void)
+{
+ if (term_buf_len != 0)
+ {
+ term_raw_write (term_buf, term_buf_len);
+ term_buf_len = 0;
+ }
+}
+
+/* Write a single character, without translation. */
+
+static void
+term_raw_putchar (char c)
+{
+ if (term_buf_len == sizeof (term_buf))
+ term_flush ();
+ term_buf[term_buf_len++] = c;
+}
+
+/* Write character C. Translate '\n' to '\n\r'. */
+
+static void
+term_putc (char c)
+{
+ term_raw_putchar (c);
+ if (c == '\n')
+ {
+ term_raw_putchar ('\r');
+ term_flush ();
+ }
+}
+
+/* Write a C string. */
+
+static void
+term_puts (const char *str)
+{
+ while (*str)
+ term_putc (*str++);
+}
+
+/* Write LEN bytes from STR. */
+
+static void
+term_write (const char *str, unsigned int len)
+{
+ for (; len > 0; len--)
+ term_putc (*str++);
+}
+
+static const char hex[] = "0123456789abcdef";
+
+/* Write V as a 16 hexa digits number. */
+
+static void
+term_puthex8 (unsigned __int64 v)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ {
+ term_putc (hex[(v >> 60) & 0x0f]);
+ v <<= 4;
+ }
+}
+
+/* Write a byte in hexa (two digits). */
+
+void
+term_puthex1 (unsigned int v)
+{
+ term_putc (hex[v >> 4]);
+ term_putc (hex[v & 0x0f]);
+}
+
+/* Write V in decimal. */
+
+static void
+term_putdec (unsigned __int64 v)
+{
+ char res[20];
+ int i;
+
+ i = sizeof (res) - 1;
+ while (1)
+ {
+ res[i] = '0' + (v % 10);
+ v /= 10;
+ if (v == 0 || i == 0)
+ break;
+ i--;
+ }
+ term_write (res + i, sizeof (res) - i);
+}
+
+/* Write P in hexa. */
+
+static void
+term_putp (void *p)
+{
+ term_puthex8 ((unsigned __int64)p);
+}
+
+/* New line. */
+
+static void
+term_putnl (void)
+{
+ term_putc ('\n');
+}
+
+/* Initialize terminal. */
+
+static void
+term_init (void)
+{
+ unsigned int status,i;
+ unsigned short len;
+ char resstring[LNM$C_NAMLENGTH];
+ static const $DESCRIPTOR (tabdesc, "LNM$FILE_DEV");
+ static const $DESCRIPTOR (logdesc, "SYS$OUTPUT");
+ $DESCRIPTOR (term_desc, resstring);
+ ILE3 item_lst[2];
+
+ item_lst[0].ile3$w_length = LNM$C_NAMLENGTH;
+ item_lst[0].ile3$w_code = LNM$_STRING;
+ item_lst[0].ile3$ps_bufaddr = resstring;
+ item_lst[0].ile3$ps_retlen_addr = &len;
+ item_lst[1].ile3$w_length = 0;
+ item_lst[1].ile3$w_code = 0;
+
+ /* Translate the logical name. */
+ status = SYS$TRNLNM (0, /* Attr of the logical name. */
+ (void *) &tabdesc, /* Logical name table. */
+ (void *) &logdesc, /* Logical name. */
+ 0, /* Access mode. */
+ item_lst); /* Item list. */
+ if (!(status & STS$M_SUCCESS))
+ LIB$SIGNAL (status);
+
+ term_desc.dsc$w_length = len;
+
+ /* Examine 4-byte header. Skip escape sequence. */
+ if (resstring[0] == 0x1B)
+ {
+ term_desc.dsc$w_length -= 4;
+ term_desc.dsc$a_pointer += 4;
+ }
+
+ /* Assign a channel. */
+ status = sys$assign (&term_desc, /* Device name. */
+ &term_chan, /* I/O channel. */
+ 0, /* Access mode. */
+ 0);
+ if (!(status & STS$M_SUCCESS))
+ LIB$SIGNAL (status);
+}
+
+/* Convert from native endianness to network endianness (and vice-versa). */
+
+static unsigned int
+wordswap (unsigned int v)
+{
+ return ((v & 0xff) << 8) | ((v >> 8) & 0xff);
+}
+
+/* Initialize the socket connection, and wait for a client. */
+
+static void
+sock_init (void)
+{
+ struct _iosb iosb;
+ unsigned int status;
+
+ /* Listen channel and characteristics. */
+ unsigned short listen_channel;
+ struct sockchar listen_sockchar;
+
+ /* Client address. */
+ unsigned short cli_addrlen;
+ struct sockaddr_in cli_addr;
+ ILE3 cli_itemlst;
+
+ /* Our address. */
+ struct sockaddr_in serv_addr;
+ ILE2 serv_itemlst;
+
+ /* Reuseaddr option value (on). */
+ int optval = 1;
+ ILE2 sockopt_itemlst;
+ ILE2 reuseaddr_itemlst;
+
+ /* TCP/IP network pseudodevice. */
+ static const $DESCRIPTOR (inet_device, "TCPIP$DEVICE:");
+
+ /* Initialize socket characteristics. */
+ listen_sockchar.prot = TCPIP$C_TCP;
+ listen_sockchar.type = TCPIP$C_STREAM;
+ listen_sockchar.af = TCPIP$C_AF_INET;
+
+ /* Assign I/O channels to network device. */
+ status = sys$assign ((void *) &inet_device, &listen_channel, 0, 0);
+ if (status & STS$M_SUCCESS)
+ status = sys$assign ((void *) &inet_device, &conn_channel, 0, 0);
+ if (!(status & STS$M_SUCCESS))
+ {
+ term_puts ("Failed to assign I/O channel(s)\n\r");
+ LIB$SIGNAL (status);
+ }
+
+ /* Create a listen socket. */
+ status = sys$qiow (EFN$C_ENF, /* Event flag. */
+ listen_channel, /* I/O channel. */
+ IO$_SETMODE, /* I/O function code. */
+ &iosb, /* I/O status block. */
+ 0, /* Ast service routine. */
+ 0, /* Ast parameter. */
+ &listen_sockchar, /* P1 - socket characteristics. */
+ 0, 0, 0, 0, 0);
+ if (status & STS$M_SUCCESS)
+ status = iosb.iosb$w_status;
+ if (!(status & STS$M_SUCCESS))
+ {
+ term_puts ("Failed to create socket\n\r");
+ LIB$SIGNAL (status);
+ }
+
+ /* Set reuse address option. */
+ /* Initialize reuseaddr's item-list element. */
+ reuseaddr_itemlst.ile2$w_length = sizeof (optval);
+ reuseaddr_itemlst.ile2$w_code = TCPIP$C_REUSEADDR;
+ reuseaddr_itemlst.ile2$ps_bufaddr = &optval;
+
+ /* Initialize setsockopt's item-list descriptor. */
+ sockopt_itemlst.ile2$w_length = sizeof (reuseaddr_itemlst);
+ sockopt_itemlst.ile2$w_code = TCPIP$C_SOCKOPT;
+ sockopt_itemlst.ile2$ps_bufaddr = &reuseaddr_itemlst;
+
+ status = sys$qiow (EFN$C_ENF, /* Event flag. */
+ listen_channel, /* I/O channel. */
+ IO$_SETMODE, /* I/O function code. */
+ &iosb, /* I/O status block. */
+ 0, /* Ast service routine. */
+ 0, /* Ast parameter. */
+ 0, /* P1. */
+ 0, /* P2. */
+ 0, /* P3. */
+ 0, /* P4. */
+ (__int64) &sockopt_itemlst, /* P5 - socket options. */
+ 0);
+ if (status & STS$M_SUCCESS)
+ status = iosb.iosb$w_status;
+ if (!(status & STS$M_SUCCESS))
+ {
+ term_puts ("Failed to set socket option\n\r");
+ LIB$SIGNAL (status);
+ }
+
+ /* Bind server's ip address and port number to listen socket. */
+ /* Initialize server's socket address structure. */
+ ots$fill (&serv_addr, sizeof (serv_addr), 0);
+ serv_addr.sin_family = TCPIP$C_AF_INET;
+ serv_addr.sin_port = wordswap (serv_port);
+ serv_addr.sin_addr.s_addr = TCPIP$C_INADDR_ANY;
+
+ /* Initialize server's item-list descriptor. */
+ serv_itemlst.ile2$w_length = sizeof (serv_addr);
+ serv_itemlst.ile2$w_code = TCPIP$C_SOCK_NAME;
+ serv_itemlst.ile2$ps_bufaddr = &serv_addr;
+
+ status = sys$qiow (EFN$C_ENF, /* Event flag. */
+ listen_channel, /* I/O channel. */
+ IO$_SETMODE, /* I/O function code. */
+ &iosb, /* I/O status block. */
+ 0, /* Ast service routine. */
+ 0, /* Ast parameter. */
+ 0, /* P1. */
+ 0, /* P2. */
+ (__int64) &serv_itemlst, /* P3 - local socket name. */
+ 0, 0, 0);
+ if (status & STS$M_SUCCESS)
+ status = iosb.iosb$w_status;
+ if (!(status & STS$M_SUCCESS))
+ {
+ term_puts ("Failed to bind socket\n\r");
+ LIB$SIGNAL (status);
+ }
+
+ /* Set socket as a listen socket. */
+ status = sys$qiow (EFN$C_ENF, /* Event flag. */
+ listen_channel, /* I/O channel. */
+ IO$_SETMODE, /* I/O function code. */
+ &iosb, /* I/O status block. */
+ 0, /* Ast service routine. */
+ 0, /* Ast parameter. */
+ 0, /* P1. */
+ 0, /* P2. */
+ 0, /* P3. */
+ 1, /* P4 - connection backlog. */
+ 0, 0);
+ if (status & STS$M_SUCCESS)
+ status = iosb.iosb$w_status;
+ if (!(status & STS$M_SUCCESS))
+ {
+ term_puts ("Failed to set socket passive\n\r");
+ LIB$SIGNAL (status);
+ }
+
+ /* Accept connection from a client. */
+ term_puts ("Waiting for a client connection on port: ");
+ term_putdec (wordswap (serv_addr.sin_port));
+ term_putnl ();
+
+ status = sys$qiow (EFN$C_ENF, /* Event flag. */
+ listen_channel, /* I/O channel. */
+ IO$_ACCESS|IO$M_ACCEPT, /* I/O function code. */
+ &iosb, /* I/O status block. */
+ 0, /* Ast service routine. */
+ 0, /* Ast parameter. */
+ 0, /* P1. */
+ 0, /* P2. */
+ 0, /* P3. */
+ (__int64) &conn_channel, /* P4 - I/O channel for conn. */
+ 0, 0);
+
+ if (status & STS$M_SUCCESS)
+ status = iosb.iosb$w_status;
+ if (!(status & STS$M_SUCCESS))
+ {
+ term_puts ("Failed to accept client connection\n\r");
+ LIB$SIGNAL (status);
+ }
+
+ /* Log client connection request. */
+ cli_itemlst.ile3$w_length = sizeof (cli_addr);
+ cli_itemlst.ile3$w_code = TCPIP$C_SOCK_NAME;
+ cli_itemlst.ile3$ps_bufaddr = &cli_addr;
+ cli_itemlst.ile3$ps_retlen_addr = &cli_addrlen;
+ ots$fill (&cli_addr, sizeof(cli_addr), 0);
+ status = sys$qiow (EFN$C_ENF, /* Event flag. */
+ conn_channel, /* I/O channel. */
+ IO$_SENSEMODE, /* I/O function code. */
+ &iosb, /* I/O status block. */
+ 0, /* Ast service routine. */
+ 0, /* Ast parameter. */
+ 0, /* P1. */
+ 0, /* P2. */
+ 0, /* P3. */
+ (__int64) &cli_itemlst, /* P4 - peer socket name. */
+ 0, 0);
+ if (status & STS$M_SUCCESS)
+ status = iosb.iosb$w_status;
+ if (!(status & STS$M_SUCCESS))
+ {
+ term_puts ("Failed to get client name\n\r");
+ LIB$SIGNAL (status);
+ }
+
+ term_puts ("Accepted connection from host: ");
+ term_putdec ((cli_addr.sin_addr.s_addr >> 0) & 0xff);
+ term_putc ('.');
+ term_putdec ((cli_addr.sin_addr.s_addr >> 8) & 0xff);
+ term_putc ('.');
+ term_putdec ((cli_addr.sin_addr.s_addr >> 16) & 0xff);
+ term_putc ('.');
+ term_putdec ((cli_addr.sin_addr.s_addr >> 24) & 0xff);
+ term_puts (", port: ");
+ term_putdec (wordswap (cli_addr.sin_port));
+ term_putnl ();
+}
+
+/* Close the socket. */
+
+static void
+sock_close (void)
+{
+ struct _iosb iosb;
+ unsigned int status;
+
+ /* Close socket. */
+ status = sys$qiow (EFN$C_ENF, /* Event flag. */
+ conn_channel, /* I/O channel. */
+ IO$_DEACCESS, /* I/O function code. */
+ &iosb, /* I/O status block. */
+ 0, /* Ast service routine. */
+ 0, /* Ast parameter. */
+ 0, 0, 0, 0, 0, 0);
+
+ if (status & STS$M_SUCCESS)
+ status = iosb.iosb$w_status;
+ if (!(status & STS$M_SUCCESS))
+ {
+ term_puts ("Failed to close socket\n\r");
+ LIB$SIGNAL (status);
+ }
+
+ /* Deassign I/O channel to network device. */
+ status = sys$dassgn (conn_channel);
+
+ if (!(status & STS$M_SUCCESS))
+ {
+ term_puts ("Failed to deassign I/O channel\n\r");
+ LIB$SIGNAL (status);
+ }
+}
+
+/* Mark a page as R/W. Return old rights. */
+
+static unsigned int
+page_set_rw (unsigned __int64 startva, unsigned __int64 len,
+ unsigned int *oldprot)
+{
+ unsigned int status;
+ unsigned __int64 retva;
+ unsigned __int64 retlen;
+
+ status = SYS$SETPRT_64 ((void *)startva, len, PSL$C_USER, PRT$C_UW,
+ (void *)&retva, &retlen, oldprot);
+ return status;
+}
+
+/* Restore page rights. */
+
+static void
+page_restore_rw (unsigned __int64 startva, unsigned __int64 len,
+ unsigned int prot)
+{
+ unsigned int status;
+ unsigned __int64 retva;
+ unsigned __int64 retlen;
+ unsigned int oldprot;
+
+ status = SYS$SETPRT_64 ((void *)startva, len, PSL$C_USER, prot,
+ (void *)&retva, &retlen, &oldprot);
+ if (!(status & STS$M_SUCCESS))
+ LIB$SIGNAL (status);
+}
+
+/* Convert an hexadecimal character to a nibble. Return -1 in case of
+ error. */
+
+static int
+hex2nibble (unsigned char h)
+{
+ if (h >= '0' && h <= '9')
+ return h - '0';
+ if (h >= 'A' && h <= 'F')
+ return h - 'A' + 10;
+ if (h >= 'a' && h <= 'f')
+ return h - 'a' + 10;
+ return -1;
+}
+
+/* Convert an hexadecimal 2 character string to a byte. Return -1 in case
+ of error. */
+
+static int
+hex2byte (const unsigned char *p)
+{
+ int h, l;
+
+ h = hex2nibble (p[0]);
+ l = hex2nibble (p[1]);
+ if (h == -1 || l == -1)
+ return -1;
+ return (h << 4) | l;
+}
+
+/* Convert a byte V to a 2 character strings P. */
+
+static void
+byte2hex (unsigned char *p, unsigned char v)
+{
+ p[0] = hex[v >> 4];
+ p[1] = hex[v & 0xf];
+}
+
+/* Convert a quadword V to a 16 character strings P. */
+
+static void
+quad2hex (unsigned char *p, unsigned __int64 v)
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ {
+ p[i] = hex[v >> 60];
+ v <<= 4;
+ }
+}
+
+/* Generate an error packet. */
+
+static void
+packet_error (unsigned int err)
+{
+ gdb_buf[1] = 'E';
+ byte2hex (gdb_buf + 2, err);
+ gdb_blen = 4;
+}
+
+/* Generate an OK packet. */
+
+static void
+packet_ok (void)
+{
+ gdb_buf[1] = 'O';
+ gdb_buf[2] = 'K';
+ gdb_blen = 3;
+}
+
+/* Append a register to the packet. */
+
+static void
+ireg2pkt (const unsigned char *p)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ byte2hex (gdb_buf + gdb_blen, p[i]);
+ gdb_blen += 2;
+ }
+}
+
+/* Extract a number fro the packet. */
+
+static unsigned __int64
+pkt2val (const unsigned char *pkt, unsigned int *pos)
+{
+ unsigned __int64 res = 0;
+ unsigned int i;
+
+ while (1)
+ {
+ int r = hex2nibble (pkt[*pos]);
+
+ if (r < 0)
+ return res;
+ res = (res << 4) | r;
+ (*pos)++;
+ }
+}
+
+/* Append LEN bytes from B to the current gdb packet (encode in binary). */
+
+static void
+mem2bin (const unsigned char *b, unsigned int len)
+{
+ unsigned int i;
+ for (i = 0; i < len; i++)
+ switch (b[i])
+ {
+ case '#':
+ case '$':
+ case '}':
+ case '*':
+ case 0:
+ gdb_buf[gdb_blen++] = '}';
+ gdb_buf[gdb_blen++] = b[i] ^ 0x20;
+ break;
+ default:
+ gdb_buf[gdb_blen++] = b[i];
+ break;
+ }
+}
+
+/* Append LEN bytes from B to the current gdb packet (encode in hex). */
+
+static void
+mem2hex (const unsigned char *b, unsigned int len)
+{
+ unsigned int i;
+ for (i = 0; i < len; i++)
+ {
+ byte2hex (gdb_buf + gdb_blen, b[i]);
+ gdb_blen += 2;
+ }
+}
+
+/* Handle the 'q' packet. */
+
+static void
+handle_q_packet (const unsigned char *pkt, unsigned int pktlen)
+{
+ static const char xfer_uib[] = "qXfer:uib:read:";
+#define XFER_UIB_LEN (sizeof (xfer_uib) - 1)
+
+ if (pktlen > XFER_UIB_LEN
+ && ots$strcmp_eql (pkt, XFER_UIB_LEN, xfer_uib, XFER_UIB_LEN))
+ {
+ unsigned __int64 pc;
+ unsigned int pos = 24;
+ unsigned int off;
+ unsigned int len;
+ unsigned char blk[32];
+ int res;
+ int i;
+
+ packet_error (0);
+
+ pc = pkt2val (pkt, &pos);
+ if (pkt[pos] != ':')
+ return;
+ pos++;
+ off = pkt2val (pkt, &pos);
+ if (pkt[pos] != ',' || off != 0)
+ return;
+ pos++;
+ len = pkt2val (pkt, &pos);
+ if (pkt[pos] != '#' || len != 0x20)
+ return;
+
+ res = SYS$GET_UNWIND_ENTRY_INFO (pc, blk, 0);
+ if (res == SS$_NODATA || res != SS$_NORMAL)
+ ots$fill (blk, sizeof (blk), 0);
+
+#if 0
+ term_puts ("Got unwind request for ");
+ term_puthex8 (pc);
+ term_puts (", res=");
+ term_puthex8 (res);
+ term_putnl ();
+#endif
+
+ gdb_buf[0] = '$';
+ gdb_buf[1] = 'l';
+ gdb_blen = 2;
+ mem2bin (blk, sizeof (blk));
+ }
+ else
+ return;
+}
+
+/* Return 1 to continue. */
+
+static int
+handle_packet (const unsigned char *pkt, unsigned int len)
+{
+ unsigned int pos;
+
+ /* By default, reply unsupported. */
+ gdb_buf[0] = '$';
+ gdb_blen = 1;
+
+ pos = 1;
+ switch (pkt[0])
+ {
+ case '?':
+ if (len == 1)
+ {
+ gdb_buf[1] = 'S';
+ gdb_buf[2] = '0';
+ gdb_buf[3] = '5';
+ gdb_blen += 3;
+ return 0;
+ }
+ break;
+ case 'g':
+ if (len == 1)
+ {
+ unsigned int i;
+ unsigned char *p = regs.gr[0].b;
+
+ for (i = 0; i < 8 * 32; i++)
+ byte2hex (gdb_buf + 1 + 2 * i, p[i]);
+ gdb_blen += 2 * 8 * 32;
+ return 0;
+ }
+ break;
+ case 'p':
+ {
+ unsigned int num = 0;
+ unsigned int i;
+
+ num = pkt2val (pkt, &pos);
+ if (pos != len)
+ {
+ packet_error (0);
+ return 0;
+ }
+
+ switch (num)
+ {
+ case IA64_IP_REGNUM:
+ ireg2pkt (regs.ip.b);
+ break;
+ case IA64_BR0_REGNUM:
+ ireg2pkt (regs.br[0].b);
+ break;
+ case IA64_PSR_REGNUM:
+ ireg2pkt (regs.psr.b);
+ break;
+ case IA64_BSP_REGNUM:
+ ireg2pkt (regs.bsp.b);
+ break;
+ case IA64_CFM_REGNUM:
+ ireg2pkt (regs.cfm.b);
+ break;
+ case IA64_PFS_REGNUM:
+ ireg2pkt (regs.pfs.b);
+ break;
+ case IA64_PR_REGNUM:
+ ireg2pkt (regs.pr.b);
+ break;
+ default:
+ term_puts ("gdbserv: unhandled reg ");
+ term_putdec (num);
+ term_putnl ();
+ packet_error (0);
+ return 0;
+ }
+ }
+ break;
+ case 'q':
+ handle_q_packet (pkt, len);
+ break;
+ case 'k':
+ SYS$EXIT (SS$_NORMAL);
+ break;
+ case 'm':
+ {
+ unsigned __int64 addr;
+ unsigned int l;
+ unsigned int i;
+ addr = pkt2val (pkt, &pos);
+ if (pkt[pos] != ',')
+ {
+ packet_error (0);
+ return 0;
+ }
+ pos++;
+ l = pkt2val (pkt, &pos);
+ if (pkt[pos] != '#')
+ {
+ packet_error (0);
+ return 0;
+ }
+ for (i = 0; i < l; i++)
+ byte2hex (gdb_buf + 1 + 2 * i, ((unsigned char *)addr)[i]);
+ gdb_blen += 2 * l;
+ }
+ break;
+ case 'M':
+ {
+ unsigned __int64 addr;
+ unsigned int l;
+ unsigned int i;
+ unsigned int oldprot;
+
+ addr = pkt2val (pkt, &pos);
+ if (pkt[pos] != ',')
+ {
+ packet_error (0);
+ return 0;
+ }
+ pos++;
+ l = pkt2val (pkt, &pos);
+ if (pkt[pos] != ':')
+ {
+ packet_error (0);
+ return 0;
+ }
+ pos++;
+ page_set_rw (addr, l, &oldprot);
+ for (i = 0; i < l; i++)
+ {
+ int v = hex2byte (pkt + pos);
+ pos += 2;
+ ((unsigned char *)addr)[i] = v;
+ }
+ for (i = 0; i < l; i += 15)
+ __fc (addr + i);
+ __fc (addr + l);
+ page_restore_rw (addr, l, oldprot);
+ packet_ok ();
+ }
+ break;
+ case 'c':
+ if (len == 1)
+ {
+ /* Clear psr.ss. */
+ regs.psr.v &= ~(unsigned __int64)PSR$M_SS;
+ return 1;
+ }
+ else
+ packet_error (0);
+ break;
+ case 's':
+ if (len == 1)
+ {
+ /* Set psr.ss. */
+ regs.psr.v |= (unsigned __int64)PSR$M_SS;
+ return 1;
+ }
+ else
+ packet_error (0);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* Raw write to gdb. */
+
+static void
+sock_write (const unsigned char *buf, int len)
+{
+ struct _iosb iosb;
+ unsigned int status;
+
+ /* Write data to connection. */
+ status = sys$qiow (EFN$C_ENF, /* Event flag. */
+ conn_channel, /* I/O channel. */
+ IO$_WRITEVBLK, /* I/O function code. */
+ &iosb, /* I/O status block. */
+ 0, /* Ast service routine. */
+ 0, /* Ast parameter. */
+ (char *)buf, /* P1 - buffer address. */
+ len, /* P2 - buffer length. */
+ 0, 0, 0, 0);
+ if (status & STS$M_SUCCESS)
+ status = iosb.iosb$w_status;
+ if (!(status & STS$M_SUCCESS))
+ {
+ term_puts ("Failed to write data to gdb\n\r");
+ LIB$SIGNAL (status);
+ }
+}
+
+/* Compute the cheksum and send the packet. */
+
+static void
+send_pkt (void)
+{
+ unsigned char chksum = 0;
+ unsigned int i;
+
+ for (i = 1; i < gdb_blen; i++)
+ chksum += gdb_buf[i];
+
+ gdb_buf[gdb_blen] = '#';
+ byte2hex (gdb_buf + gdb_blen + 1, chksum);
+
+ sock_write (gdb_buf, gdb_blen + 3);
+
+ if (trace_pkt)
+ {
+ term_puts (">: ");
+ term_write ((char *)gdb_buf, gdb_blen + 3);
+ term_putnl ();
+ }
+}
+
+/* Read and handle one command. Return 1 is execution must resume. */
+
+static int
+one_command (void)
+{
+ struct _iosb iosb;
+ unsigned int status;
+ unsigned int off;
+ unsigned int dollar_off = 0;
+ unsigned int sharp_off = 0;
+ unsigned int cmd_off;
+ unsigned int cmd_len;
+
+ /* Wait for a packet. */
+ while (1)
+ {
+ off = 0;
+ while (1)
+ {
+ /* Read data from connection. */
+ status = sys$qiow (EFN$C_ENF, /* Event flag. */
+ conn_channel, /* I/O channel. */
+ IO$_READVBLK, /* I/O function code. */
+ &iosb, /* I/O status block. */
+ 0, /* Ast service routine. */
+ 0, /* Ast parameter. */
+ gdb_buf + off, /* P1 - buffer address. */
+ sizeof (gdb_buf) - off, /* P2 - buffer leng. */
+ 0, 0, 0, 0);
+ if (status & STS$M_SUCCESS)
+ status = iosb.iosb$w_status;
+ if (!(status & STS$M_SUCCESS))
+ {
+ term_puts ("Failed to read data from connection\n\r" );
+ LIB$SIGNAL (status);
+ }
+
+#ifdef RAW_DUMP
+ term_puts ("{: ");
+ term_write ((char *)gdb_buf + off, iosb.iosb$w_bcnt);
+ term_putnl ();
+#endif
+
+ gdb_blen = off + iosb.iosb$w_bcnt;
+
+ if (off == 0)
+ {
+ /* Search for '$'. */
+ for (dollar_off = 0; dollar_off < gdb_blen; dollar_off++)
+ if (gdb_buf[dollar_off] == '$')
+ break;
+ if (dollar_off >= gdb_blen)
+ {
+ /* Not found, discard the data. */
+ off = 0;
+ continue;
+ }
+ /* Search for '#'. */
+ for (sharp_off = dollar_off + 1; sharp_off < gdb_blen; sharp_off++)
+ if (gdb_buf[sharp_off] == '#')
+ break;
+ }
+ else if (sharp_off >= off)
+ {
+ /* Search for '#'. */
+ for (; sharp_off < gdb_blen; sharp_off++)
+ if (gdb_buf[sharp_off] == '#')
+ break;
+ }
+
+ /* Got packet with checksum. */
+ if (sharp_off + 2 <= gdb_blen)
+ break;
+
+ off = gdb_blen;
+ if (gdb_blen == sizeof (gdb_buf))
+ {
+ /* Packet too large, discard. */
+ off = 0;
+ }
+ }
+
+ /* Validate and acknowledge a packet. */
+ {
+ unsigned char chksum = 0;
+ unsigned int i;
+ int v;
+
+ for (i = dollar_off + 1; i < sharp_off; i++)
+ chksum += gdb_buf[i];
+ v = hex2byte (gdb_buf + sharp_off + 1);
+ if (v != chksum)
+ {
+ term_puts ("Discard bad checksum packet\n\r");
+ continue;
+ }
+ else
+ {
+ sock_write ((const unsigned char *)"+", 1);
+ break;
+ }
+ }
+ }
+
+ if (trace_pkt)
+ {
+ term_puts ("<: ");
+ term_write ((char *)gdb_buf + dollar_off, sharp_off - dollar_off + 1);
+ term_putnl ();
+ }
+
+ cmd_off = dollar_off + 1;
+ cmd_len = sharp_off - dollar_off - 1;
+
+ if (handle_packet (gdb_buf + dollar_off + 1, sharp_off - dollar_off - 1) == 1)
+ return 1;
+
+ send_pkt ();
+ return 0;
+}
+
+/* Display the condition given by SIG64. */
+
+static void
+display_excp (struct chf64$signal_array *sig64)
+{
+ unsigned int status;
+ char msg[160];
+ unsigned short msglen;
+ $DESCRIPTOR (msg_desc, msg);
+ unsigned char outadr[4];
+
+ status = SYS$GETMSG (sig64->chf64$q_sig_name, &msglen, &msg_desc, 0, outadr);
+ if (status & STS$M_SUCCESS)
+ {
+ char msg2[160];
+ unsigned short msg2len;
+ struct dsc$descriptor_s msg2_desc =
+ { sizeof (msg2), DSC$K_DTYPE_T, DSC$K_CLASS_S, msg2};
+ msg_desc.dsc$w_length = msglen;
+ status = SYS$FAOL_64 (&msg_desc, &msg2len, &msg2_desc,
+ &sig64->chf64$q_sig_arg1);
+ if (status & STS$M_SUCCESS)
+ term_write (msg2, msg2len);
+ }
+ else
+ term_puts ("no message");
+ term_putnl ();
+}
+
+/* The condition handler. That's the core of the stub. */
+
+static int
+excp_handler (struct chf$signal_array *sig,
+ struct chf$mech_array *mech)
+{
+ unsigned int status;
+ struct chf64$signal_array *sig64 =
+ (struct chf64$signal_array *)mech->chf$ph_mch_sig64_addr;
+ struct _intstk *intstk =
+ (struct _intstk *)mech->chf$q_mch_esf_addr;
+
+ unsigned int code = sig->chf$l_sig_name & STS$M_COND_ID;
+ unsigned int cnt = sig64->chf64$w_sig_arg_count;
+ unsigned __int64 pc = (&sig64->chf64$q_sig_name)[cnt - 2];
+ int i;
+ int ret = SS$_RESIGNAL_64;
+
+ /* Completly ignore some conditions (signaled indirectly by this stub). */
+ switch (code)
+ {
+ case LIB$_KEYNOTFOU & STS$M_COND_ID:
+ return SS$_RESIGNAL_64;
+ default:
+ break;
+ }
+
+ if (trace_excp)
+ {
+ term_puts ("excp_handler: ");
+ term_puthex8 (code);
+ term_puts (", vec count: ");
+ term_putdec (cnt);
+ term_puts (", pc=");
+ term_puthex8 (pc);
+ term_putnl ();
+ }
+
+ /* If break on the entry point, restore the bundle. */
+ if (code == (SS$_BREAK & STS$M_COND_ID)
+ && pc == entry_pc
+ && entry_pc != 0)
+ {
+ static unsigned int entry_prot;
+
+ if (trace_entry)
+ term_puts ("initial entry breakpoint\n\r");
+ page_set_rw (entry_pc, 16, &entry_prot);
+
+ ots$move3 (16, entry_saved, (void *)entry_pc);
+ __fc (entry_pc);
+ page_restore_rw (entry_pc, 16, entry_prot);
+
+ ret = SS$_CONTINUE_64;
+ }
+
+ switch (code)
+ {
+ case SS$_ACCVIO & STS$M_COND_ID:
+ if (trace_excp <= 1)
+ display_excp (sig64);
+ /* Fall through. */
+ case SS$_BREAK & STS$M_COND_ID:
+ case SS$_OPCDEC & STS$M_COND_ID:
+ case SS$_TBIT & STS$M_COND_ID:
+ if (trace_excp > 1)
+ {
+ display_excp (sig64);
+
+ term_puts (" intstk: ");
+ term_putp (intstk);
+ term_putnl ();
+ for (i = 0; i < cnt + 1; i++)
+ {
+ term_puts (" ");
+ term_puthex8 (((unsigned __int64 *)sig64)[i]);
+ term_putnl ();
+ }
+ }
+ regs.ip.v = pc;
+ regs.psr.v = intstk->intstk$q_ipsr;
+#if 1
+ /* What a mess. Gdb and linux expects bsp to point after the current
+ register frame. Adjust. */
+ {
+ unsigned __int64 bsp = intstk->intstk$q_bsp;
+ unsigned int sof = intstk->intstk$q_ifs & 0x7f;
+ unsigned int delta = ((bsp >> 3) & 0x3f) + sof;
+ regs.bsp.v = bsp + ((sof + delta / 0x3f) << 3);
+ }
+#else
+ regs.bsp.v = intstk->intstk$q_bsp;
+#endif
+ regs.cfm.v = intstk->intstk$q_ifs & 0x3fffffffff;
+ regs.pfs.v = intstk->intstk$q_pfs;
+ regs.pr.v = intstk->intstk$q_preds;
+ regs.gr[0].v = 0;
+ regs.gr[1].v = intstk->intstk$q_gp;
+ regs.gr[2].v = intstk->intstk$q_r2;
+ regs.gr[3].v = intstk->intstk$q_r3;
+ regs.gr[4].v = intstk->intstk$q_r4;
+ regs.gr[5].v = intstk->intstk$q_r5;
+ regs.gr[6].v = intstk->intstk$q_r6;
+ regs.gr[7].v = intstk->intstk$q_r7;
+ regs.gr[8].v = intstk->intstk$q_r8;
+ regs.gr[9].v = intstk->intstk$q_r9;
+ regs.gr[10].v = intstk->intstk$q_r10;
+ regs.gr[11].v = intstk->intstk$q_r11;
+ regs.gr[12].v = (unsigned __int64)intstk + intstk->intstk$l_stkalign;
+ regs.gr[13].v = intstk->intstk$q_r13;
+ regs.gr[14].v = intstk->intstk$q_r14;
+ regs.gr[15].v = intstk->intstk$q_r15;
+ regs.gr[16].v = intstk->intstk$q_r16;
+ regs.gr[17].v = intstk->intstk$q_r17;
+ regs.gr[18].v = intstk->intstk$q_r18;
+ regs.gr[19].v = intstk->intstk$q_r19;
+ regs.gr[20].v = intstk->intstk$q_r20;
+ regs.gr[21].v = intstk->intstk$q_r21;
+ regs.gr[22].v = intstk->intstk$q_r22;
+ regs.gr[23].v = intstk->intstk$q_r23;
+ regs.gr[24].v = intstk->intstk$q_r24;
+ regs.gr[25].v = intstk->intstk$q_r25;
+ regs.gr[26].v = intstk->intstk$q_r26;
+ regs.gr[27].v = intstk->intstk$q_r27;
+ regs.gr[28].v = intstk->intstk$q_r28;
+ regs.gr[29].v = intstk->intstk$q_r29;
+ regs.gr[30].v = intstk->intstk$q_r30;
+ regs.gr[31].v = intstk->intstk$q_r31;
+ regs.br[0].v = intstk->intstk$q_b0;
+ regs.br[1].v = intstk->intstk$q_b1;
+ regs.br[2].v = intstk->intstk$q_b2;
+ regs.br[3].v = intstk->intstk$q_b3;
+ regs.br[4].v = intstk->intstk$q_b4;
+ regs.br[5].v = intstk->intstk$q_b5;
+ regs.br[6].v = intstk->intstk$q_b6;
+ regs.br[7].v = intstk->intstk$q_b7;
+
+ /* Send stop reply packet. */
+ {
+ gdb_buf[0] = '$';
+ gdb_buf[1] = 'S';
+ gdb_buf[2] = '0';
+ gdb_buf[3] = '5';
+ gdb_blen = 4;
+ send_pkt ();
+ }
+ while (one_command () == 0)
+ ;
+ intstk->intstk$q_ipsr = regs.psr.v;
+ ret = SS$_CONTINUE_64;
+ break;
+
+ default:
+ display_excp (sig64);
+ break;
+ }
+
+ return ret;
+}
+
+/* Setup internal trace flags according to GDBSTUB$TRACE logical. */
+
+static void
+trace_init (void)
+{
+ unsigned int status, i, start;
+ unsigned short len;
+ char resstring[LNM$C_NAMLENGTH];
+ static const $DESCRIPTOR (tabdesc, "LNM$DCL_LOGICAL");
+ static const $DESCRIPTOR (logdesc, "GDBSTUB$TRACE");
+ static const $DESCRIPTOR (pkt_desc, "packets");
+ static const $DESCRIPTOR (entry_desc, "entry");
+ static const $DESCRIPTOR (except_desc, "except");
+ $DESCRIPTOR (sub_desc, resstring);
+ ILE3 item_lst[2];
+
+ item_lst[0].ile3$w_length = LNM$C_NAMLENGTH;
+ item_lst[0].ile3$w_code = LNM$_STRING;
+ item_lst[0].ile3$ps_bufaddr = resstring;
+ item_lst[0].ile3$ps_retlen_addr = &len;
+ item_lst[1].ile3$w_length = 0;
+ item_lst[1].ile3$w_code = 0;
+
+ /* Translate the logical name. */
+ status = SYS$TRNLNM (0, /* Attributes of the logical name. */
+ (void *)&tabdesc, /* Logical name table. */
+ (void *)&logdesc, /* Logical name. */
+ 0, /* Access mode. */
+ &item_lst); /* Item list. */
+ if (status == SS$_NOLOGNAM)
+ return;
+ if (!(status & STS$M_SUCCESS))
+ LIB$SIGNAL (status);
+
+ start = 0;
+ for (i = 0; i <= len; i++)
+ {
+ if ((i == len || resstring[i] == ',') && i != start)
+ {
+ sub_desc.dsc$a_pointer = resstring + start;
+ sub_desc.dsc$w_length = i - start;
+
+ if (str$case_blind_compare (&sub_desc, (void *)&pkt_desc) == 0)
+ trace_pkt++;
+ else if (str$case_blind_compare (&sub_desc, (void *)&entry_desc) == 0)
+ trace_entry++;
+ else if (str$case_blind_compare (&sub_desc, (void *)&except_desc) == 0)
+ trace_excp++;
+ else
+ {
+ term_puts ("GDBSTUB$TRACE: unknown directive ");
+ term_write (sub_desc.dsc$a_pointer, sub_desc.dsc$w_length);
+ term_putnl ();
+ }
+
+ start = i + 1;
+ }
+ }
+}
+
+/* Entry point. */
+
+static void
+stub_start (unsigned __int64 *progxfer, void *cli_util,
+ void *imghdr, void *imgfile,
+ unsigned int linkflag, unsigned int cliflag)
+{
+ int i;
+
+ term_init ();
+ trace_init ();
+
+ /* Hello banner. */
+ term_puts ("Hello gdb stub\n\r");
+ if (trace_entry)
+ {
+ term_puts ("xfer: ");
+ term_putp (progxfer);
+ term_putnl ();
+ for (i = -2; i < 8; i++)
+ {
+ term_puthex8 (progxfer[i]);
+ term_putnl ();
+ }
+ }
+
+ /* Search for entry point. */
+ entry_pc = 0;
+ for (i = 0; progxfer[i]; i++)
+ entry_pc = progxfer[i];
+
+ if (trace_entry)
+ {
+ if (entry_pc == 0)
+ {
+ term_puts ("No entry point\n\r");
+ return;
+ }
+ else
+ {
+ term_puts ("Entry: ");
+ term_puthex8 (entry_pc);
+ term_putnl ();
+ }
+ }
+
+ /* Set primary exception vector. */
+ {
+ unsigned int status;
+ status = sys$setexv (0, excp_handler, PSL$C_USER, &prevhnd);
+ if (!(status & STS$M_SUCCESS))
+ LIB$SIGNAL (status);
+ }
+
+ /* Change first instruction to set a breakpoint. */
+ {
+ /*
+ 01 08 00 40 00 00 [MII] break.m 0x80001
+ 00 00 00 02 00 00 nop.i 0x0
+ 00 00 04 00 nop.i 0x0;;
+ */
+ static const unsigned char initbp[16] =
+ { 0x01, 0x08, 0x00, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x00 };
+ unsigned int entry_prot;
+ unsigned int status;
+
+ status = page_set_rw (entry_pc, 16, &entry_prot);
+#if 0
+ term_puthex8 (status);
+ term_putnl ();
+ term_puthex8 ((status & STS$M_COND_ID));
+ term_puthex8 (SS$_NOT_PROCESS_VA);
+ term_putnl ();
+#endif
+
+ if (!(status & STS$M_SUCCESS))
+ {
+ if ((status & STS$M_COND_ID) == (SS$_NOT_PROCESS_VA & STS$M_COND_ID))
+ {
+ /* Cannot write here. This can happen when pthreads are used. */
+ entry_pc = 0;
+ term_puts ("gdbstub: cannot set breakpoint on entry\n\r");
+ }
+ else
+ LIB$SIGNAL (status);
+ }
+
+ if (entry_pc != 0)
+ {
+ ots$move3 (16, (void *)entry_pc, entry_saved);
+ ots$move3 (16, (void *)initbp, (void *)entry_pc);
+ __fc (entry_pc);
+ page_restore_rw (entry_pc, 16, entry_prot);
+ }
+ }
+
+#if 0
+ term_puts ("sizeof regs: ");
+ term_putdec (sizeof (regs));
+ term_putnl ();
+#endif
+
+ sock_init ();
+
+ /* If it wasn't possible to set a breakpoint on the entry point,
+ accept gdb commands now. Note that registers are not updated. */
+ if (entry_pc == 0)
+ {
+ while (one_command () == 0)
+ ;
+ }
+
+ /* We will see! */
+ return;
+}
+
+/* Declare the entry point of this relocatable module. */
+
+static void stub_start ();
+
+struct xfer_vector
+{
+ __int64 impure_start;
+ __int64 impure_end;
+ void (*entry) ();
+};
+
+#pragma __extern_model save
+#pragma __extern_model strict_refdef "XFER_PSECT"
+struct xfer_vector xfer_vector = {0, 0, stub_start};
+#pragma __extern_model restore