mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-18 12:24:38 +08:00
1d506c26d9
This commit is the result of the following actions: - Running gdb/copyright.py to update all of the copyright headers to include 2024, - Manually updating a few files the copyright.py script told me to update, these files had copyright headers embedded within the file, - Regenerating gdbsupport/Makefile.in to refresh it's copyright date, - Using grep to find other files that still mentioned 2023. If these files were updated last year from 2022 to 2023 then I've updated them this year to 2024. I'm sure I've probably missed some dates. Feel free to fix them up as you spot them.
2602 lines
62 KiB
C
2602 lines
62 KiB
C
/* GDB stub for Itanium OpenVMS
|
|
Copyright (C) 2012-2024 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 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 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 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)
|
|
{
|
|
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
|