1/* Host support routines for MinGW, for GDB, the GNU debugger. 2 3 Copyright (C) 2006-2023 Free Software Foundation, Inc. 4 5 This file is part of GDB. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20#include "defs.h" 21#include "main.h" 22#include "serial.h" 23#include "gdbsupport/event-loop.h" 24#include "gdbsupport/gdb_select.h" 25#include "inferior.h" 26 27#include <windows.h> 28#include <signal.h> 29 30/* Return an absolute file name of the running GDB, if possible, or 31 ARGV0 if not. The return value is in malloc'ed storage. */ 32 33char * 34windows_get_absolute_argv0 (const char *argv0) 35{ 36 char full_name[PATH_MAX]; 37 38 if (GetModuleFileName (NULL, full_name, PATH_MAX)) 39 return xstrdup (full_name); 40 return xstrdup (argv0); 41} 42 43/* Wrapper for select. On Windows systems, where the select interface 44 only works for sockets, this uses the GDB serial abstraction to 45 handle sockets, consoles, pipes, and serial ports. 46 47 The arguments to this function are the same as the traditional 48 arguments to select on POSIX platforms. */ 49 50int 51gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, 52 struct timeval *timeout) 53{ 54 static HANDLE never_handle; 55 HANDLE handles[MAXIMUM_WAIT_OBJECTS]; 56 HANDLE h; 57 DWORD event; 58 DWORD num_handles; 59 /* SCBS contains serial control objects corresponding to file 60 descriptors in READFDS and WRITEFDS. */ 61 struct serial *scbs[MAXIMUM_WAIT_OBJECTS]; 62 /* The number of valid entries in SCBS. */ 63 size_t num_scbs; 64 int fd; 65 int num_ready; 66 size_t indx; 67 68 if (n == 0) 69 { 70 /* The MS API says that the first argument to 71 WaitForMultipleObjects cannot be zero. That's why we just 72 use a regular Sleep here. */ 73 if (timeout != NULL) 74 Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000); 75 76 return 0; 77 } 78 79 num_ready = 0; 80 num_handles = 0; 81 num_scbs = 0; 82 for (fd = 0; fd < n; ++fd) 83 { 84 HANDLE read = NULL, except = NULL; 85 struct serial *scb; 86 87 /* There is no support yet for WRITEFDS. At present, this isn't 88 used by GDB -- but we do not want to silently ignore WRITEFDS 89 if something starts using it. */ 90 gdb_assert (!writefds || !FD_ISSET (fd, writefds)); 91 92 if ((!readfds || !FD_ISSET (fd, readfds)) 93 && (!exceptfds || !FD_ISSET (fd, exceptfds))) 94 continue; 95 96 scb = serial_for_fd (fd); 97 if (scb) 98 { 99 serial_wait_handle (scb, &read, &except); 100 scbs[num_scbs++] = scb; 101 } 102 103 if (read == NULL) 104 read = (HANDLE) _get_osfhandle (fd); 105 if (except == NULL) 106 { 107 if (!never_handle) 108 never_handle = CreateEvent (0, FALSE, FALSE, 0); 109 110 except = never_handle; 111 } 112 113 if (readfds && FD_ISSET (fd, readfds)) 114 { 115 gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); 116 handles[num_handles++] = read; 117 } 118 119 if (exceptfds && FD_ISSET (fd, exceptfds)) 120 { 121 gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); 122 handles[num_handles++] = except; 123 } 124 } 125 126 gdb_assert (num_handles <= MAXIMUM_WAIT_OBJECTS); 127 128 event = WaitForMultipleObjects (num_handles, 129 handles, 130 FALSE, 131 timeout 132 ? (timeout->tv_sec * 1000 133 + timeout->tv_usec / 1000) 134 : INFINITE); 135 /* EVENT can only be a value in the WAIT_ABANDONED_0 range if the 136 HANDLES included an abandoned mutex. Since GDB doesn't use 137 mutexes, that should never occur. */ 138 gdb_assert (!(WAIT_ABANDONED_0 <= event 139 && event < WAIT_ABANDONED_0 + num_handles)); 140 /* We no longer need the helper threads to check for activity. */ 141 for (indx = 0; indx < num_scbs; ++indx) 142 serial_done_wait_handle (scbs[indx]); 143 if (event == WAIT_FAILED) 144 return -1; 145 if (event == WAIT_TIMEOUT) 146 return 0; 147 /* Run through the READFDS, clearing bits corresponding to descriptors 148 for which input is unavailable. */ 149 h = handles[event - WAIT_OBJECT_0]; 150 for (fd = 0, indx = 0; fd < n; ++fd) 151 { 152 HANDLE fd_h; 153 154 if ((!readfds || !FD_ISSET (fd, readfds)) 155 && (!exceptfds || !FD_ISSET (fd, exceptfds))) 156 continue; 157 158 if (readfds && FD_ISSET (fd, readfds)) 159 { 160 fd_h = handles[indx++]; 161 /* This handle might be ready, even though it wasn't the handle 162 returned by WaitForMultipleObjects. */ 163 if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) 164 FD_CLR (fd, readfds); 165 else 166 num_ready++; 167 } 168 169 if (exceptfds && FD_ISSET (fd, exceptfds)) 170 { 171 fd_h = handles[indx++]; 172 /* This handle might be ready, even though it wasn't the handle 173 returned by WaitForMultipleObjects. */ 174 if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) 175 FD_CLR (fd, exceptfds); 176 else 177 num_ready++; 178 } 179 } 180 181 return num_ready; 182} 183 184/* Map COLOR's RGB triplet, with 8 bits per component, into 16 Windows 185 console colors, where each component has just 1 bit, plus a single 186 intensity bit which affects all 3 components. */ 187static int 188rgb_to_16colors (const ui_file_style::color &color) 189{ 190 uint8_t rgb[3]; 191 color.get_rgb (rgb); 192 193 int retval = 0; 194 for (int i = 0; i < 3; i++) 195 { 196 /* Subdivide 256 possible values of each RGB component into 3 197 regions: no color, normal color, bright color. 256 / 3 = 85, 198 but ui-style.c follows xterm and uses 92 for R and G 199 components of the bright-blue color, so we bias the divisor a 200 bit to have the bright colors between 9 and 15 identical to 201 what ui-style.c expects. */ 202 int bits = rgb[i] / 93; 203 retval |= ((bits > 0) << (2 - i)) | ((bits > 1) << 3); 204 } 205 206 return retval; 207} 208 209/* Zero if not yet initialized, 1 if stdout is a console device, else -1. */ 210static int mingw_console_initialized; 211 212/* Handle to stdout . */ 213static HANDLE hstdout = INVALID_HANDLE_VALUE; 214 215/* Text attribute to use for normal text (the "none" pseudo-color). */ 216static SHORT norm_attr; 217 218/* The most recently applied style. */ 219static ui_file_style last_style; 220 221/* Alternative for the libc 'fputs' which handles embedded SGR 222 sequences in support of styling. */ 223 224int 225gdb_console_fputs (const char *linebuf, FILE *fstream) 226{ 227 if (!mingw_console_initialized) 228 { 229 hstdout = (HANDLE)_get_osfhandle (fileno (fstream)); 230 DWORD cmode; 231 CONSOLE_SCREEN_BUFFER_INFO csbi; 232 233 if (hstdout != INVALID_HANDLE_VALUE 234 && GetConsoleMode (hstdout, &cmode) != 0 235 && GetConsoleScreenBufferInfo (hstdout, &csbi)) 236 { 237 norm_attr = csbi.wAttributes; 238 mingw_console_initialized = 1; 239 } 240 else if (hstdout != INVALID_HANDLE_VALUE) 241 mingw_console_initialized = -1; /* valid, but not a console device */ 242 } 243 /* If our stdout is not a console device, let the default 'fputs' 244 handle the task. */ 245 if (mingw_console_initialized <= 0) 246 return 0; 247 248 /* Mapping between 8 ANSI colors and Windows console attributes. */ 249 static int fg_color[] = { 250 0, /* black */ 251 FOREGROUND_RED, /* red */ 252 FOREGROUND_GREEN, /* green */ 253 FOREGROUND_GREEN | FOREGROUND_RED, /* yellow */ 254 FOREGROUND_BLUE, /* blue */ 255 FOREGROUND_BLUE | FOREGROUND_RED, /* magenta */ 256 FOREGROUND_BLUE | FOREGROUND_GREEN, /* cyan */ 257 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE /* gray */ 258 }; 259 static int bg_color[] = { 260 0, /* black */ 261 BACKGROUND_RED, /* red */ 262 BACKGROUND_GREEN, /* green */ 263 BACKGROUND_GREEN | BACKGROUND_RED, /* yellow */ 264 BACKGROUND_BLUE, /* blue */ 265 BACKGROUND_BLUE | BACKGROUND_RED, /* magenta */ 266 BACKGROUND_BLUE | BACKGROUND_GREEN, /* cyan */ 267 BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE /* gray */ 268 }; 269 270 ui_file_style style = last_style; 271 unsigned char c; 272 size_t n_read; 273 274 for ( ; (c = *linebuf) != 0; linebuf += n_read) 275 { 276 if (c == '\033') 277 { 278 fflush (fstream); 279 bool parsed = style.parse (linebuf, &n_read); 280 if (n_read <= 0) /* should never happen */ 281 n_read = 1; 282 if (!parsed) 283 { 284 /* This means we silently swallow SGR sequences we 285 cannot parse. */ 286 continue; 287 } 288 /* Colors. */ 289 const ui_file_style::color &fg = style.get_foreground (); 290 const ui_file_style::color &bg = style.get_background (); 291 int fgcolor, bgcolor, bright, inverse; 292 if (fg.is_none ()) 293 fgcolor = norm_attr & 15; 294 else if (fg.is_basic ()) 295 fgcolor = fg_color[fg.get_value () & 15]; 296 else 297 fgcolor = rgb_to_16colors (fg); 298 if (bg.is_none ()) 299 bgcolor = norm_attr & (15 << 4); 300 else if (bg.is_basic ()) 301 bgcolor = bg_color[bg.get_value () & 15]; 302 else 303 bgcolor = rgb_to_16colors (bg) << 4; 304 305 /* Intensity. */ 306 switch (style.get_intensity ()) 307 { 308 case ui_file_style::NORMAL: 309 case ui_file_style::DIM: 310 bright = 0; 311 break; 312 case ui_file_style::BOLD: 313 bright = 1; 314 break; 315 default: 316 gdb_assert_not_reached ("invalid intensity"); 317 } 318 319 /* Inverse video. */ 320 if (style.is_reverse ()) 321 inverse = 1; 322 else 323 inverse = 0; 324 325 /* Construct the attribute. */ 326 if (inverse) 327 { 328 int t = fgcolor; 329 fgcolor = (bgcolor >> 4); 330 bgcolor = (t << 4); 331 } 332 if (bright) 333 fgcolor |= FOREGROUND_INTENSITY; 334 335 SHORT attr = (bgcolor & (15 << 4)) | (fgcolor & 15); 336 337 /* Apply the attribute. */ 338 SetConsoleTextAttribute (hstdout, attr); 339 } 340 else 341 { 342 /* When we are about to write newline, we need to clear to 343 EOL with the normal attribute, to avoid spilling the 344 colors to the next screen line. We assume here that no 345 non-default attribute extends beyond the newline. */ 346 if (c == '\n') 347 { 348 DWORD nchars; 349 COORD start_pos; 350 DWORD written; 351 CONSOLE_SCREEN_BUFFER_INFO csbi; 352 353 fflush (fstream); 354 GetConsoleScreenBufferInfo (hstdout, &csbi); 355 356 if (csbi.wAttributes != norm_attr) 357 { 358 start_pos = csbi.dwCursorPosition; 359 nchars = csbi.dwSize.X - start_pos.X; 360 361 FillConsoleOutputAttribute (hstdout, norm_attr, nchars, 362 start_pos, &written); 363 FillConsoleOutputCharacter (hstdout, ' ', nchars, 364 start_pos, &written); 365 } 366 } 367 fputc (c, fstream); 368 n_read = 1; 369 } 370 } 371 372 last_style = style; 373 return 1; 374} 375 376/* See inferior.h. */ 377 378tribool 379sharing_input_terminal (int pid) 380{ 381 std::vector<DWORD> results (10); 382 DWORD len = 0; 383 while (true) 384 { 385 len = GetConsoleProcessList (results.data (), results.size ()); 386 /* Note that LEN == 0 is a failure, but we can treat it the same 387 as a "no". */ 388 if (len < results.size ()) 389 break; 390 391 results.resize (len); 392 } 393 /* In case the vector was too big. */ 394 results.resize (len); 395 if (std::find (results.begin (), results.end (), pid) != results.end ()) 396 { 397 /* The pid is in the list sharing the console, so don't 398 interrupt the inferior -- it will get the signal itself. */ 399 return TRIBOOL_TRUE; 400 } 401 402 return TRIBOOL_FALSE; 403} 404 405/* Current C-c handler. */ 406static c_c_handler_ftype *current_handler; 407 408/* The Windows callback that forwards requests to the C-c handler. */ 409static BOOL WINAPI 410ctrl_c_handler (DWORD event_type) 411{ 412 if (event_type == CTRL_BREAK_EVENT || event_type == CTRL_C_EVENT) 413 { 414 if (current_handler != SIG_IGN) 415 current_handler (SIGINT); 416 } 417 else 418 return FALSE; 419 return TRUE; 420} 421 422/* See inferior.h. */ 423 424c_c_handler_ftype * 425install_sigint_handler (c_c_handler_ftype *fn) 426{ 427 /* We want to make sure the gdb handler always comes first, so that 428 gdb gets to handle the C-c. This is why the handler is always 429 removed and reinstalled here. Note that trying to remove the 430 function without installing it first will cause a crash. */ 431 static bool installed = false; 432 if (installed) 433 SetConsoleCtrlHandler (ctrl_c_handler, FALSE); 434 SetConsoleCtrlHandler (ctrl_c_handler, TRUE); 435 installed = true; 436 437 c_c_handler_ftype *result = current_handler; 438 current_handler = fn; 439 return result; 440} 441