|  | /* GDB stub for Itanium OpenVMS | 
|  | Copyright (C) 2012-2025 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. | 
|  | */ | 
|  |  | 
|  | #if __INITIAL_POINTER_SIZE != 64 | 
|  | #error "Must be compiled with 64 bit pointers" | 
|  | #endif | 
|  |  | 
|  | #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 <chfdef.h> | 
|  |  | 
|  | #include <lib_c/imcbdef.h> | 
|  | #include <lib_c/ldrimgdef.h> | 
|  | #include <lib_c/intstkdef.h> | 
|  | #include <lib_c/psrdef.h> | 
|  | #include <lib_c/ifddef.h> | 
|  | #include <lib_c/eihddef.h> | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include <pthread_debug.h> | 
|  |  | 
|  | #define VMS_PAGE_SIZE 0x2000 | 
|  | #define VMS_PAGE_MASK (VMS_PAGE_SIZE - 1) | 
|  |  | 
|  | /* Declared in lib$ots.  */ | 
|  | extern void ots$fill (void *addr, size_t len, unsigned char b); | 
|  | extern void ots$move (void *dst, size_t len, const void *src); | 
|  | 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; | 
|  |  | 
|  | /* DBGEXT structure.  Not declared in any header.  */ | 
|  | struct dbgext_control_block | 
|  | { | 
|  | unsigned short dbgext$w_function_code; | 
|  | #define DBGEXT$K_NEXT_TASK	      3 | 
|  | #define DBGEXT$K_STOP_ALL_OTHER_TASKS 31 | 
|  | #define DBGEXT$K_GET_REGS 33 | 
|  | unsigned short dbgext$w_facility_id; | 
|  | #define CMA$_FACILITY 64 | 
|  | unsigned int dbgext$l_status; | 
|  | unsigned int dbgext$l_flags; | 
|  | unsigned int dbgext$l_print_routine; | 
|  | unsigned int dbgext$l_evnt_code; | 
|  | unsigned int dbgext$l_evnt_name; | 
|  | unsigned int dbgext$l_evnt_entry; | 
|  | unsigned int dbgext$l_task_value; | 
|  | unsigned int dbgext$l_task_number; | 
|  | unsigned int dbgext$l_ada_flags; | 
|  | unsigned int dbgext$l_stop_value; | 
|  | #define dbgext$l_priority   dbgext$l_stop_value; | 
|  | #define dbgext$l_symb_addr  dbgext$l_stop_value; | 
|  | #define dbgext$l_time_slice dbgext$l_stop_value; | 
|  | unsigned int dbgext$l_active_registers; | 
|  | }; | 
|  |  | 
|  | #pragma pointer_size save | 
|  | #pragma pointer_size 32 | 
|  |  | 
|  | /* Pthread handler.  */ | 
|  | static int (*dbgext_func) (struct dbgext_control_block *blk); | 
|  |  | 
|  | #pragma pointer_size restore | 
|  |  | 
|  | /* Set to 1 if thread-aware.  */ | 
|  | static int has_threads; | 
|  |  | 
|  | /* Current thread.  */ | 
|  | static pthread_t selected_thread; | 
|  | static pthreadDebugId_t selected_id; | 
|  |  | 
|  | /* Internal debugging flags.  */ | 
|  | struct debug_flag | 
|  | { | 
|  | /* Name of the flag (as a string descriptor).  */ | 
|  | const struct dsc$descriptor_s name; | 
|  | /* Value.  */ | 
|  | int val; | 
|  | }; | 
|  |  | 
|  | /* Macro to define a debugging flag.  */ | 
|  | #define DEBUG_FLAG_ENTRY(str) \ | 
|  | { { sizeof (str) - 1, DSC$K_DTYPE_T, DSC$K_CLASS_S, str }, 0} | 
|  |  | 
|  | static struct debug_flag debug_flags[] = | 
|  | { | 
|  | /* Disp packets exchanged with gdb.  */ | 
|  | DEBUG_FLAG_ENTRY("packets"), | 
|  | #define trace_pkt (debug_flags[0].val) | 
|  | /* Display entry point informations.  */ | 
|  | DEBUG_FLAG_ENTRY("entry"), | 
|  | #define trace_entry (debug_flags[1].val) | 
|  | /* Be verbose about exceptions.  */ | 
|  | DEBUG_FLAG_ENTRY("excp"), | 
|  | #define trace_excp (debug_flags[2].val) | 
|  | /* Be verbose about unwinding.  */ | 
|  | DEBUG_FLAG_ENTRY("unwind"), | 
|  | #define trace_unwind (debug_flags[3].val) | 
|  | /* Display image at startup.  */ | 
|  | DEBUG_FLAG_ENTRY("images"), | 
|  | #define trace_images (debug_flags[4].val) | 
|  | /* Display pthread_debug info.  */ | 
|  | DEBUG_FLAG_ENTRY("pthreaddbg") | 
|  | #define trace_pthreaddbg (debug_flags[5].val) | 
|  | }; | 
|  |  | 
|  | #define NBR_DEBUG_FLAGS (sizeof (debug_flags) / sizeof (debug_flags[0])) | 
|  |  | 
|  | /* Connect inet device I/O channel.  */ | 
|  | static unsigned short conn_channel; | 
|  |  | 
|  | /* Widely used hex digit to ascii.  */ | 
|  | static const char hex[] = "0123456789abcdef"; | 
|  |  | 
|  | /* Socket characteristics.  Apparently, there are no declaration for it in | 
|  | standard headers.  */ | 
|  | struct sockchar | 
|  | { | 
|  | unsigned short prot; | 
|  | unsigned char type; | 
|  | unsigned char af; | 
|  | }; | 
|  |  | 
|  | /* Chain of images loaded.  */ | 
|  | extern IMCB* ctl$gl_imglstptr; | 
|  |  | 
|  | /* 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 through 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 excp_regs; | 
|  | static struct ia64_all_regs sel_regs; | 
|  | static pthread_t sel_regs_pthread; | 
|  |  | 
|  | /* 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 (struct ia64_all_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 the 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) | 
|  | { | 
|  | if (c < 32) | 
|  | switch (c) | 
|  | { | 
|  | case '\r': | 
|  | case '\n': | 
|  | break; | 
|  | default: | 
|  | c = '.'; | 
|  | break; | 
|  | } | 
|  | 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++); | 
|  | } | 
|  |  | 
|  | /* Write using FAO formatting.  */ | 
|  |  | 
|  | static void | 
|  | term_fao (const char *str, unsigned int str_len, ...) | 
|  | { | 
|  | int cnt; | 
|  | va_list vargs; | 
|  | int i; | 
|  | __int64 *args; | 
|  | int status; | 
|  | struct dsc$descriptor_s dstr = | 
|  | { str_len, DSC$K_DTYPE_T, DSC$K_CLASS_S, (__char_ptr32)str }; | 
|  | char buf[128]; | 
|  | $DESCRIPTOR (buf_desc, buf); | 
|  |  | 
|  | va_start (vargs, str_len); | 
|  | va_count (cnt); | 
|  | args = (__int64 *) __ALLOCA (cnt * sizeof (__int64)); | 
|  | cnt -= 2; | 
|  | for (i = 0; i < cnt; i++) | 
|  | args[i] = va_arg (vargs, __int64); | 
|  |  | 
|  | status = sys$faol_64 (&dstr, &buf_desc.dsc$w_length, &buf_desc, args); | 
|  | if (status & 1) | 
|  | { | 
|  | /* FAO !/ already insert a line feed.  */ | 
|  | for (i = 0; i < buf_desc.dsc$w_length; i++) | 
|  | { | 
|  | term_raw_putchar (buf[i]); | 
|  | if (buf[i] == '\n') | 
|  | term_flush (); | 
|  | } | 
|  | } | 
|  |  | 
|  | va_end (vargs); | 
|  | } | 
|  |  | 
|  | #define TERM_FAO(STR, ...) term_fao (STR, sizeof (STR) - 1, __VA_ARGS__) | 
|  |  | 
|  | /* 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"); | 
|  | 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"); | 
|  | 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"); | 
|  | 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"); | 
|  | 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"); | 
|  | LIB$SIGNAL (status); | 
|  | } | 
|  |  | 
|  | /* Accept connection from a client.  */ | 
|  | TERM_FAO ("Waiting for a client connection on port: !ZW!/", | 
|  | wordswap (serv_addr.sin_port)); | 
|  |  | 
|  | 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"); | 
|  | 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"); | 
|  | LIB$SIGNAL (status); | 
|  | } | 
|  |  | 
|  | TERM_FAO ("Accepted connection from host: !UB.!UB,!UB.!UB, port: !UW!/", | 
|  | (cli_addr.sin_addr.s_addr >> 0) & 0xff, | 
|  | (cli_addr.sin_addr.s_addr >> 8) & 0xff, | 
|  | (cli_addr.sin_addr.s_addr >> 16) & 0xff, | 
|  | (cli_addr.sin_addr.s_addr >> 24) & 0xff, | 
|  | wordswap (cli_addr.sin_port)); | 
|  | } | 
|  |  | 
|  | /* 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"); | 
|  | 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"); | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* Get the TEB (thread environment block).  */ | 
|  |  | 
|  | static pthread_t | 
|  | get_teb (void) | 
|  | { | 
|  | return (pthread_t)__getReg (_IA64_REG_TP); | 
|  | } | 
|  |  | 
|  | /* Enable thread scheduling if VAL is true.  */ | 
|  |  | 
|  | static unsigned int | 
|  | set_thread_scheduling (int val) | 
|  | { | 
|  | struct dbgext_control_block blk; | 
|  | unsigned int status; | 
|  |  | 
|  | if (!dbgext_func) | 
|  | return 0; | 
|  |  | 
|  | blk.dbgext$w_function_code = DBGEXT$K_STOP_ALL_OTHER_TASKS; | 
|  | blk.dbgext$w_facility_id = CMA$_FACILITY; | 
|  | blk.dbgext$l_stop_value = val; | 
|  |  | 
|  | status = dbgext_func (&blk); | 
|  | if (!(status & STS$M_SUCCESS)) | 
|  | { | 
|  | TERM_FAO ("set_thread_scheduling error, val=!SL, status=!XL!/", | 
|  | val, blk.dbgext$l_status); | 
|  | lib$signal (status); | 
|  | } | 
|  |  | 
|  | return blk.dbgext$l_stop_value; | 
|  | } | 
|  |  | 
|  | /* Get next thread (after THR).  Start with 0.  */ | 
|  |  | 
|  | static unsigned int | 
|  | thread_next (unsigned int thr) | 
|  | { | 
|  | struct dbgext_control_block blk; | 
|  | unsigned int status; | 
|  |  | 
|  | if (!dbgext_func) | 
|  | return 0; | 
|  |  | 
|  | blk.dbgext$w_function_code = DBGEXT$K_NEXT_TASK; | 
|  | blk.dbgext$w_facility_id = CMA$_FACILITY; | 
|  | blk.dbgext$l_ada_flags = 0; | 
|  | blk.dbgext$l_task_value = thr; | 
|  |  | 
|  | status = dbgext_func (&blk); | 
|  | if (!(status & STS$M_SUCCESS)) | 
|  | lib$signal (status); | 
|  |  | 
|  | return blk.dbgext$l_task_value; | 
|  | } | 
|  |  | 
|  | /* Pthread Debug callbacks.  */ | 
|  |  | 
|  | static int | 
|  | read_callback (pthreadDebugClient_t context, | 
|  | pthreadDebugTargetAddr_t addr, | 
|  | pthreadDebugAddr_t buf, | 
|  | size_t size) | 
|  | { | 
|  | if (trace_pthreaddbg) | 
|  | TERM_FAO ("read_callback (!XH, !XH, !SL)!/", addr, buf, size); | 
|  | ots$move (buf, size, addr); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | write_callback (pthreadDebugClient_t context, | 
|  | pthreadDebugTargetAddr_t addr, | 
|  | pthreadDebugLongConstAddr_t buf, | 
|  | size_t size) | 
|  | { | 
|  | if (trace_pthreaddbg) | 
|  | TERM_FAO ("write_callback (!XH, !XH, !SL)!/", addr, buf, size); | 
|  | ots$move (addr, size, buf); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | suspend_callback (pthreadDebugClient_t context) | 
|  | { | 
|  | /* Always suspended.  */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | resume_callback (pthreadDebugClient_t context) | 
|  | { | 
|  | /* So no need to resume.  */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | kthdinfo_callback (pthreadDebugClient_t context, | 
|  | pthreadDebugKId_t kid, | 
|  | pthreadDebugKThreadInfo_p thread_info) | 
|  | { | 
|  | if (trace_pthreaddbg) | 
|  | term_puts ("kthinfo_callback"); | 
|  | return ENOSYS; | 
|  | } | 
|  |  | 
|  | static int | 
|  | hold_callback (pthreadDebugClient_t context, | 
|  | pthreadDebugKId_t kid) | 
|  | { | 
|  | if (trace_pthreaddbg) | 
|  | term_puts ("hold_callback"); | 
|  | return ENOSYS; | 
|  | } | 
|  |  | 
|  | static int | 
|  | unhold_callback (pthreadDebugClient_t context, | 
|  | pthreadDebugKId_t kid) | 
|  | { | 
|  | if (trace_pthreaddbg) | 
|  | term_puts ("unhold_callback"); | 
|  | return ENOSYS; | 
|  | } | 
|  |  | 
|  | static int | 
|  | getfreg_callback (pthreadDebugClient_t context, | 
|  | pthreadDebugFregs_t *reg, | 
|  | pthreadDebugKId_t kid) | 
|  | { | 
|  | if (trace_pthreaddbg) | 
|  | term_puts ("getfreg_callback"); | 
|  | return ENOSYS; | 
|  | } | 
|  |  | 
|  | static int | 
|  | setfreg_callback (pthreadDebugClient_t context, | 
|  | const pthreadDebugFregs_t *reg, | 
|  | pthreadDebugKId_t kid) | 
|  | { | 
|  | if (trace_pthreaddbg) | 
|  | term_puts ("setfreg_callback"); | 
|  | return ENOSYS; | 
|  | } | 
|  |  | 
|  | static int | 
|  | getreg_callback (pthreadDebugClient_t context, | 
|  | pthreadDebugRegs_t *reg, | 
|  | pthreadDebugKId_t kid) | 
|  | { | 
|  | if (trace_pthreaddbg) | 
|  | term_puts ("getreg_callback"); | 
|  | return ENOSYS; | 
|  | } | 
|  |  | 
|  | static int | 
|  | setreg_callback (pthreadDebugClient_t context, | 
|  | const pthreadDebugRegs_t *reg, | 
|  | pthreadDebugKId_t kid) | 
|  | { | 
|  | if (trace_pthreaddbg) | 
|  | term_puts ("setreg_callback"); | 
|  | return ENOSYS; | 
|  | } | 
|  |  | 
|  | static int | 
|  | output_callback (pthreadDebugClient_t context, | 
|  | pthreadDebugConstString_t line) | 
|  | { | 
|  | term_puts (line); | 
|  | term_putnl (); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | error_callback (pthreadDebugClient_t context, | 
|  | pthreadDebugConstString_t line) | 
|  | { | 
|  | term_puts (line); | 
|  | term_putnl (); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static pthreadDebugAddr_t | 
|  | malloc_callback (pthreadDebugClient_t caller_context, size_t size) | 
|  | { | 
|  | unsigned int status; | 
|  | unsigned int res; | 
|  | int len; | 
|  |  | 
|  | len = size + 16; | 
|  | status = lib$get_vm (&len, &res, 0); | 
|  | if (!(status & STS$M_SUCCESS)) | 
|  | LIB$SIGNAL (status); | 
|  | if (trace_pthreaddbg) | 
|  | TERM_FAO ("malloc_callback (!UL) -> !XA!/", size, res); | 
|  | *(unsigned int *)res = len; | 
|  | return (char *)res + 16; | 
|  | } | 
|  |  | 
|  | static void | 
|  | free_callback (pthreadDebugClient_t caller_context, pthreadDebugAddr_t address) | 
|  | { | 
|  | unsigned int status; | 
|  | unsigned int res; | 
|  | int len; | 
|  |  | 
|  | res = (unsigned int)address - 16; | 
|  | len = *(unsigned int *)res; | 
|  | if (trace_pthreaddbg) | 
|  | TERM_FAO ("free_callback (!XA)!/", address); | 
|  | status = lib$free_vm (&len, &res, 0); | 
|  | if (!(status & STS$M_SUCCESS)) | 
|  | LIB$SIGNAL (status); | 
|  | } | 
|  |  | 
|  | static int | 
|  | speckthd_callback (pthreadDebugClient_t caller_context, | 
|  | pthreadDebugSpecialType_t type, | 
|  | pthreadDebugKId_t *kernel_tid) | 
|  | { | 
|  | return ENOTSUP; | 
|  | } | 
|  |  | 
|  | static pthreadDebugCallbacks_t pthread_debug_callbacks = { | 
|  | PTHREAD_DEBUG_VERSION, | 
|  | read_callback, | 
|  | write_callback, | 
|  | suspend_callback, | 
|  | resume_callback, | 
|  | kthdinfo_callback, | 
|  | hold_callback, | 
|  | unhold_callback, | 
|  | getfreg_callback, | 
|  | setfreg_callback, | 
|  | getreg_callback, | 
|  | setreg_callback, | 
|  | output_callback, | 
|  | error_callback, | 
|  | malloc_callback, | 
|  | free_callback, | 
|  | speckthd_callback | 
|  | }; | 
|  |  | 
|  | /* Name of the pthread shared library.  */ | 
|  | static const $DESCRIPTOR (pthread_rtl_desc, "PTHREAD$RTL"); | 
|  |  | 
|  | /* List of symbols to extract from pthread debug library.  */ | 
|  | struct pthread_debug_entry | 
|  | { | 
|  | const unsigned int namelen; | 
|  | const __char_ptr32 name; | 
|  | __void_ptr32 func; | 
|  | }; | 
|  |  | 
|  | #define DEBUG_ENTRY(str) { sizeof(str) - 1, str, 0 } | 
|  |  | 
|  | static struct pthread_debug_entry pthread_debug_entries[] = { | 
|  | DEBUG_ENTRY("pthreadDebugContextInit"), | 
|  | DEBUG_ENTRY("pthreadDebugThdSeqInit"), | 
|  | DEBUG_ENTRY("pthreadDebugThdSeqNext"), | 
|  | DEBUG_ENTRY("pthreadDebugThdSeqDestroy"), | 
|  | DEBUG_ENTRY("pthreadDebugThdGetInfo"), | 
|  | DEBUG_ENTRY("pthreadDebugThdGetInfoAddr"), | 
|  | DEBUG_ENTRY("pthreadDebugThdGetReg"), | 
|  | DEBUG_ENTRY("pthreadDebugCmd") | 
|  | }; | 
|  |  | 
|  | /* Pthread debug context.  */ | 
|  | static pthreadDebugContext_t debug_context; | 
|  |  | 
|  | /* Wrapper around pthread debug entry points.  */ | 
|  |  | 
|  | static int | 
|  | pthread_debug_thd_seq_init (pthreadDebugId_t *id) | 
|  | { | 
|  | return ((int (*)())pthread_debug_entries[1].func) | 
|  | (debug_context, id); | 
|  | } | 
|  |  | 
|  | static int | 
|  | pthread_debug_thd_seq_next (pthreadDebugId_t *id) | 
|  | { | 
|  | return ((int (*)())pthread_debug_entries[2].func) | 
|  | (debug_context, id); | 
|  | } | 
|  |  | 
|  | static int | 
|  | pthread_debug_thd_seq_destroy (void) | 
|  | { | 
|  | return ((int (*)())pthread_debug_entries[3].func) | 
|  | (debug_context); | 
|  | } | 
|  |  | 
|  | static int | 
|  | pthread_debug_thd_get_info (pthreadDebugId_t id, | 
|  | pthreadDebugThreadInfo_t *info) | 
|  | { | 
|  | return ((int (*)())pthread_debug_entries[4].func) | 
|  | (debug_context, id, info); | 
|  | } | 
|  |  | 
|  | static int | 
|  | pthread_debug_thd_get_info_addr (pthread_t thr, | 
|  | pthreadDebugThreadInfo_t *info) | 
|  | { | 
|  | return ((int (*)())pthread_debug_entries[5].func) | 
|  | (debug_context, thr, info); | 
|  | } | 
|  |  | 
|  | static int | 
|  | pthread_debug_thd_get_reg (pthreadDebugId_t thr, | 
|  | pthreadDebugRegs_t *regs) | 
|  | { | 
|  | return ((int (*)())pthread_debug_entries[6].func) | 
|  | (debug_context, thr, regs); | 
|  | } | 
|  |  | 
|  | static int | 
|  | stub_pthread_debug_cmd (const char *cmd) | 
|  | { | 
|  | return ((int (*)())pthread_debug_entries[7].func) | 
|  | (debug_context, cmd); | 
|  | } | 
|  |  | 
|  | /* Show all the threads.  */ | 
|  |  | 
|  | static void | 
|  | threads_show (void) | 
|  | { | 
|  | pthreadDebugId_t id; | 
|  | pthreadDebugThreadInfo_t info; | 
|  | int res; | 
|  |  | 
|  | res = pthread_debug_thd_seq_init (&id); | 
|  | if (res != 0) | 
|  | { | 
|  | TERM_FAO ("seq init failed, res=!SL!/", res); | 
|  | return; | 
|  | } | 
|  | while (1) | 
|  | { | 
|  | if (pthread_debug_thd_get_info (id, &info) != 0) | 
|  | { | 
|  | TERM_FAO ("thd_get_info !SL failed!/", id); | 
|  | break; | 
|  | } | 
|  | if (pthread_debug_thd_seq_next (&id) != 0) | 
|  | break; | 
|  | } | 
|  | pthread_debug_thd_seq_destroy (); | 
|  | } | 
|  |  | 
|  | /* Initialize pthread support.  */ | 
|  |  | 
|  | static void | 
|  | threads_init (void) | 
|  | { | 
|  | static const $DESCRIPTOR (dbgext_desc, "PTHREAD$DBGEXT"); | 
|  | static const $DESCRIPTOR (pthread_debug_desc, "PTHREAD$DBGSHR"); | 
|  | static const $DESCRIPTOR (dbgsymtable_desc, "PTHREAD_DBG_SYMTABLE"); | 
|  | int pthread_dbgext; | 
|  | int status; | 
|  | void *dbg_symtable; | 
|  | int i; | 
|  | void *caller_context = 0; | 
|  |  | 
|  | status = lib$find_image_symbol | 
|  | ((void *) &pthread_rtl_desc, (void *) &dbgext_desc, | 
|  | (int *) &dbgext_func); | 
|  | if (!(status & STS$M_SUCCESS)) | 
|  | LIB$SIGNAL (status); | 
|  |  | 
|  | status = lib$find_image_symbol | 
|  | ((void *) &pthread_rtl_desc, (void *) &dbgsymtable_desc, | 
|  | (int *) &dbg_symtable); | 
|  | if (!(status & STS$M_SUCCESS)) | 
|  | LIB$SIGNAL (status); | 
|  |  | 
|  | /* Find entry points in pthread_debug.  */ | 
|  | for (i = 0; | 
|  | i < sizeof (pthread_debug_entries) / sizeof (pthread_debug_entries[0]); | 
|  | i++) | 
|  | { | 
|  | struct dsc$descriptor_s sym = | 
|  | { pthread_debug_entries[i].namelen, | 
|  | DSC$K_DTYPE_T, DSC$K_CLASS_S, | 
|  | pthread_debug_entries[i].name }; | 
|  | status = lib$find_image_symbol | 
|  | ((void *) &pthread_debug_desc, (void *) &sym, | 
|  | (int *) &pthread_debug_entries[i].func); | 
|  | if (!(status & STS$M_SUCCESS)) | 
|  | lib$signal (status); | 
|  | } | 
|  |  | 
|  | if (trace_pthreaddbg) | 
|  | TERM_FAO ("debug symtable: !XH!/", dbg_symtable); | 
|  | status = ((int (*)()) pthread_debug_entries[0].func) | 
|  | (&caller_context, &pthread_debug_callbacks, dbg_symtable, &debug_context); | 
|  | if (status != 0) | 
|  | TERM_FAO ("cannot initialize pthread_debug: !UL!/", status); | 
|  | TERM_FAO ("pthread debug done!/", 0); | 
|  | } | 
|  |  | 
|  | /* 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | long2pkt (unsigned int v) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < 8; i++) | 
|  | { | 
|  | gdb_buf[gdb_blen + i] = hex[(v >> 28) & 0x0f]; | 
|  | v <<= 4; | 
|  | } | 
|  | gdb_blen += 8; | 
|  | } | 
|  |  | 
|  | /* 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Append a C string (ASCIZ) to the packet.  */ | 
|  |  | 
|  | static void | 
|  | str2pkt (const char *str) | 
|  | { | 
|  | while (*str) | 
|  | gdb_buf[gdb_blen++] = *str++; | 
|  | } | 
|  |  | 
|  | /* 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) | 
|  | { | 
|  | /* For qfThreadInfo and qsThreadInfo.  */ | 
|  | static unsigned int first_thread; | 
|  | static unsigned int last_thread; | 
|  |  | 
|  | static const char xfer_uib[] = "qXfer:uib:read:"; | 
|  | #define XFER_UIB_LEN (sizeof (xfer_uib) - 1) | 
|  | static const char qfthreadinfo[] = "qfThreadInfo"; | 
|  | #define QFTHREADINFO_LEN (sizeof (qfthreadinfo) - 1) | 
|  | static const char qsthreadinfo[] = "qsThreadInfo"; | 
|  | #define QSTHREADINFO_LEN (sizeof (qsthreadinfo) - 1) | 
|  | static const char qthreadextrainfo[] = "qThreadExtraInfo,"; | 
|  | #define QTHREADEXTRAINFO_LEN (sizeof (qthreadextrainfo) - 1) | 
|  | static const char qsupported[] = "qSupported:"; | 
|  | #define QSUPPORTED_LEN (sizeof (qsupported) - 1) | 
|  |  | 
|  | if (pktlen == 2 && pkt[1] == 'C') | 
|  | { | 
|  | /* Current thread.  */ | 
|  | gdb_buf[0] = '$'; | 
|  | gdb_buf[1] = 'Q'; | 
|  | gdb_buf[2] = 'C'; | 
|  | gdb_blen = 3; | 
|  | if (has_threads) | 
|  | long2pkt ((unsigned long) get_teb ()); | 
|  | return; | 
|  | } | 
|  | else if (pktlen > XFER_UIB_LEN | 
|  | && ots$strcmp_eql (pkt, XFER_UIB_LEN, xfer_uib, XFER_UIB_LEN)) | 
|  | { | 
|  | /* Get unwind information block.  */ | 
|  | unsigned __int64 pc; | 
|  | unsigned int pos = XFER_UIB_LEN; | 
|  | unsigned int off; | 
|  | unsigned int len; | 
|  | union | 
|  | { | 
|  | unsigned char bytes[32]; | 
|  | struct | 
|  | { | 
|  | unsigned __int64 code_start_va; | 
|  | unsigned __int64 code_end_va; | 
|  | unsigned __int64 uib_start_va; | 
|  | unsigned __int64 gp_value; | 
|  | } data; | 
|  | } uei; | 
|  | 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, &uei.data, 0); | 
|  | if (res == SS$_NODATA || res != SS$_NORMAL) | 
|  | ots$fill (uei.bytes, sizeof (uei.bytes), 0); | 
|  |  | 
|  | if (trace_unwind) | 
|  | { | 
|  | TERM_FAO ("Unwind request for !XH, status=!XL, uib=!XQ, GP=!XQ!/", | 
|  | pc, res, uei.data.uib_start_va, uei.data.gp_value); | 
|  | } | 
|  |  | 
|  | gdb_buf[0] = '$'; | 
|  | gdb_buf[1] = 'l'; | 
|  | gdb_blen = 2; | 
|  | mem2bin (uei.bytes, sizeof (uei.bytes)); | 
|  | } | 
|  | else if (pktlen == QFTHREADINFO_LEN | 
|  | && ots$strcmp_eql (pkt, QFTHREADINFO_LEN, | 
|  | qfthreadinfo, QFTHREADINFO_LEN)) | 
|  | { | 
|  | /* Get first thread(s).  */ | 
|  | gdb_buf[0] = '$'; | 
|  | gdb_buf[1] = 'm'; | 
|  | gdb_blen = 2; | 
|  |  | 
|  | if (!has_threads) | 
|  | { | 
|  | gdb_buf[1] = 'l'; | 
|  | return; | 
|  | } | 
|  | first_thread = thread_next (0); | 
|  | last_thread = first_thread; | 
|  | long2pkt (first_thread); | 
|  | } | 
|  | else if (pktlen == QSTHREADINFO_LEN | 
|  | && ots$strcmp_eql (pkt, QSTHREADINFO_LEN, | 
|  | qsthreadinfo, QSTHREADINFO_LEN)) | 
|  | { | 
|  | /* Get subsequent threads.  */ | 
|  | gdb_buf[0] = '$'; | 
|  | gdb_buf[1] = 'm'; | 
|  | gdb_blen = 2; | 
|  | while (dbgext_func) | 
|  | { | 
|  | unsigned int res; | 
|  | res = thread_next (last_thread); | 
|  | if (res == first_thread) | 
|  | break; | 
|  | if (gdb_blen > 2) | 
|  | gdb_buf[gdb_blen++] = ','; | 
|  | long2pkt (res); | 
|  | last_thread = res; | 
|  | if (gdb_blen > sizeof (gdb_buf) - 16) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (gdb_blen == 2) | 
|  | gdb_buf[1] = 'l'; | 
|  | } | 
|  | else if (pktlen > QTHREADEXTRAINFO_LEN | 
|  | && ots$strcmp_eql (pkt, QTHREADEXTRAINFO_LEN, | 
|  | qthreadextrainfo, QTHREADEXTRAINFO_LEN)) | 
|  | { | 
|  | /* Get extra info about a thread.  */ | 
|  | pthread_t thr; | 
|  | unsigned int pos = QTHREADEXTRAINFO_LEN; | 
|  | pthreadDebugThreadInfo_t info; | 
|  | int res; | 
|  |  | 
|  | packet_error (0); | 
|  | if (!has_threads) | 
|  | return; | 
|  |  | 
|  | thr = (pthread_t) pkt2val (pkt, &pos); | 
|  | if (pkt[pos] != '#') | 
|  | return; | 
|  | res = pthread_debug_thd_get_info_addr (thr, &info); | 
|  | if (res != 0) | 
|  | { | 
|  | TERM_FAO ("qThreadExtraInfo (!XH) failed: !SL!/", thr, res); | 
|  | return; | 
|  | } | 
|  | gdb_buf[0] = '$'; | 
|  | gdb_blen = 1; | 
|  | mem2hex ((const unsigned char *)"VMS-thread", 11); | 
|  | } | 
|  | else if (pktlen > QSUPPORTED_LEN | 
|  | && ots$strcmp_eql (pkt, QSUPPORTED_LEN, | 
|  | qsupported, QSUPPORTED_LEN)) | 
|  | { | 
|  | /* Get supported features.  */ | 
|  | pthread_t thr; | 
|  | unsigned int pos = QSUPPORTED_LEN; | 
|  | pthreadDebugThreadInfo_t info; | 
|  | int res; | 
|  |  | 
|  | /* Ignore gdb features.  */ | 
|  | gdb_buf[0] = '$'; | 
|  | gdb_blen = 1; | 
|  |  | 
|  | str2pkt ("qXfer:uib:read+"); | 
|  | return; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (trace_pkt) | 
|  | { | 
|  | term_puts ("unknown <: "); | 
|  | term_write ((char *)pkt, pktlen); | 
|  | term_putnl (); | 
|  | } | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Handle the 'v' packet.  */ | 
|  |  | 
|  | static int | 
|  | handle_v_packet (const unsigned char *pkt, unsigned int pktlen) | 
|  | { | 
|  | static const char vcontq[] = "vCont?"; | 
|  | #define VCONTQ_LEN (sizeof (vcontq) - 1) | 
|  |  | 
|  | if (pktlen == VCONTQ_LEN | 
|  | && ots$strcmp_eql (pkt, VCONTQ_LEN, vcontq, VCONTQ_LEN)) | 
|  | { | 
|  | gdb_buf[0] = '$'; | 
|  | gdb_blen = 1; | 
|  |  | 
|  | str2pkt ("vCont;c;s"); | 
|  | return 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (trace_pkt) | 
|  | { | 
|  | term_puts ("unknown <: "); | 
|  | term_write ((char *)pkt, pktlen); | 
|  | term_putnl (); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Get regs for the selected thread.  */ | 
|  |  | 
|  | static struct ia64_all_regs * | 
|  | get_selected_regs (void) | 
|  | { | 
|  | pthreadDebugRegs_t regs; | 
|  | int res; | 
|  |  | 
|  | if (selected_thread == 0 || selected_thread == get_teb ()) | 
|  | return &excp_regs; | 
|  |  | 
|  | if (selected_thread == sel_regs_pthread) | 
|  | return &sel_regs; | 
|  |  | 
|  | /* Read registers.  */ | 
|  | res = pthread_debug_thd_get_reg (selected_id, ®s); | 
|  | if (res != 0) | 
|  | { | 
|  | /* FIXME: return NULL ?  */ | 
|  | return &excp_regs; | 
|  | } | 
|  | sel_regs_pthread = selected_thread; | 
|  | sel_regs.gr[1].v = regs.gp; | 
|  | sel_regs.gr[4].v = regs.r4; | 
|  | sel_regs.gr[5].v = regs.r5; | 
|  | sel_regs.gr[6].v = regs.r6; | 
|  | sel_regs.gr[7].v = regs.r7; | 
|  | sel_regs.gr[12].v = regs.sp; | 
|  | sel_regs.br[0].v = regs.rp; | 
|  | sel_regs.br[1].v = regs.b1; | 
|  | sel_regs.br[2].v = regs.b2; | 
|  | sel_regs.br[3].v = regs.b3; | 
|  | sel_regs.br[4].v = regs.b4; | 
|  | sel_regs.br[5].v = regs.b5; | 
|  | sel_regs.ip.v = regs.ip; | 
|  | sel_regs.bsp.v = regs.bspstore; /* FIXME: it is correct ?  */ | 
|  | sel_regs.pfs.v = regs.pfs; | 
|  | sel_regs.pr.v = regs.pr; | 
|  | return &sel_regs; | 
|  | } | 
|  |  | 
|  | /* Create a status packet.  */ | 
|  |  | 
|  | static void | 
|  | packet_status (void) | 
|  | { | 
|  | gdb_blen = 0; | 
|  | if (has_threads) | 
|  | { | 
|  | str2pkt ("$T05thread:"); | 
|  | long2pkt ((unsigned long) get_teb ()); | 
|  | gdb_buf[gdb_blen++] = ';'; | 
|  | } | 
|  | else | 
|  | str2pkt ("$S05"); | 
|  | } | 
|  |  | 
|  | /* Return 1 to continue.  */ | 
|  |  | 
|  | static int | 
|  | handle_packet (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) | 
|  | { | 
|  | packet_status (); | 
|  | return 0; | 
|  | } | 
|  | break; | 
|  | case 'c': | 
|  | if (len == 1) | 
|  | { | 
|  | /* Clear psr.ss.  */ | 
|  | excp_regs.psr.v &= ~(unsigned __int64)PSR$M_SS; | 
|  | return 1; | 
|  | } | 
|  | else | 
|  | packet_error (0); | 
|  | break; | 
|  | case 'g': | 
|  | if (len == 1) | 
|  | { | 
|  | unsigned int i; | 
|  | struct ia64_all_regs *regs = get_selected_regs (); | 
|  | 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 'H': | 
|  | if (pkt[1] == 'g') | 
|  | { | 
|  | int res; | 
|  | unsigned __int64 val; | 
|  | pthreadDebugThreadInfo_t info; | 
|  |  | 
|  | pos++; | 
|  | val = pkt2val (pkt, &pos); | 
|  | if (pos != len) | 
|  | { | 
|  | packet_error (0); | 
|  | return 0; | 
|  | } | 
|  | if (val == 0) | 
|  | { | 
|  | /* Default one.  */ | 
|  | selected_thread = get_teb (); | 
|  | selected_id = 0; | 
|  | } | 
|  | else if (!has_threads) | 
|  | { | 
|  | packet_error (0); | 
|  | return 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | res = pthread_debug_thd_get_info_addr ((pthread_t) val, &info); | 
|  | if (res != 0) | 
|  | { | 
|  | TERM_FAO ("qThreadExtraInfo (!XH) failed: !SL!/", val, res); | 
|  | packet_error (0); | 
|  | return 0; | 
|  | } | 
|  | selected_thread = info.teb; | 
|  | selected_id = info.sequence; | 
|  | } | 
|  | packet_ok (); | 
|  | break; | 
|  | } | 
|  | else if (pkt[1] == 'c' | 
|  | && ((pkt[2] == '-' && pkt[3] == '1' && len == 4) | 
|  | || (pkt[2] == '0' && len == 3))) | 
|  | { | 
|  | /* Silently accept 'Hc0' and 'Hc-1'.  */ | 
|  | packet_ok (); | 
|  | break; | 
|  | } | 
|  | else | 
|  | { | 
|  | packet_error (0); | 
|  | return 0; | 
|  | } | 
|  | case 'k': | 
|  | SYS$EXIT (SS$_NORMAL); | 
|  | break; | 
|  | case 'm': | 
|  | { | 
|  | unsigned __int64 addr; | 
|  | unsigned __int64 paddr; | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* Check access.  */ | 
|  | i = l + (addr & VMS_PAGE_MASK); | 
|  | paddr = addr & ~VMS_PAGE_MASK; | 
|  | while (1) | 
|  | { | 
|  | if (__prober (paddr, 0) != 1) | 
|  | { | 
|  | packet_error (2); | 
|  | return 0; | 
|  | } | 
|  | if (i < VMS_PAGE_SIZE) | 
|  | break; | 
|  | i -= VMS_PAGE_SIZE; | 
|  | paddr += VMS_PAGE_SIZE; | 
|  | } | 
|  |  | 
|  | /* Transfer.  */ | 
|  | 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 __int64 paddr; | 
|  | 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); | 
|  |  | 
|  | /* Check access.  */ | 
|  | i = l + (addr & VMS_PAGE_MASK); | 
|  | paddr = addr & ~VMS_PAGE_MASK; | 
|  | while (1) | 
|  | { | 
|  | if (__probew (paddr, 0) != 1) | 
|  | { | 
|  | page_restore_rw (addr, l, oldprot); | 
|  | return 0; | 
|  | } | 
|  | if (i < VMS_PAGE_SIZE) | 
|  | break; | 
|  | i -= VMS_PAGE_SIZE; | 
|  | paddr += VMS_PAGE_SIZE; | 
|  | } | 
|  |  | 
|  | /* Write.  */ | 
|  | for (i = 0; i < l; i++) | 
|  | { | 
|  | int v = hex2byte (pkt + pos); | 
|  | pos += 2; | 
|  | ((unsigned char *)addr)[i] = v; | 
|  | } | 
|  |  | 
|  | /* Sync caches.  */ | 
|  | for (i = 0; i < l; i += 15) | 
|  | __fc (addr + i); | 
|  | __fc (addr + l); | 
|  |  | 
|  | page_restore_rw (addr, l, oldprot); | 
|  | packet_ok (); | 
|  | } | 
|  | break; | 
|  | case 'p': | 
|  | { | 
|  | unsigned int num = 0; | 
|  | unsigned int i; | 
|  | struct ia64_all_regs *regs = get_selected_regs (); | 
|  |  | 
|  | 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_FAO ("gdbserv: unhandled reg !UW!/", num); | 
|  | packet_error (0); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case 'q': | 
|  | handle_q_packet (pkt, len); | 
|  | break; | 
|  | case 's': | 
|  | if (len == 1) | 
|  | { | 
|  | /* Set psr.ss.  */ | 
|  | excp_regs.psr.v |= (unsigned __int64)PSR$M_SS; | 
|  | return 1; | 
|  | } | 
|  | else | 
|  | packet_error (0); | 
|  | break; | 
|  | case 'T': | 
|  | /* Thread status.  */ | 
|  | if (!has_threads) | 
|  | { | 
|  | packet_ok (); | 
|  | break; | 
|  | } | 
|  | else | 
|  | { | 
|  | int res; | 
|  | unsigned __int64 val; | 
|  | unsigned int fthr, thr; | 
|  |  | 
|  | val = pkt2val (pkt, &pos); | 
|  | /* Default is error (but only after parsing is complete).  */ | 
|  | packet_error (0); | 
|  | if (pos != len) | 
|  | break; | 
|  |  | 
|  | /* Follow the list.  This makes a O(n2) algorithm, but we don't really | 
|  | have the choice.  Note that pthread_debug_thd_get_info_addr | 
|  | doesn't look reliable.  */ | 
|  | fthr = thread_next (0); | 
|  | thr = fthr; | 
|  | do | 
|  | { | 
|  | if (val == thr) | 
|  | { | 
|  | packet_ok (); | 
|  | break; | 
|  | } | 
|  | thr = thread_next (thr); | 
|  | } | 
|  | while (thr != fthr); | 
|  | } | 
|  | break; | 
|  | case 'v': | 
|  | return handle_v_packet (pkt, len); | 
|  | break; | 
|  | case 'V': | 
|  | if (len > 3 && pkt[1] == 'M' && pkt[2] == 'S' && pkt[3] == ' ') | 
|  | { | 
|  | /* Temporary extension.  */ | 
|  | if (has_threads) | 
|  | { | 
|  | pkt[len] = 0; | 
|  | stub_pthread_debug_cmd ((char *)pkt + 4); | 
|  | packet_ok (); | 
|  | } | 
|  | else | 
|  | packet_error (0); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | if (trace_pkt) | 
|  | { | 
|  | term_puts ("unknown <: "); | 
|  | term_write ((char *)pkt, len); | 
|  | term_putnl (); | 
|  | } | 
|  | 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"); | 
|  | LIB$SIGNAL (status); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Compute the checksum 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 > 1) | 
|  | { | 
|  | 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" ); | 
|  | 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"); | 
|  | continue; | 
|  | } | 
|  | else | 
|  | { | 
|  | sock_write ((const unsigned char *)"+", 1); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (trace_pkt > 1) | 
|  | { | 
|  | 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, struct chf$mech_array *mech) | 
|  | { | 
|  | 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 (); | 
|  |  | 
|  | if (trace_excp > 1) | 
|  | { | 
|  | TERM_FAO (" Frame: !XH, Depth: !4SL, Esf: !XH!/", | 
|  | mech->chf$q_mch_frame, mech->chf$q_mch_depth, | 
|  | mech->chf$q_mch_esf_addr); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Get all registers from current thread.  */ | 
|  |  | 
|  | static void | 
|  | read_all_registers (struct chf$mech_array *mech) | 
|  | { | 
|  | struct _intstk *intstk = | 
|  | (struct _intstk *)mech->chf$q_mch_esf_addr; | 
|  | struct chf64$signal_array *sig64 = | 
|  | (struct chf64$signal_array *)mech->chf$ph_mch_sig64_addr; | 
|  | unsigned int cnt = sig64->chf64$w_sig_arg_count; | 
|  | unsigned __int64 pc = (&sig64->chf64$q_sig_name)[cnt - 2]; | 
|  |  | 
|  | excp_regs.ip.v = pc; | 
|  | excp_regs.psr.v = intstk->intstk$q_ipsr; | 
|  | /* 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; | 
|  | excp_regs.bsp.v = bsp + ((sof + delta / 0x3f) << 3); | 
|  | } | 
|  | excp_regs.cfm.v = intstk->intstk$q_ifs & 0x3fffffffff; | 
|  | excp_regs.pfs.v = intstk->intstk$q_pfs; | 
|  | excp_regs.pr.v = intstk->intstk$q_preds; | 
|  | excp_regs.gr[0].v = 0; | 
|  | excp_regs.gr[1].v = intstk->intstk$q_gp; | 
|  | excp_regs.gr[2].v = intstk->intstk$q_r2; | 
|  | excp_regs.gr[3].v = intstk->intstk$q_r3; | 
|  | excp_regs.gr[4].v = intstk->intstk$q_r4; | 
|  | excp_regs.gr[5].v = intstk->intstk$q_r5; | 
|  | excp_regs.gr[6].v = intstk->intstk$q_r6; | 
|  | excp_regs.gr[7].v = intstk->intstk$q_r7; | 
|  | excp_regs.gr[8].v = intstk->intstk$q_r8; | 
|  | excp_regs.gr[9].v = intstk->intstk$q_r9; | 
|  | excp_regs.gr[10].v = intstk->intstk$q_r10; | 
|  | excp_regs.gr[11].v = intstk->intstk$q_r11; | 
|  | excp_regs.gr[12].v = (unsigned __int64)intstk + intstk->intstk$l_stkalign; | 
|  | excp_regs.gr[13].v = intstk->intstk$q_r13; | 
|  | excp_regs.gr[14].v = intstk->intstk$q_r14; | 
|  | excp_regs.gr[15].v = intstk->intstk$q_r15; | 
|  | excp_regs.gr[16].v = intstk->intstk$q_r16; | 
|  | excp_regs.gr[17].v = intstk->intstk$q_r17; | 
|  | excp_regs.gr[18].v = intstk->intstk$q_r18; | 
|  | excp_regs.gr[19].v = intstk->intstk$q_r19; | 
|  | excp_regs.gr[20].v = intstk->intstk$q_r20; | 
|  | excp_regs.gr[21].v = intstk->intstk$q_r21; | 
|  | excp_regs.gr[22].v = intstk->intstk$q_r22; | 
|  | excp_regs.gr[23].v = intstk->intstk$q_r23; | 
|  | excp_regs.gr[24].v = intstk->intstk$q_r24; | 
|  | excp_regs.gr[25].v = intstk->intstk$q_r25; | 
|  | excp_regs.gr[26].v = intstk->intstk$q_r26; | 
|  | excp_regs.gr[27].v = intstk->intstk$q_r27; | 
|  | excp_regs.gr[28].v = intstk->intstk$q_r28; | 
|  | excp_regs.gr[29].v = intstk->intstk$q_r29; | 
|  | excp_regs.gr[30].v = intstk->intstk$q_r30; | 
|  | excp_regs.gr[31].v = intstk->intstk$q_r31; | 
|  | excp_regs.br[0].v = intstk->intstk$q_b0; | 
|  | excp_regs.br[1].v = intstk->intstk$q_b1; | 
|  | excp_regs.br[2].v = intstk->intstk$q_b2; | 
|  | excp_regs.br[3].v = intstk->intstk$q_b3; | 
|  | excp_regs.br[4].v = intstk->intstk$q_b4; | 
|  | excp_regs.br[5].v = intstk->intstk$q_b5; | 
|  | excp_regs.br[6].v = intstk->intstk$q_b6; | 
|  | excp_regs.br[7].v = intstk->intstk$q_b7; | 
|  | } | 
|  |  | 
|  | /* Write all registers to current thread.  FIXME: not yet complete.  */ | 
|  |  | 
|  | static void | 
|  | write_all_registers (struct chf$mech_array *mech) | 
|  | { | 
|  | struct _intstk *intstk = | 
|  | (struct _intstk *)mech->chf$q_mch_esf_addr; | 
|  |  | 
|  | intstk->intstk$q_ipsr = excp_regs.psr.v; | 
|  | } | 
|  |  | 
|  | /* Do debugging.  Report status to gdb and execute commands.  */ | 
|  |  | 
|  | static void | 
|  | do_debug (struct chf$mech_array *mech) | 
|  | { | 
|  | struct _intstk *intstk = | 
|  | (struct _intstk *)mech->chf$q_mch_esf_addr; | 
|  | unsigned int old_ast; | 
|  | unsigned int old_sch; | 
|  | unsigned int status; | 
|  |  | 
|  | /* Disable ast.  */ | 
|  | status = sys$setast (0); | 
|  | switch (status) | 
|  | { | 
|  | case SS$_WASCLR: | 
|  | old_ast = 0; | 
|  | break; | 
|  | case SS$_WASSET: | 
|  | old_ast = 1; | 
|  | break; | 
|  | default: | 
|  | /* Should never happen!  */ | 
|  | lib$signal (status); | 
|  | } | 
|  |  | 
|  | /* Disable thread scheduling.  */ | 
|  | if (has_threads) | 
|  | old_sch = set_thread_scheduling (0); | 
|  |  | 
|  | read_all_registers (mech); | 
|  |  | 
|  | /* Send stop reply packet.  */ | 
|  | packet_status (); | 
|  | send_pkt (); | 
|  |  | 
|  | while (one_command () == 0) | 
|  | ; | 
|  |  | 
|  | write_all_registers (mech); | 
|  |  | 
|  | /* Re-enable scheduling.  */ | 
|  | if (has_threads) | 
|  | set_thread_scheduling (old_sch); | 
|  |  | 
|  | /* Re-enable AST.  */ | 
|  | status = sys$setast (old_ast); | 
|  | if (!(status & STS$M_SUCCESS)) | 
|  | LIB$SIGNAL (status); | 
|  | } | 
|  |  | 
|  | /* The condition handler.  That's the core of the stub.  */ | 
|  |  | 
|  | static int | 
|  | excp_handler (struct chf$signal_array *sig, | 
|  | struct chf$mech_array *mech) | 
|  | { | 
|  | struct chf64$signal_array *sig64 = | 
|  | (struct chf64$signal_array *)mech->chf$ph_mch_sig64_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; | 
|  | unsigned int ret; | 
|  | /* Self protection.  FIXME: Should be per thread ?  */ | 
|  | static int in_handler = 0; | 
|  |  | 
|  | /* Completely ignore some conditions (signaled indirectly by this stub).  */ | 
|  | switch (code) | 
|  | { | 
|  | case LIB$_KEYNOTFOU & STS$M_COND_ID: | 
|  | return SS$_RESIGNAL_64; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Protect against recursion.  */ | 
|  | in_handler++; | 
|  | if (in_handler > 1) | 
|  | { | 
|  | if (in_handler == 2) | 
|  | TERM_FAO ("gdbstub: exception in handler (pc=!XH)!!!/", | 
|  | (&sig64->chf64$q_sig_name)[cnt - 2]); | 
|  | sys$exit (sig->chf$l_sig_name); | 
|  | } | 
|  |  | 
|  | pc = (&sig64->chf64$q_sig_name)[cnt - 2]; | 
|  | if (trace_excp) | 
|  | TERM_FAO ("excp_handler: code: !XL, pc=!XH!/", code, pc); | 
|  |  | 
|  | /* 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"); | 
|  | page_set_rw (entry_pc, 16, &entry_prot); | 
|  |  | 
|  | ots$move ((void *)entry_pc, 16, entry_saved); | 
|  | __fc (entry_pc); | 
|  | page_restore_rw (entry_pc, 16, entry_prot); | 
|  | } | 
|  |  | 
|  | switch (code) | 
|  | { | 
|  | case SS$_ACCVIO & STS$M_COND_ID: | 
|  | if (trace_excp <= 1) | 
|  | display_excp (sig64, mech); | 
|  | /* Fall through.  */ | 
|  | case SS$_BREAK  & STS$M_COND_ID: | 
|  | case SS$_OPCDEC & STS$M_COND_ID: | 
|  | case SS$_TBIT   & STS$M_COND_ID: | 
|  | case SS$_DEBUG  & STS$M_COND_ID: | 
|  | if (trace_excp > 1) | 
|  | { | 
|  | int i; | 
|  | struct _intstk *intstk = | 
|  | (struct _intstk *)mech->chf$q_mch_esf_addr; | 
|  |  | 
|  | display_excp (sig64, mech); | 
|  |  | 
|  | TERM_FAO (" intstk: !XH!/", intstk); | 
|  | for (i = 0; i < cnt + 1; i++) | 
|  | TERM_FAO ("   !XH!/", ((unsigned __int64 *)sig64)[i]); | 
|  | } | 
|  | do_debug (mech); | 
|  | ret = SS$_CONTINUE_64; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | display_excp (sig64, mech); | 
|  | ret = SS$_RESIGNAL_64; | 
|  | break; | 
|  | } | 
|  |  | 
|  | in_handler--; | 
|  | /* Discard selected thread registers.  */ | 
|  | sel_regs_pthread = 0; | 
|  | 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"); | 
|  | $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] == ',' || resstring[i] == ';') | 
|  | && i != start) | 
|  | { | 
|  | int j; | 
|  |  | 
|  | sub_desc.dsc$a_pointer = resstring + start; | 
|  | sub_desc.dsc$w_length = i - start; | 
|  |  | 
|  | for (j = 0; j < NBR_DEBUG_FLAGS; j++) | 
|  | if (str$case_blind_compare (&sub_desc, | 
|  | (void *)&debug_flags[j].name) == 0) | 
|  | { | 
|  | debug_flags[j].val++; | 
|  | break; | 
|  | } | 
|  | if (j == NBR_DEBUG_FLAGS) | 
|  | TERM_FAO ("GDBSTUB$TRACE: unknown directive !AS!/", &sub_desc); | 
|  |  | 
|  | start = i + 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | TERM_FAO ("GDBSTUB$TRACE=!AD ->", len, resstring); | 
|  | for (i = 0; i < NBR_DEBUG_FLAGS; i++) | 
|  | if (debug_flags[i].val > 0) | 
|  | TERM_FAO (" !AS=!ZL", &debug_flags[i].name, debug_flags[i].val); | 
|  | term_putnl (); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Entry point.  */ | 
|  |  | 
|  | static int | 
|  | stub_start (unsigned __int64 *progxfer, void *cli_util, | 
|  | EIHD *imghdr, IFD *imgfile, | 
|  | unsigned int linkflag, unsigned int cliflag) | 
|  | { | 
|  | static int initialized; | 
|  | int i; | 
|  | int cnt; | 
|  | int is_attached; | 
|  | IMCB *imcb; | 
|  | if (initialized) | 
|  | term_puts ("gdbstub: re-entry\n"); | 
|  | else | 
|  | initialized = 1; | 
|  |  | 
|  | /* When attached (through SS$_DEBUG condition), the number of arguments | 
|  | is 4 and PROGXFER is the PC at interruption.  */ | 
|  | va_count (cnt); | 
|  | is_attached = cnt == 4; | 
|  |  | 
|  | term_init (); | 
|  |  | 
|  | /* Hello banner.  */ | 
|  | term_puts ("Hello from gdb stub\n"); | 
|  |  | 
|  | trace_init (); | 
|  |  | 
|  | if (trace_entry && !is_attached) | 
|  | { | 
|  | TERM_FAO ("xfer: !XH, imghdr: !XH, ifd: !XH!/", | 
|  | progxfer, imghdr, imgfile); | 
|  | for (i = -2; i < 8; i++) | 
|  | TERM_FAO ("  at !2SW: !XH!/", i, progxfer[i]); | 
|  | } | 
|  |  | 
|  | /* Search for entry point.  */ | 
|  | if (!is_attached) | 
|  | { | 
|  | 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"); | 
|  | return 0; | 
|  | } | 
|  | else | 
|  | TERM_FAO ("Entry: !XH!/",entry_pc); | 
|  | } | 
|  | } | 
|  | else | 
|  | entry_pc = progxfer[0]; | 
|  |  | 
|  | has_threads = 0; | 
|  | for (imcb = ctl$gl_imglstptr->imcb$l_flink; | 
|  | imcb != ctl$gl_imglstptr; | 
|  | imcb = imcb->imcb$l_flink) | 
|  | { | 
|  | if (ots$strcmp_eql (pthread_rtl_desc.dsc$a_pointer, | 
|  | pthread_rtl_desc.dsc$w_length, | 
|  | imcb->imcb$t_log_image_name + 1, | 
|  | imcb->imcb$t_log_image_name[0])) | 
|  | has_threads = 1; | 
|  |  | 
|  | if (trace_images) | 
|  | { | 
|  | unsigned int j; | 
|  | LDRIMG *ldrimg = imcb->imcb$l_ldrimg; | 
|  | LDRISD *ldrisd; | 
|  |  | 
|  | TERM_FAO ("!XA-!XA ", | 
|  | imcb->imcb$l_starting_address, | 
|  | imcb->imcb$l_end_address); | 
|  |  | 
|  | switch (imcb->imcb$b_act_code) | 
|  | { | 
|  | case IMCB$K_MAIN_PROGRAM: | 
|  | term_puts ("prog"); | 
|  | break; | 
|  | case IMCB$K_MERGED_IMAGE: | 
|  | term_puts ("mrge"); | 
|  | break; | 
|  | case IMCB$K_GLOBAL_IMAGE_SECTION: | 
|  | term_puts ("glob"); | 
|  | break; | 
|  | default: | 
|  | term_puts ("????"); | 
|  | } | 
|  | TERM_FAO (" !AD !40AC!/", | 
|  | 1, "KESU" + (imcb->imcb$b_access_mode & 3), | 
|  | imcb->imcb$t_log_image_name); | 
|  |  | 
|  | if ((long) ldrimg < 0 || trace_images < 2) | 
|  | continue; | 
|  | ldrisd = ldrimg->ldrimg$l_segments; | 
|  | for (j = 0; j < ldrimg->ldrimg$l_segcount; j++) | 
|  | { | 
|  | unsigned int flags = ldrisd[j].ldrisd$i_flags; | 
|  | term_puts ("   "); | 
|  | term_putc (flags & 0x04 ? 'R' : '-'); | 
|  | term_putc (flags & 0x02 ? 'W' : '-'); | 
|  | term_putc (flags & 0x01 ? 'X' : '-'); | 
|  | term_puts (flags & 0x01000000 ? " Prot" : "     "); | 
|  | term_puts (flags & 0x04000000 ? " Shrt" : "     "); | 
|  | term_puts (flags & 0x08000000 ? " Shrd" : "     "); | 
|  | TERM_FAO (" !XA-!XA!/", | 
|  | ldrisd[j].ldrisd$p_base, | 
|  | (unsigned __int64) ldrisd[j].ldrisd$p_base | 
|  | + ldrisd[j].ldrisd$i_len - 1); | 
|  | } | 
|  | ldrisd = ldrimg->ldrimg$l_dyn_seg; | 
|  | if (ldrisd) | 
|  | TERM_FAO ("   dynamic            !XA-!XA!/", | 
|  | ldrisd->ldrisd$p_base, | 
|  | (unsigned __int64) ldrisd->ldrisd$p_base | 
|  | + ldrisd->ldrisd$i_len - 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (has_threads) | 
|  | threads_init (); | 
|  |  | 
|  | /* Wait for connection.  */ | 
|  | sock_init (); | 
|  |  | 
|  | /* Set primary exception vector.  */ | 
|  | { | 
|  | unsigned int status; | 
|  | status = sys$setexv (0, excp_handler, PSL$C_USER, (__void_ptr32) &prevhnd); | 
|  | if (!(status & STS$M_SUCCESS)) | 
|  | LIB$SIGNAL (status); | 
|  | } | 
|  |  | 
|  | if (is_attached) | 
|  | { | 
|  | return excp_handler ((struct chf$signal_array *) progxfer[2], | 
|  | (struct chf$mech_array *) progxfer[3]); | 
|  | } | 
|  |  | 
|  | /* 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 (!(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"); | 
|  | } | 
|  | else | 
|  | LIB$SIGNAL (status); | 
|  | } | 
|  |  | 
|  | if (entry_pc != 0) | 
|  | { | 
|  | ots$move (entry_saved, 16, (void *)entry_pc); | 
|  | ots$move ((void *)entry_pc, 16, (void *)initbp); | 
|  | __fc (entry_pc); | 
|  | page_restore_rw (entry_pc, 16, entry_prot); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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 SS$_CONTINUE; | 
|  | } | 
|  |  | 
|  | /* Declare the entry point of this relocatable module.  */ | 
|  |  | 
|  | struct xfer_vector | 
|  | { | 
|  | __int64 impure_start; | 
|  | __int64 impure_end; | 
|  | int (*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 |