1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247 |
- /* Target-vector operations for controlling windows child processes, for GDB.
- Copyright (C) 1995-2022 Free Software Foundation, Inc.
- Contributed by Cygnus Solutions, A Red Hat Company.
- This file is part of GDB.
- 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/>. */
- /* Originally by Steve Chamberlain, sac@cygnus.com */
- #include "defs.h"
- #include "frame.h" /* required by inferior.h */
- #include "inferior.h"
- #include "infrun.h"
- #include "target.h"
- #include "gdbcore.h"
- #include "command.h"
- #include "completer.h"
- #include "regcache.h"
- #include "top.h"
- #include <signal.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <windows.h>
- #include <imagehlp.h>
- #ifdef __CYGWIN__
- #include <wchar.h>
- #include <sys/cygwin.h>
- #include <cygwin/version.h>
- #endif
- #include <algorithm>
- #include <vector>
- #include "filenames.h"
- #include "symfile.h"
- #include "objfiles.h"
- #include "gdb_bfd.h"
- #include "gdbsupport/gdb_obstack.h"
- #include "gdbthread.h"
- #include "gdbcmd.h"
- #include <unistd.h>
- #include "exec.h"
- #include "solist.h"
- #include "solib.h"
- #include "xml-support.h"
- #include "inttypes.h"
- #include "i386-tdep.h"
- #include "i387-tdep.h"
- #include "windows-tdep.h"
- #include "windows-nat.h"
- #include "x86-nat.h"
- #include "complaints.h"
- #include "inf-child.h"
- #include "gdbsupport/gdb_tilde_expand.h"
- #include "gdbsupport/pathstuff.h"
- #include "gdbsupport/gdb_wait.h"
- #include "nat/windows-nat.h"
- #include "gdbsupport/symbol.h"
- using namespace windows_nat;
- /* The current process. */
- static windows_process_info windows_process;
- #undef STARTUPINFO
- #undef CreateProcess
- #undef GetModuleFileNameEx
- #ifndef __CYGWIN__
- # define __PMAX (MAX_PATH + 1)
- # define GetModuleFileNameEx GetModuleFileNameExA
- # define STARTUPINFO STARTUPINFOA
- # define CreateProcess CreateProcessA
- #else
- # define __PMAX PATH_MAX
- /* The starting and ending address of the cygwin1.dll text segment. */
- static CORE_ADDR cygwin_load_start;
- static CORE_ADDR cygwin_load_end;
- # define __USEWIDE
- typedef wchar_t cygwin_buf_t;
- # define GetModuleFileNameEx GetModuleFileNameExW
- # define STARTUPINFO STARTUPINFOW
- # define CreateProcess CreateProcessW
- #endif
- static int have_saved_context; /* True if we've saved context from a
- cygwin signal. */
- #ifdef __CYGWIN__
- static CONTEXT saved_context; /* Contains the saved context from a
- cygwin signal. */
- #endif
- /* If we're not using the old Cygwin header file set, define the
- following which never should have been in the generic Win32 API
- headers in the first place since they were our own invention... */
- #ifndef _GNU_H_WINDOWS_H
- enum
- {
- FLAG_TRACE_BIT = 0x100,
- };
- #endif
- #ifndef CONTEXT_EXTENDED_REGISTERS
- /* This macro is only defined on ia32. It only makes sense on this target,
- so define it as zero if not already defined. */
- #define CONTEXT_EXTENDED_REGISTERS 0
- #endif
- #define CONTEXT_DEBUGGER_DR CONTEXT_FULL | CONTEXT_FLOATING_POINT \
- | CONTEXT_SEGMENTS | CONTEXT_DEBUG_REGISTERS \
- | CONTEXT_EXTENDED_REGISTERS
- static uintptr_t dr[8];
- static int windows_initialization_done;
- #define DR6_CLEAR_VALUE 0xffff0ff0
- /* The string sent by cygwin when it processes a signal.
- FIXME: This should be in a cygwin include file. */
- #ifndef _CYGWIN_SIGNAL_STRING
- #define _CYGWIN_SIGNAL_STRING "cYgSiGw00f"
- #endif
- #define CHECK(x) check (x, __FILE__,__LINE__)
- #define DEBUG_EXEC(fmt, ...) \
- debug_prefixed_printf_cond (debug_exec, "windows exec", fmt, ## __VA_ARGS__)
- #define DEBUG_EVENTS(fmt, ...) \
- debug_prefixed_printf_cond (debug_events, "windows events", fmt, \
- ## __VA_ARGS__)
- #define DEBUG_MEM(fmt, ...) \
- debug_prefixed_printf_cond (debug_memory, "windows mem", fmt, \
- ## __VA_ARGS__)
- #define DEBUG_EXCEPT(fmt, ...) \
- debug_prefixed_printf_cond (debug_exceptions, "windows except", fmt, \
- ## __VA_ARGS__)
- static void cygwin_set_dr (int i, CORE_ADDR addr);
- static void cygwin_set_dr7 (unsigned long val);
- static CORE_ADDR cygwin_get_dr (int i);
- static unsigned long cygwin_get_dr6 (void);
- static unsigned long cygwin_get_dr7 (void);
- static std::vector<std::unique_ptr<windows_thread_info>> thread_list;
- /* Counts of things. */
- static int saw_create;
- static int open_process_used = 0;
- #ifdef __x86_64__
- static void *wow64_dbgbreak;
- #endif
- /* User options. */
- static bool new_console = false;
- #ifdef __CYGWIN__
- static bool cygwin_exceptions = false;
- #endif
- static bool new_group = true;
- static bool debug_exec = false; /* show execution */
- static bool debug_events = false; /* show events from kernel */
- static bool debug_memory = false; /* show target memory accesses */
- static bool debug_exceptions = false; /* show target exceptions */
- static bool useshell = false; /* use shell for subprocesses */
- /* This vector maps GDB's idea of a register's number into an offset
- in the windows exception context vector.
- It also contains the bit mask needed to load the register in question.
- The contents of this table can only be computed by the units
- that provide CPU-specific support for Windows native debugging.
- These units should set the table by calling
- windows_set_context_register_offsets.
- One day we could read a reg, we could inspect the context we
- already have loaded, if it doesn't have the bit set that we need,
- we read that set of registers in using GetThreadContext. If the
- context already contains what we need, we just unpack it. Then to
- write a register, first we have to ensure that the context contains
- the other regs of the group, and then we copy the info in and set
- out bit. */
- static const int *mappings;
- /* The function to use in order to determine whether a register is
- a segment register or not. */
- static segment_register_p_ftype *segment_register_p;
- /* See windows_nat_target::resume to understand why this is commented
- out. */
- #if 0
- /* This vector maps the target's idea of an exception (extracted
- from the DEBUG_EVENT structure) to GDB's idea. */
- struct xlate_exception
- {
- DWORD them;
- enum gdb_signal us;
- };
- static const struct xlate_exception xlate[] =
- {
- {EXCEPTION_ACCESS_VIOLATION, GDB_SIGNAL_SEGV},
- {STATUS_STACK_OVERFLOW, GDB_SIGNAL_SEGV},
- {EXCEPTION_BREAKPOINT, GDB_SIGNAL_TRAP},
- {DBG_CONTROL_C, GDB_SIGNAL_INT},
- {EXCEPTION_SINGLE_STEP, GDB_SIGNAL_TRAP},
- {STATUS_FLOAT_DIVIDE_BY_ZERO, GDB_SIGNAL_FPE}
- };
- #endif /* 0 */
- struct windows_nat_target final : public x86_nat_target<inf_child_target>
- {
- void close () override;
- void attach (const char *, int) override;
- bool attach_no_wait () override
- { return true; }
- void detach (inferior *, int) override;
- void resume (ptid_t, int , enum gdb_signal) override;
- ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
- void fetch_registers (struct regcache *, int) override;
- void store_registers (struct regcache *, int) override;
- bool stopped_by_sw_breakpoint () override
- {
- windows_thread_info *th
- = windows_process.thread_rec (inferior_ptid, DONT_INVALIDATE_CONTEXT);
- return th->stopped_at_software_breakpoint;
- }
- bool supports_stopped_by_sw_breakpoint () override
- {
- return true;
- }
- enum target_xfer_status xfer_partial (enum target_object object,
- const char *annex,
- gdb_byte *readbuf,
- const gdb_byte *writebuf,
- ULONGEST offset, ULONGEST len,
- ULONGEST *xfered_len) override;
- void files_info () override;
- void kill () override;
- void create_inferior (const char *, const std::string &,
- char **, int) override;
- void mourn_inferior () override;
- bool thread_alive (ptid_t ptid) override;
- std::string pid_to_str (ptid_t) override;
- void interrupt () override;
- char *pid_to_exec_file (int pid) override;
- ptid_t get_ada_task_ptid (long lwp, ULONGEST thread) override;
- bool get_tib_address (ptid_t ptid, CORE_ADDR *addr) override;
- const char *thread_name (struct thread_info *) override;
- int get_windows_debug_event (int pid, struct target_waitstatus *ourstatus);
- void do_initial_windows_stuff (DWORD pid, bool attaching);
- };
- static windows_nat_target the_windows_nat_target;
- /* Set the MAPPINGS static global to OFFSETS.
- See the description of MAPPINGS for more details. */
- static void
- windows_set_context_register_offsets (const int *offsets)
- {
- mappings = offsets;
- }
- /* Set the function that should be used by this module to determine
- whether a given register is a segment register or not. */
- static void
- windows_set_segment_register_p (segment_register_p_ftype *fun)
- {
- segment_register_p = fun;
- }
- static void
- check (BOOL ok, const char *file, int line)
- {
- if (!ok)
- gdb_printf ("error return %s:%d was %u\n", file, line,
- (unsigned) GetLastError ());
- }
- /* See nat/windows-nat.h. */
- windows_thread_info *
- windows_nat::windows_process_info::thread_rec
- (ptid_t ptid, thread_disposition_type disposition)
- {
- for (auto &th : thread_list)
- if (th->tid == ptid.lwp ())
- {
- if (!th->suspended)
- {
- switch (disposition)
- {
- case DONT_INVALIDATE_CONTEXT:
- /* Nothing. */
- break;
- case INVALIDATE_CONTEXT:
- if (ptid.lwp () != current_event.dwThreadId)
- th->suspend ();
- th->reload_context = true;
- break;
- case DONT_SUSPEND:
- th->reload_context = true;
- th->suspended = -1;
- break;
- }
- }
- return th.get ();
- }
- return NULL;
- }
- /* Add a thread to the thread list.
- PTID is the ptid of the thread to be added.
- H is its Windows handle.
- TLB is its thread local base.
- MAIN_THREAD_P should be true if the thread to be added is
- the main thread, false otherwise. */
- static windows_thread_info *
- windows_add_thread (ptid_t ptid, HANDLE h, void *tlb, bool main_thread_p)
- {
- windows_thread_info *th;
- gdb_assert (ptid.lwp () != 0);
- if ((th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT)))
- return th;
- CORE_ADDR base = (CORE_ADDR) (uintptr_t) tlb;
- #ifdef __x86_64__
- /* For WOW64 processes, this is actually the pointer to the 64bit TIB,
- and the 32bit TIB is exactly 2 pages after it. */
- if (windows_process.wow64_process)
- base += 0x2000;
- #endif
- th = new windows_thread_info (ptid.lwp (), h, base);
- thread_list.emplace_back (th);
- /* Add this new thread to the list of threads.
- To be consistent with what's done on other platforms, we add
- the main thread silently (in reality, this thread is really
- more of a process to the user than a thread). */
- if (main_thread_p)
- add_thread_silent (&the_windows_nat_target, ptid);
- else
- add_thread (&the_windows_nat_target, ptid);
- /* It's simplest to always set this and update the debug
- registers. */
- th->debug_registers_changed = true;
- return th;
- }
- /* Clear out any old thread list and reinitialize it to a
- pristine state. */
- static void
- windows_init_thread_list (void)
- {
- DEBUG_EVENTS ("called");
- thread_list.clear ();
- }
- /* Delete a thread from the list of threads.
- PTID is the ptid of the thread to be deleted.
- EXIT_CODE is the thread's exit code.
- MAIN_THREAD_P should be true if the thread to be deleted is
- the main thread, false otherwise. */
- static void
- windows_delete_thread (ptid_t ptid, DWORD exit_code, bool main_thread_p)
- {
- DWORD id;
- gdb_assert (ptid.lwp () != 0);
- id = ptid.lwp ();
- /* Emit a notification about the thread being deleted.
- Note that no notification was printed when the main thread
- was created, and thus, unless in verbose mode, we should be
- symmetrical, and avoid that notification for the main thread
- here as well. */
- if (info_verbose)
- gdb_printf ("[Deleting %s]\n", target_pid_to_str (ptid).c_str ());
- else if (print_thread_events && !main_thread_p)
- gdb_printf (_("[%s exited with code %u]\n"),
- target_pid_to_str (ptid).c_str (),
- (unsigned) exit_code);
- delete_thread (find_thread_ptid (&the_windows_nat_target, ptid));
- auto iter = std::find_if (thread_list.begin (), thread_list.end (),
- [=] (auto &th)
- {
- return th->tid == id;
- });
- if (iter != thread_list.end ())
- thread_list.erase (iter);
- }
- /* Fetches register number R from the given windows_thread_info,
- and supplies its value to the given regcache.
- This function assumes that R is non-negative. A failed assertion
- is raised if that is not true.
- This function assumes that TH->RELOAD_CONTEXT is not set, meaning
- that the windows_thread_info has an up-to-date context. A failed
- assertion is raised if that assumption is violated. */
- static void
- windows_fetch_one_register (struct regcache *regcache,
- windows_thread_info *th, int r)
- {
- gdb_assert (r >= 0);
- gdb_assert (!th->reload_context);
- char *context_ptr = (char *) &th->context;
- #ifdef __x86_64__
- if (windows_process.wow64_process)
- context_ptr = (char *) &th->wow64_context;
- #endif
- char *context_offset = context_ptr + mappings[r];
- struct gdbarch *gdbarch = regcache->arch ();
- i386_gdbarch_tdep *tdep = (i386_gdbarch_tdep *) gdbarch_tdep (gdbarch);
- gdb_assert (!gdbarch_read_pc_p (gdbarch));
- gdb_assert (gdbarch_pc_regnum (gdbarch) >= 0);
- gdb_assert (!gdbarch_write_pc_p (gdbarch));
- if (r == I387_FISEG_REGNUM (tdep))
- {
- long l = *((long *) context_offset) & 0xffff;
- regcache->raw_supply (r, (char *) &l);
- }
- else if (r == I387_FOP_REGNUM (tdep))
- {
- long l = (*((long *) context_offset) >> 16) & ((1 << 11) - 1);
- regcache->raw_supply (r, (char *) &l);
- }
- else if (segment_register_p (r))
- {
- /* GDB treats segment registers as 32bit registers, but they are
- in fact only 16 bits long. Make sure we do not read extra
- bits from our source buffer. */
- long l = *((long *) context_offset) & 0xffff;
- regcache->raw_supply (r, (char *) &l);
- }
- else
- {
- if (th->stopped_at_software_breakpoint
- && !th->pc_adjusted
- && r == gdbarch_pc_regnum (gdbarch))
- {
- int size = register_size (gdbarch, r);
- if (size == 4)
- {
- uint32_t value;
- memcpy (&value, context_offset, size);
- value -= gdbarch_decr_pc_after_break (gdbarch);
- memcpy (context_offset, &value, size);
- }
- else
- {
- gdb_assert (size == 8);
- uint64_t value;
- memcpy (&value, context_offset, size);
- value -= gdbarch_decr_pc_after_break (gdbarch);
- memcpy (context_offset, &value, size);
- }
- /* Make sure we only rewrite the PC a single time. */
- th->pc_adjusted = true;
- }
- regcache->raw_supply (r, context_offset);
- }
- }
- void
- windows_nat_target::fetch_registers (struct regcache *regcache, int r)
- {
- windows_thread_info *th
- = windows_process.thread_rec (regcache->ptid (), INVALIDATE_CONTEXT);
- /* Check if TH exists. Windows sometimes uses a non-existent
- thread id in its events. */
- if (th == NULL)
- return;
- if (th->reload_context)
- {
- #ifdef __CYGWIN__
- if (have_saved_context)
- {
- /* Lie about where the program actually is stopped since
- cygwin has informed us that we should consider the signal
- to have occurred at another location which is stored in
- "saved_context. */
- memcpy (&th->context, &saved_context,
- __COPY_CONTEXT_SIZE);
- have_saved_context = 0;
- }
- else
- #endif
- #ifdef __x86_64__
- if (windows_process.wow64_process)
- {
- th->wow64_context.ContextFlags = CONTEXT_DEBUGGER_DR;
- CHECK (Wow64GetThreadContext (th->h, &th->wow64_context));
- /* Copy dr values from that thread.
- But only if there were not modified since last stop.
- PR gdb/2388 */
- if (!th->debug_registers_changed)
- {
- dr[0] = th->wow64_context.Dr0;
- dr[1] = th->wow64_context.Dr1;
- dr[2] = th->wow64_context.Dr2;
- dr[3] = th->wow64_context.Dr3;
- dr[6] = th->wow64_context.Dr6;
- dr[7] = th->wow64_context.Dr7;
- }
- }
- else
- #endif
- {
- th->context.ContextFlags = CONTEXT_DEBUGGER_DR;
- CHECK (GetThreadContext (th->h, &th->context));
- /* Copy dr values from that thread.
- But only if there were not modified since last stop.
- PR gdb/2388 */
- if (!th->debug_registers_changed)
- {
- dr[0] = th->context.Dr0;
- dr[1] = th->context.Dr1;
- dr[2] = th->context.Dr2;
- dr[3] = th->context.Dr3;
- dr[6] = th->context.Dr6;
- dr[7] = th->context.Dr7;
- }
- }
- th->reload_context = false;
- }
- if (r < 0)
- for (r = 0; r < gdbarch_num_regs (regcache->arch()); r++)
- windows_fetch_one_register (regcache, th, r);
- else
- windows_fetch_one_register (regcache, th, r);
- }
- /* Collect the register number R from the given regcache, and store
- its value into the corresponding area of the given thread's context.
- This function assumes that R is non-negative. A failed assertion
- assertion is raised if that is not true. */
- static void
- windows_store_one_register (const struct regcache *regcache,
- windows_thread_info *th, int r)
- {
- gdb_assert (r >= 0);
- char *context_ptr = (char *) &th->context;
- #ifdef __x86_64__
- if (windows_process.wow64_process)
- context_ptr = (char *) &th->wow64_context;
- #endif
- regcache->raw_collect (r, context_ptr + mappings[r]);
- }
- /* Store a new register value into the context of the thread tied to
- REGCACHE. */
- void
- windows_nat_target::store_registers (struct regcache *regcache, int r)
- {
- windows_thread_info *th
- = windows_process.thread_rec (regcache->ptid (), INVALIDATE_CONTEXT);
- /* Check if TH exists. Windows sometimes uses a non-existent
- thread id in its events. */
- if (th == NULL)
- return;
- if (r < 0)
- for (r = 0; r < gdbarch_num_regs (regcache->arch ()); r++)
- windows_store_one_register (regcache, th, r);
- else
- windows_store_one_register (regcache, th, r);
- }
- /* Maintain a linked list of "so" information. */
- struct windows_solib
- {
- LPVOID load_addr = 0;
- CORE_ADDR text_offset = 0;
- /* Original name. */
- std::string original_name;
- /* Expanded form of the name. */
- std::string name;
- };
- static std::vector<windows_solib> solibs;
- /* See nat/windows-nat.h. */
- static windows_solib *
- windows_make_so (const char *name, LPVOID load_addr)
- {
- char *p;
- #ifndef __CYGWIN__
- char buf[__PMAX];
- char cwd[__PMAX];
- WIN32_FIND_DATA w32_fd;
- HANDLE h = FindFirstFile(name, &w32_fd);
- if (h == INVALID_HANDLE_VALUE)
- strcpy (buf, name);
- else
- {
- FindClose (h);
- strcpy (buf, name);
- if (GetCurrentDirectory (MAX_PATH + 1, cwd))
- {
- p = strrchr (buf, '\\');
- if (p)
- p[1] = '\0';
- SetCurrentDirectory (buf);
- GetFullPathName (w32_fd.cFileName, MAX_PATH, buf, &p);
- SetCurrentDirectory (cwd);
- }
- }
- if (strcasecmp (buf, "ntdll.dll") == 0)
- {
- GetSystemDirectory (buf, sizeof (buf));
- strcat (buf, "\\ntdll.dll");
- }
- #else
- cygwin_buf_t buf[__PMAX];
- buf[0] = 0;
- if (access (name, F_OK) != 0)
- {
- if (strcasecmp (name, "ntdll.dll") == 0)
- #ifdef __USEWIDE
- {
- GetSystemDirectoryW (buf, sizeof (buf) / sizeof (wchar_t));
- wcscat (buf, L"\\ntdll.dll");
- }
- #else
- {
- GetSystemDirectoryA (buf, sizeof (buf) / sizeof (wchar_t));
- strcat (buf, "\\ntdll.dll");
- }
- #endif
- }
- #endif
- solibs.emplace_back ();
- windows_solib *so = &solibs.back ();
- so->load_addr = load_addr;
- so->original_name = name;
- #ifndef __CYGWIN__
- so->name = buf;
- #else
- if (buf[0])
- {
- char name[SO_NAME_MAX_PATH_SIZE];
- cygwin_conv_path (CCP_WIN_W_TO_POSIX, buf, name,
- SO_NAME_MAX_PATH_SIZE);
- so->name = name;
- }
- else
- {
- char *rname = realpath (name, NULL);
- if (rname && strlen (rname) < SO_NAME_MAX_PATH_SIZE)
- {
- so->name = rname;
- free (rname);
- }
- else
- {
- warning (_("dll path for \"%s\" too long or inaccessible"), name);
- so->name = so->original_name;
- }
- }
- /* Record cygwin1.dll .text start/end. */
- size_t len = sizeof ("/cygwin1.dll") - 1;
- if (so->name.size () >= len
- && strcasecmp (so->name.c_str () + so->name.size () - len,
- "/cygwin1.dll") == 0)
- {
- asection *text = NULL;
- gdb_bfd_ref_ptr abfd (gdb_bfd_open (so->name, "pei-i386"));
- if (abfd == NULL)
- return so;
- if (bfd_check_format (abfd.get (), bfd_object))
- text = bfd_get_section_by_name (abfd.get (), ".text");
- if (!text)
- return so;
- /* The symbols in a dll are offset by 0x1000, which is the
- offset from 0 of the first byte in an image - because of the
- file header and the section alignment. */
- cygwin_load_start = (CORE_ADDR) (uintptr_t) ((char *)
- load_addr + 0x1000);
- cygwin_load_end = cygwin_load_start + bfd_section_size (text);
- }
- #endif
- return so;
- }
- /* See nat/windows-nat.h. */
- void
- windows_nat::windows_process_info::handle_load_dll (const char *dll_name,
- LPVOID base)
- {
- windows_solib *solib = windows_make_so (dll_name, base);
- DEBUG_EVENTS ("Loading dll \"%s\" at %s.", solib->name.c_str (),
- host_address_to_string (solib->load_addr));
- }
- /* See nat/windows-nat.h. */
- void
- windows_nat::windows_process_info::handle_unload_dll ()
- {
- LPVOID lpBaseOfDll = current_event.u.UnloadDll.lpBaseOfDll;
- auto iter = std::remove_if (solibs.begin (), solibs.end (),
- [&] (windows_solib &lib)
- {
- if (lib.load_addr == lpBaseOfDll)
- {
- DEBUG_EVENTS ("Unloading dll \"%s\".", lib.name.c_str ());
- return true;
- }
- return false;
- });
- if (iter != solibs.end ())
- {
- solibs.erase (iter, solibs.end ());
- return;
- }
- /* We did not find any DLL that was previously loaded at this address,
- so register a complaint. We do not report an error, because we have
- observed that this may be happening under some circumstances. For
- instance, running 32bit applications on x64 Windows causes us to receive
- 4 mysterious UNLOAD_DLL_DEBUG_EVENTs during the startup phase (these
- events are apparently caused by the WOW layer, the interface between
- 32bit and 64bit worlds). */
- complaint (_("dll starting at %s not found."),
- host_address_to_string (lpBaseOfDll));
- }
- /* Clear list of loaded DLLs. */
- static void
- windows_clear_solib (void)
- {
- solibs.clear ();
- }
- static void
- signal_event_command (const char *args, int from_tty)
- {
- uintptr_t event_id = 0;
- char *endargs = NULL;
- if (args == NULL)
- error (_("signal-event requires an argument (integer event id)"));
- event_id = strtoumax (args, &endargs, 10);
- if ((errno == ERANGE) || (event_id == 0) || (event_id > UINTPTR_MAX) ||
- ((HANDLE) event_id == INVALID_HANDLE_VALUE))
- error (_("Failed to convert `%s' to event id"), args);
- SetEvent ((HANDLE) event_id);
- CloseHandle ((HANDLE) event_id);
- }
- /* See nat/windows-nat.h. */
- int
- windows_nat::windows_process_info::handle_output_debug_string
- (struct target_waitstatus *ourstatus)
- {
- int retval = 0;
- gdb::unique_xmalloc_ptr<char> s
- = (target_read_string
- ((CORE_ADDR) (uintptr_t) current_event.u.DebugString.lpDebugStringData,
- 1024));
- if (s == nullptr || !*(s.get ()))
- /* nothing to do */;
- else if (!startswith (s.get (), _CYGWIN_SIGNAL_STRING))
- {
- #ifdef __CYGWIN__
- if (!startswith (s.get (), "cYg"))
- #endif
- {
- char *p = strchr (s.get (), '\0');
- if (p > s.get () && *--p == '\n')
- *p = '\0';
- warning (("%s"), s.get ());
- }
- }
- #ifdef __CYGWIN__
- else
- {
- /* Got a cygwin signal marker. A cygwin signal is followed by
- the signal number itself and then optionally followed by the
- thread id and address to saved context within the DLL. If
- these are supplied, then the given thread is assumed to have
- issued the signal and the context from the thread is assumed
- to be stored at the given address in the inferior. Tell gdb
- to treat this like a real signal. */
- char *p;
- int sig = strtol (s.get () + sizeof (_CYGWIN_SIGNAL_STRING) - 1, &p, 0);
- gdb_signal gotasig = gdb_signal_from_host (sig);
- if (gotasig)
- {
- LPCVOID x;
- SIZE_T n;
- ourstatus->set_stopped (gotasig);
- retval = strtoul (p, &p, 0);
- if (!retval)
- retval = current_event.dwThreadId;
- else if ((x = (LPCVOID) (uintptr_t) strtoull (p, NULL, 0))
- && ReadProcessMemory (current_process_handle, x,
- &saved_context,
- __COPY_CONTEXT_SIZE, &n)
- && n == __COPY_CONTEXT_SIZE)
- have_saved_context = 1;
- }
- }
- #endif
- return retval;
- }
- static int
- display_selector (HANDLE thread, DWORD sel)
- {
- LDT_ENTRY info;
- BOOL ret;
- #ifdef __x86_64__
- if (windows_process.wow64_process)
- ret = Wow64GetThreadSelectorEntry (thread, sel, &info);
- else
- #endif
- ret = GetThreadSelectorEntry (thread, sel, &info);
- if (ret)
- {
- int base, limit;
- gdb_printf ("0x%03x: ", (unsigned) sel);
- if (!info.HighWord.Bits.Pres)
- {
- gdb_puts ("Segment not present\n");
- return 0;
- }
- base = (info.HighWord.Bits.BaseHi << 24) +
- (info.HighWord.Bits.BaseMid << 16)
- + info.BaseLow;
- limit = (info.HighWord.Bits.LimitHi << 16) + info.LimitLow;
- if (info.HighWord.Bits.Granularity)
- limit = (limit << 12) | 0xfff;
- gdb_printf ("base=0x%08x limit=0x%08x", base, limit);
- if (info.HighWord.Bits.Default_Big)
- gdb_puts(" 32-bit ");
- else
- gdb_puts(" 16-bit ");
- switch ((info.HighWord.Bits.Type & 0xf) >> 1)
- {
- case 0:
- gdb_puts ("Data (Read-Only, Exp-up");
- break;
- case 1:
- gdb_puts ("Data (Read/Write, Exp-up");
- break;
- case 2:
- gdb_puts ("Unused segment (");
- break;
- case 3:
- gdb_puts ("Data (Read/Write, Exp-down");
- break;
- case 4:
- gdb_puts ("Code (Exec-Only, N.Conf");
- break;
- case 5:
- gdb_puts ("Code (Exec/Read, N.Conf");
- break;
- case 6:
- gdb_puts ("Code (Exec-Only, Conf");
- break;
- case 7:
- gdb_puts ("Code (Exec/Read, Conf");
- break;
- default:
- gdb_printf ("Unknown type 0x%lx",
- (unsigned long) info.HighWord.Bits.Type);
- }
- if ((info.HighWord.Bits.Type & 0x1) == 0)
- gdb_puts(", N.Acc");
- gdb_puts (")\n");
- if ((info.HighWord.Bits.Type & 0x10) == 0)
- gdb_puts("System selector ");
- gdb_printf ("Priviledge level = %ld. ",
- (unsigned long) info.HighWord.Bits.Dpl);
- if (info.HighWord.Bits.Granularity)
- gdb_puts ("Page granular.\n");
- else
- gdb_puts ("Byte granular.\n");
- return 1;
- }
- else
- {
- DWORD err = GetLastError ();
- if (err == ERROR_NOT_SUPPORTED)
- gdb_printf ("Function not supported\n");
- else
- gdb_printf ("Invalid selector 0x%x.\n", (unsigned) sel);
- return 0;
- }
- }
- static void
- display_selectors (const char * args, int from_tty)
- {
- if (inferior_ptid == null_ptid)
- {
- gdb_puts ("Impossible to display selectors now.\n");
- return;
- }
- windows_thread_info *current_windows_thread
- = windows_process.thread_rec (inferior_ptid, DONT_INVALIDATE_CONTEXT);
- if (!args)
- {
- #ifdef __x86_64__
- if (windows_process.wow64_process)
- {
- gdb_puts ("Selector $cs\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->wow64_context.SegCs);
- gdb_puts ("Selector $ds\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->wow64_context.SegDs);
- gdb_puts ("Selector $es\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->wow64_context.SegEs);
- gdb_puts ("Selector $ss\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->wow64_context.SegSs);
- gdb_puts ("Selector $fs\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->wow64_context.SegFs);
- gdb_puts ("Selector $gs\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->wow64_context.SegGs);
- }
- else
- #endif
- {
- gdb_puts ("Selector $cs\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->context.SegCs);
- gdb_puts ("Selector $ds\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->context.SegDs);
- gdb_puts ("Selector $es\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->context.SegEs);
- gdb_puts ("Selector $ss\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->context.SegSs);
- gdb_puts ("Selector $fs\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->context.SegFs);
- gdb_puts ("Selector $gs\n");
- display_selector (current_windows_thread->h,
- current_windows_thread->context.SegGs);
- }
- }
- else
- {
- int sel;
- sel = parse_and_eval_long (args);
- gdb_printf ("Selector \"%s\"\n",args);
- display_selector (current_windows_thread->h, sel);
- }
- }
- /* See nat/windows-nat.h. */
- bool
- windows_nat::windows_process_info::handle_ms_vc_exception
- (const EXCEPTION_RECORD *rec)
- {
- if (rec->NumberParameters >= 3
- && (rec->ExceptionInformation[0] & 0xffffffff) == 0x1000)
- {
- DWORD named_thread_id;
- windows_thread_info *named_thread;
- CORE_ADDR thread_name_target;
- thread_name_target = rec->ExceptionInformation[1];
- named_thread_id = (DWORD) (0xffffffff & rec->ExceptionInformation[2]);
- if (named_thread_id == (DWORD) -1)
- named_thread_id = current_event.dwThreadId;
- named_thread = thread_rec (ptid_t (current_event.dwProcessId,
- named_thread_id, 0),
- DONT_INVALIDATE_CONTEXT);
- if (named_thread != NULL)
- {
- int thread_name_len;
- gdb::unique_xmalloc_ptr<char> thread_name
- = target_read_string (thread_name_target, 1025, &thread_name_len);
- if (thread_name_len > 0)
- {
- thread_name.get ()[thread_name_len - 1] = '\0';
- named_thread->name = std::move (thread_name);
- }
- }
- return true;
- }
- return false;
- }
- /* See nat/windows-nat.h. */
- bool
- windows_nat::windows_process_info::handle_access_violation
- (const EXCEPTION_RECORD *rec)
- {
- #ifdef __CYGWIN__
- /* See if the access violation happened within the cygwin DLL
- itself. Cygwin uses a kind of exception handling to deal with
- passed-in invalid addresses. gdb should not treat these as real
- SEGVs since they will be silently handled by cygwin. A real SEGV
- will (theoretically) be caught by cygwin later in the process and
- will be sent as a cygwin-specific-signal. So, ignore SEGVs if
- they show up within the text segment of the DLL itself. */
- const char *fn;
- CORE_ADDR addr = (CORE_ADDR) (uintptr_t) rec->ExceptionAddress;
- if ((!cygwin_exceptions && (addr >= cygwin_load_start
- && addr < cygwin_load_end))
- || (find_pc_partial_function (addr, &fn, NULL, NULL)
- && startswith (fn, "KERNEL32!IsBad")))
- return true;
- #endif
- return false;
- }
- /* Resume thread specified by ID, or all artificially suspended
- threads, if we are continuing execution. KILLED non-zero means we
- have killed the inferior, so we should ignore weird errors due to
- threads shutting down. */
- static BOOL
- windows_continue (DWORD continue_status, int id, int killed)
- {
- BOOL res;
- windows_process.desired_stop_thread_id = id;
- if (windows_process.matching_pending_stop (debug_events))
- return TRUE;
- for (auto &th : thread_list)
- if (id == -1 || id == (int) th->tid)
- {
- #ifdef __x86_64__
- if (windows_process.wow64_process)
- {
- if (th->debug_registers_changed)
- {
- th->wow64_context.ContextFlags |= CONTEXT_DEBUG_REGISTERS;
- th->wow64_context.Dr0 = dr[0];
- th->wow64_context.Dr1 = dr[1];
- th->wow64_context.Dr2 = dr[2];
- th->wow64_context.Dr3 = dr[3];
- th->wow64_context.Dr6 = DR6_CLEAR_VALUE;
- th->wow64_context.Dr7 = dr[7];
- th->debug_registers_changed = false;
- }
- if (th->wow64_context.ContextFlags)
- {
- DWORD ec = 0;
- if (GetExitCodeThread (th->h, &ec)
- && ec == STILL_ACTIVE)
- {
- BOOL status = Wow64SetThreadContext (th->h,
- &th->wow64_context);
- if (!killed)
- CHECK (status);
- }
- th->wow64_context.ContextFlags = 0;
- }
- }
- else
- #endif
- {
- if (th->debug_registers_changed)
- {
- th->context.ContextFlags |= CONTEXT_DEBUG_REGISTERS;
- th->context.Dr0 = dr[0];
- th->context.Dr1 = dr[1];
- th->context.Dr2 = dr[2];
- th->context.Dr3 = dr[3];
- th->context.Dr6 = DR6_CLEAR_VALUE;
- th->context.Dr7 = dr[7];
- th->debug_registers_changed = false;
- }
- if (th->context.ContextFlags)
- {
- DWORD ec = 0;
- if (GetExitCodeThread (th->h, &ec)
- && ec == STILL_ACTIVE)
- {
- BOOL status = SetThreadContext (th->h, &th->context);
- if (!killed)
- CHECK (status);
- }
- th->context.ContextFlags = 0;
- }
- }
- th->resume ();
- }
- else
- {
- /* When single-stepping a specific thread, other threads must
- be suspended. */
- th->suspend ();
- }
- res = continue_last_debug_event (continue_status, debug_events);
- if (!res)
- error (_("Failed to resume program execution"
- " (ContinueDebugEvent failed, error %u)"),
- (unsigned int) GetLastError ());
- return res;
- }
- /* Called in pathological case where Windows fails to send a
- CREATE_PROCESS_DEBUG_EVENT after an attach. */
- static DWORD
- fake_create_process (void)
- {
- windows_process.handle
- = OpenProcess (PROCESS_ALL_ACCESS, FALSE,
- windows_process.current_event.dwProcessId);
- if (windows_process.handle != NULL)
- open_process_used = 1;
- else
- {
- error (_("OpenProcess call failed, GetLastError = %u"),
- (unsigned) GetLastError ());
- /* We can not debug anything in that case. */
- }
- windows_add_thread (ptid_t (windows_process.current_event.dwProcessId, 0,
- windows_process.current_event.dwThreadId),
- windows_process.current_event.u.CreateThread.hThread,
- windows_process.current_event.u.CreateThread.lpThreadLocalBase,
- true /* main_thread_p */);
- return windows_process.current_event.dwThreadId;
- }
- void
- windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
- {
- windows_thread_info *th;
- DWORD continue_status = DBG_CONTINUE;
- /* A specific PTID means `step only this thread id'. */
- int resume_all = ptid == minus_one_ptid;
- /* If we're continuing all threads, it's the current inferior that
- should be handled specially. */
- if (resume_all)
- ptid = inferior_ptid;
- if (sig != GDB_SIGNAL_0)
- {
- if (windows_process.current_event.dwDebugEventCode
- != EXCEPTION_DEBUG_EVENT)
- {
- DEBUG_EXCEPT ("Cannot continue with signal %d here.", sig);
- }
- else if (sig == windows_process.last_sig)
- continue_status = DBG_EXCEPTION_NOT_HANDLED;
- else
- #if 0
- /* This code does not seem to work, because
- the kernel does probably not consider changes in the ExceptionRecord
- structure when passing the exception to the inferior.
- Note that this seems possible in the exception handler itself. */
- {
- for (const xlate_exception &x : xlate)
- if (x.us == sig)
- {
- current_event.u.Exception.ExceptionRecord.ExceptionCode
- = x.them;
- continue_status = DBG_EXCEPTION_NOT_HANDLED;
- break;
- }
- if (continue_status == DBG_CONTINUE)
- {
- DEBUG_EXCEPT ("Cannot continue with signal %d.", sig);
- }
- }
- #endif
- DEBUG_EXCEPT ("Can only continue with received signal %d.",
- windows_process.last_sig);
- }
- windows_process.last_sig = GDB_SIGNAL_0;
- DEBUG_EXEC ("pid=%d, tid=0x%x, step=%d, sig=%d",
- ptid.pid (), (unsigned) ptid.lwp (), step, sig);
- /* Get context for currently selected thread. */
- th = windows_process.thread_rec (inferior_ptid, DONT_INVALIDATE_CONTEXT);
- if (th)
- {
- #ifdef __x86_64__
- if (windows_process.wow64_process)
- {
- if (step)
- {
- /* Single step by setting t bit. */
- struct regcache *regcache = get_current_regcache ();
- struct gdbarch *gdbarch = regcache->arch ();
- fetch_registers (regcache, gdbarch_ps_regnum (gdbarch));
- th->wow64_context.EFlags |= FLAG_TRACE_BIT;
- }
- if (th->wow64_context.ContextFlags)
- {
- if (th->debug_registers_changed)
- {
- th->wow64_context.Dr0 = dr[0];
- th->wow64_context.Dr1 = dr[1];
- th->wow64_context.Dr2 = dr[2];
- th->wow64_context.Dr3 = dr[3];
- th->wow64_context.Dr6 = DR6_CLEAR_VALUE;
- th->wow64_context.Dr7 = dr[7];
- th->debug_registers_changed = false;
- }
- CHECK (Wow64SetThreadContext (th->h, &th->wow64_context));
- th->wow64_context.ContextFlags = 0;
- }
- }
- else
- #endif
- {
- if (step)
- {
- /* Single step by setting t bit. */
- struct regcache *regcache = get_current_regcache ();
- struct gdbarch *gdbarch = regcache->arch ();
- fetch_registers (regcache, gdbarch_ps_regnum (gdbarch));
- th->context.EFlags |= FLAG_TRACE_BIT;
- }
- if (th->context.ContextFlags)
- {
- if (th->debug_registers_changed)
- {
- th->context.Dr0 = dr[0];
- th->context.Dr1 = dr[1];
- th->context.Dr2 = dr[2];
- th->context.Dr3 = dr[3];
- th->context.Dr6 = DR6_CLEAR_VALUE;
- th->context.Dr7 = dr[7];
- th->debug_registers_changed = false;
- }
- CHECK (SetThreadContext (th->h, &th->context));
- th->context.ContextFlags = 0;
- }
- }
- }
- /* Allow continuing with the same signal that interrupted us.
- Otherwise complain. */
- if (resume_all)
- windows_continue (continue_status, -1, 0);
- else
- windows_continue (continue_status, ptid.lwp (), 0);
- }
- /* Ctrl-C handler used when the inferior is not run in the same console. The
- handler is in charge of interrupting the inferior using DebugBreakProcess.
- Note that this function is not available prior to Windows XP. In this case
- we emit a warning. */
- static BOOL WINAPI
- ctrl_c_handler (DWORD event_type)
- {
- const int attach_flag = current_inferior ()->attach_flag;
- /* Only handle Ctrl-C and Ctrl-Break events. Ignore others. */
- if (event_type != CTRL_C_EVENT && event_type != CTRL_BREAK_EVENT)
- return FALSE;
- /* If the inferior and the debugger share the same console, do nothing as
- the inferior has also received the Ctrl-C event. */
- if (!new_console && !attach_flag)
- return TRUE;
- #ifdef __x86_64__
- if (windows_process.wow64_process)
- {
- /* Call DbgUiRemoteBreakin of the 32bit ntdll.dll in the target process.
- DebugBreakProcess would call the one of the 64bit ntdll.dll, which
- can't be correctly handled by gdb. */
- if (wow64_dbgbreak == nullptr)
- {
- CORE_ADDR addr;
- if (!find_minimal_symbol_address ("ntdll!DbgUiRemoteBreakin",
- &addr, 0))
- wow64_dbgbreak = (void *) addr;
- }
- if (wow64_dbgbreak != nullptr)
- {
- HANDLE thread = CreateRemoteThread (windows_process.handle, NULL,
- 0, (LPTHREAD_START_ROUTINE)
- wow64_dbgbreak, NULL, 0, NULL);
- if (thread)
- CloseHandle (thread);
- }
- }
- else
- #endif
- {
- if (!DebugBreakProcess (windows_process.handle))
- warning (_("Could not interrupt program. "
- "Press Ctrl-c in the program console."));
- }
- /* Return true to tell that Ctrl-C has been handled. */
- return TRUE;
- }
- /* Get the next event from the child. Returns a non-zero thread id if the event
- requires handling by WFI (or whatever). */
- int
- windows_nat_target::get_windows_debug_event (int pid,
- struct target_waitstatus *ourstatus)
- {
- BOOL debug_event;
- DWORD continue_status, event_code;
- DWORD thread_id = 0;
- /* If there is a relevant pending stop, report it now. See the
- comment by the definition of "pending_stops" for details on why
- this is needed. */
- gdb::optional<pending_stop> stop
- = windows_process.fetch_pending_stop (debug_events);
- if (stop.has_value ())
- {
- thread_id = stop->thread_id;
- *ourstatus = stop->status;
- ptid_t ptid (windows_process.current_event.dwProcessId, thread_id);
- windows_thread_info *th
- = windows_process.thread_rec (ptid, INVALIDATE_CONTEXT);
- th->reload_context = true;
- return thread_id;
- }
- windows_process.last_sig = GDB_SIGNAL_0;
- DEBUG_EVENT *current_event = &windows_process.current_event;
- if (!(debug_event = wait_for_debug_event (&windows_process.current_event,
- 1000)))
- goto out;
- continue_status = DBG_CONTINUE;
- event_code = windows_process.current_event.dwDebugEventCode;
- ourstatus->set_spurious ();
- have_saved_context = 0;
- switch (event_code)
- {
- case CREATE_THREAD_DEBUG_EVENT:
- DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s",
- (unsigned) current_event->dwProcessId,
- (unsigned) current_event->dwThreadId,
- "CREATE_THREAD_DEBUG_EVENT");
- if (saw_create != 1)
- {
- inferior *inf = find_inferior_pid (this, current_event->dwProcessId);
- if (!saw_create && inf->attach_flag)
- {
- /* Kludge around a Windows bug where first event is a create
- thread event. Caused when attached process does not have
- a main thread. */
- thread_id = fake_create_process ();
- if (thread_id)
- saw_create++;
- }
- break;
- }
- /* Record the existence of this thread. */
- thread_id = current_event->dwThreadId;
- windows_add_thread
- (ptid_t (current_event->dwProcessId, current_event->dwThreadId, 0),
- current_event->u.CreateThread.hThread,
- current_event->u.CreateThread.lpThreadLocalBase,
- false /* main_thread_p */);
- break;
- case EXIT_THREAD_DEBUG_EVENT:
- DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s",
- (unsigned) current_event->dwProcessId,
- (unsigned) current_event->dwThreadId,
- "EXIT_THREAD_DEBUG_EVENT");
- windows_delete_thread (ptid_t (current_event->dwProcessId,
- current_event->dwThreadId, 0),
- current_event->u.ExitThread.dwExitCode,
- false /* main_thread_p */);
- break;
- case CREATE_PROCESS_DEBUG_EVENT:
- DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s",
- (unsigned) current_event->dwProcessId,
- (unsigned) current_event->dwThreadId,
- "CREATE_PROCESS_DEBUG_EVENT");
- CloseHandle (current_event->u.CreateProcessInfo.hFile);
- if (++saw_create != 1)
- break;
- windows_process.handle = current_event->u.CreateProcessInfo.hProcess;
- /* Add the main thread. */
- windows_add_thread
- (ptid_t (current_event->dwProcessId,
- current_event->dwThreadId, 0),
- current_event->u.CreateProcessInfo.hThread,
- current_event->u.CreateProcessInfo.lpThreadLocalBase,
- true /* main_thread_p */);
- thread_id = current_event->dwThreadId;
- break;
- case EXIT_PROCESS_DEBUG_EVENT:
- DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s",
- (unsigned) current_event->dwProcessId,
- (unsigned) current_event->dwThreadId,
- "EXIT_PROCESS_DEBUG_EVENT");
- if (!windows_initialization_done)
- {
- target_terminal::ours ();
- target_mourn_inferior (inferior_ptid);
- error (_("During startup program exited with code 0x%x."),
- (unsigned int) current_event->u.ExitProcess.dwExitCode);
- }
- else if (saw_create == 1)
- {
- windows_delete_thread (ptid_t (current_event->dwProcessId,
- current_event->dwThreadId, 0),
- 0, true /* main_thread_p */);
- DWORD exit_status = current_event->u.ExitProcess.dwExitCode;
- /* If the exit status looks like a fatal exception, but we
- don't recognize the exception's code, make the original
- exit status value available, to avoid losing
- information. */
- int exit_signal
- = WIFSIGNALED (exit_status) ? WTERMSIG (exit_status) : -1;
- if (exit_signal == -1)
- ourstatus->set_exited (exit_status);
- else
- ourstatus->set_signalled (gdb_signal_from_host (exit_signal));
- thread_id = current_event->dwThreadId;
- }
- break;
- case LOAD_DLL_DEBUG_EVENT:
- DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s",
- (unsigned) current_event->dwProcessId,
- (unsigned) current_event->dwThreadId,
- "LOAD_DLL_DEBUG_EVENT");
- CloseHandle (current_event->u.LoadDll.hFile);
- if (saw_create != 1 || ! windows_initialization_done)
- break;
- try
- {
- windows_process.dll_loaded_event ();
- }
- catch (const gdb_exception &ex)
- {
- exception_print (gdb_stderr, ex);
- }
- ourstatus->set_loaded ();
- thread_id = current_event->dwThreadId;
- break;
- case UNLOAD_DLL_DEBUG_EVENT:
- DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s",
- (unsigned) current_event->dwProcessId,
- (unsigned) current_event->dwThreadId,
- "UNLOAD_DLL_DEBUG_EVENT");
- if (saw_create != 1 || ! windows_initialization_done)
- break;
- try
- {
- windows_process.handle_unload_dll ();
- }
- catch (const gdb_exception &ex)
- {
- exception_print (gdb_stderr, ex);
- }
- ourstatus->set_loaded ();
- thread_id = current_event->dwThreadId;
- break;
- case EXCEPTION_DEBUG_EVENT:
- DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s",
- (unsigned) current_event->dwProcessId,
- (unsigned) current_event->dwThreadId,
- "EXCEPTION_DEBUG_EVENT");
- if (saw_create != 1)
- break;
- switch (windows_process.handle_exception (ourstatus, debug_exceptions))
- {
- case HANDLE_EXCEPTION_UNHANDLED:
- default:
- continue_status = DBG_EXCEPTION_NOT_HANDLED;
- break;
- case HANDLE_EXCEPTION_HANDLED:
- thread_id = current_event->dwThreadId;
- break;
- case HANDLE_EXCEPTION_IGNORED:
- continue_status = DBG_CONTINUE;
- break;
- }
- break;
- case OUTPUT_DEBUG_STRING_EVENT: /* Message from the kernel. */
- DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s",
- (unsigned) current_event->dwProcessId,
- (unsigned) current_event->dwThreadId,
- "OUTPUT_DEBUG_STRING_EVENT");
- if (saw_create != 1)
- break;
- thread_id = windows_process.handle_output_debug_string (ourstatus);
- break;
- default:
- if (saw_create != 1)
- break;
- gdb_printf ("gdb: kernel event for pid=%u tid=0x%x\n",
- (unsigned) current_event->dwProcessId,
- (unsigned) current_event->dwThreadId);
- gdb_printf (" unknown event code %u\n",
- (unsigned) current_event->dwDebugEventCode);
- break;
- }
- if (!thread_id || saw_create != 1)
- {
- CHECK (windows_continue (continue_status,
- windows_process.desired_stop_thread_id, 0));
- }
- else if (windows_process.desired_stop_thread_id != -1
- && windows_process.desired_stop_thread_id != thread_id)
- {
- /* Pending stop. See the comment by the definition of
- "pending_stops" for details on why this is needed. */
- DEBUG_EVENTS ("get_windows_debug_event - "
- "unexpected stop in 0x%x (expecting 0x%x)",
- thread_id, windows_process.desired_stop_thread_id);
- if (current_event->dwDebugEventCode == EXCEPTION_DEBUG_EVENT
- && ((current_event->u.Exception.ExceptionRecord.ExceptionCode
- == EXCEPTION_BREAKPOINT)
- || (current_event->u.Exception.ExceptionRecord.ExceptionCode
- == STATUS_WX86_BREAKPOINT))
- && windows_initialization_done)
- {
- ptid_t ptid = ptid_t (current_event->dwProcessId, thread_id, 0);
- windows_thread_info *th
- = windows_process.thread_rec (ptid, INVALIDATE_CONTEXT);
- th->stopped_at_software_breakpoint = true;
- th->pc_adjusted = false;
- }
- windows_process.pending_stops.push_back
- ({thread_id, *ourstatus, windows_process.current_event});
- thread_id = 0;
- CHECK (windows_continue (continue_status,
- windows_process.desired_stop_thread_id, 0));
- }
- out:
- return thread_id;
- }
- /* Wait for interesting events to occur in the target process. */
- ptid_t
- windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
- target_wait_flags options)
- {
- int pid = -1;
- /* We loop when we get a non-standard exception rather than return
- with a SPURIOUS because resume can try and step or modify things,
- which needs a current_thread->h. But some of these exceptions mark
- the birth or death of threads, which mean that the current thread
- isn't necessarily what you think it is. */
- while (1)
- {
- int retval;
- /* If the user presses Ctrl-c while the debugger is waiting
- for an event, he expects the debugger to interrupt his program
- and to get the prompt back. There are two possible situations:
- - The debugger and the program do not share the console, in
- which case the Ctrl-c event only reached the debugger.
- In that case, the ctrl_c handler will take care of interrupting
- the inferior. Note that this case is working starting with
- Windows XP. For Windows 2000, Ctrl-C should be pressed in the
- inferior console.
- - The debugger and the program share the same console, in which
- case both debugger and inferior will receive the Ctrl-c event.
- In that case the ctrl_c handler will ignore the event, as the
- Ctrl-c event generated inside the inferior will trigger the
- expected debug event.
- FIXME: brobecker/2008-05-20: If the inferior receives the
- signal first and the delay until GDB receives that signal
- is sufficiently long, GDB can sometimes receive the SIGINT
- after we have unblocked the CTRL+C handler. This would
- lead to the debugger stopping prematurely while handling
- the new-thread event that comes with the handling of the SIGINT
- inside the inferior, and then stop again immediately when
- the user tries to resume the execution in the inferior.
- This is a classic race that we should try to fix one day. */
- SetConsoleCtrlHandler (&ctrl_c_handler, TRUE);
- retval = get_windows_debug_event (pid, ourstatus);
- SetConsoleCtrlHandler (&ctrl_c_handler, FALSE);
- if (retval)
- {
- ptid_t result = ptid_t (windows_process.current_event.dwProcessId,
- retval, 0);
- if (ourstatus->kind () != TARGET_WAITKIND_EXITED
- && ourstatus->kind () != TARGET_WAITKIND_SIGNALLED)
- {
- windows_thread_info *th
- = windows_process.thread_rec (result, INVALIDATE_CONTEXT);
- if (th != nullptr)
- {
- th->stopped_at_software_breakpoint = false;
- if (windows_process.current_event.dwDebugEventCode
- == EXCEPTION_DEBUG_EVENT
- && ((windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode
- == EXCEPTION_BREAKPOINT)
- || (windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode
- == STATUS_WX86_BREAKPOINT))
- && windows_initialization_done)
- {
- th->stopped_at_software_breakpoint = true;
- th->pc_adjusted = false;
- }
- }
- }
- return result;
- }
- else
- {
- int detach = 0;
- if (deprecated_ui_loop_hook != NULL)
- detach = deprecated_ui_loop_hook (0);
- if (detach)
- kill ();
- }
- }
- }
- void
- windows_nat_target::do_initial_windows_stuff (DWORD pid, bool attaching)
- {
- int i;
- struct inferior *inf;
- windows_process.last_sig = GDB_SIGNAL_0;
- open_process_used = 0;
- for (i = 0; i < sizeof (dr) / sizeof (dr[0]); i++)
- dr[i] = 0;
- #ifdef __CYGWIN__
- cygwin_load_start = cygwin_load_end = 0;
- #endif
- windows_process.current_event.dwProcessId = pid;
- memset (&windows_process.current_event, 0,
- sizeof (windows_process.current_event));
- inf = current_inferior ();
- if (!inf->target_is_pushed (this))
- inf->push_target (this);
- disable_breakpoints_in_shlibs ();
- windows_clear_solib ();
- clear_proceed_status (0);
- init_wait_for_inferior ();
- #ifdef __x86_64__
- windows_process.ignore_first_breakpoint
- = !attaching && windows_process.wow64_process;
- if (!windows_process.wow64_process)
- {
- windows_set_context_register_offsets (amd64_mappings);
- windows_set_segment_register_p (amd64_windows_segment_register_p);
- }
- else
- #endif
- {
- windows_set_context_register_offsets (i386_mappings);
- windows_set_segment_register_p (i386_windows_segment_register_p);
- }
- inferior_appeared (inf, pid);
- inf->attach_flag = attaching;
- target_terminal::init ();
- target_terminal::inferior ();
- windows_initialization_done = 0;
- ptid_t last_ptid;
- while (1)
- {
- struct target_waitstatus status;
- last_ptid = this->wait (minus_one_ptid, &status, 0);
- /* Note windows_wait returns TARGET_WAITKIND_SPURIOUS for thread
- events. */
- if (status.kind () != TARGET_WAITKIND_LOADED
- && status.kind () != TARGET_WAITKIND_SPURIOUS)
- break;
- this->resume (minus_one_ptid, 0, GDB_SIGNAL_0);
- }
- switch_to_thread (find_thread_ptid (this, last_ptid));
- /* Now that the inferior has been started and all DLLs have been mapped,
- we can iterate over all DLLs and load them in.
- We avoid doing it any earlier because, on certain versions of Windows,
- LOAD_DLL_DEBUG_EVENTs are sometimes not complete. In particular,
- we have seen on Windows 8.1 that the ntdll.dll load event does not
- include the DLL name, preventing us from creating an associated SO.
- A possible explanation is that ntdll.dll might be mapped before
- the SO info gets created by the Windows system -- ntdll.dll is
- the first DLL to be reported via LOAD_DLL_DEBUG_EVENT and other DLLs
- do not seem to suffer from that problem.
- Rather than try to work around this sort of issue, it is much
- simpler to just ignore DLL load/unload events during the startup
- phase, and then process them all in one batch now. */
- windows_process.add_all_dlls ();
- windows_initialization_done = 1;
- return;
- }
- /* Try to set or remove a user privilege to the current process. Return -1
- if that fails, the previous setting of that privilege otherwise.
- This code is copied from the Cygwin source code and rearranged to allow
- dynamically loading of the needed symbols from advapi32 which is only
- available on NT/2K/XP. */
- static int
- set_process_privilege (const char *privilege, BOOL enable)
- {
- HANDLE token_hdl = NULL;
- LUID restore_priv;
- TOKEN_PRIVILEGES new_priv, orig_priv;
- int ret = -1;
- DWORD size;
- if (!OpenProcessToken (GetCurrentProcess (),
- TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
- &token_hdl))
- goto out;
- if (!LookupPrivilegeValueA (NULL, privilege, &restore_priv))
- goto out;
- new_priv.PrivilegeCount = 1;
- new_priv.Privileges[0].Luid = restore_priv;
- new_priv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
- if (!AdjustTokenPrivileges (token_hdl, FALSE, &new_priv,
- sizeof orig_priv, &orig_priv, &size))
- goto out;
- #if 0
- /* Disabled, otherwise every `attach' in an unprivileged user session
- would raise the "Failed to get SE_DEBUG_NAME privilege" warning in
- windows_attach(). */
- /* AdjustTokenPrivileges returns TRUE even if the privilege could not
- be enabled. GetLastError () returns an correct error code, though. */
- if (enable && GetLastError () == ERROR_NOT_ALL_ASSIGNED)
- goto out;
- #endif
- ret = orig_priv.Privileges[0].Attributes == SE_PRIVILEGE_ENABLED ? 1 : 0;
- out:
- if (token_hdl)
- CloseHandle (token_hdl);
- return ret;
- }
- /* Attach to process PID, then initialize for debugging it. */
- void
- windows_nat_target::attach (const char *args, int from_tty)
- {
- BOOL ok;
- DWORD pid;
- pid = parse_pid_to_attach (args);
- if (set_process_privilege (SE_DEBUG_NAME, TRUE) < 0)
- warning ("Failed to get SE_DEBUG_NAME privilege\n"
- "This can cause attach to fail on Windows NT/2K/XP");
- windows_init_thread_list ();
- ok = DebugActiveProcess (pid);
- saw_create = 0;
- #ifdef __CYGWIN__
- if (!ok)
- {
- /* Try fall back to Cygwin pid. */
- pid = cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid);
- if (pid > 0)
- ok = DebugActiveProcess (pid);
- }
- #endif
- if (!ok)
- error (_("Can't attach to process %u (error %u)"),
- (unsigned) pid, (unsigned) GetLastError ());
- DebugSetProcessKillOnExit (FALSE);
- target_announce_attach (from_tty, pid);
- #ifdef __x86_64__
- HANDLE h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, pid);
- if (h != NULL)
- {
- BOOL wow64;
- if (IsWow64Process (h, &wow64))
- windows_process.wow64_process = wow64;
- CloseHandle (h);
- }
- #endif
- do_initial_windows_stuff (pid, 1);
- target_terminal::ours ();
- }
- void
- windows_nat_target::detach (inferior *inf, int from_tty)
- {
- int detached = 1;
- ptid_t ptid = minus_one_ptid;
- resume (ptid, 0, GDB_SIGNAL_0);
- if (!DebugActiveProcessStop (windows_process.current_event.dwProcessId))
- {
- error (_("Can't detach process %u (error %u)"),
- (unsigned) windows_process.current_event.dwProcessId,
- (unsigned) GetLastError ());
- detached = 0;
- }
- DebugSetProcessKillOnExit (FALSE);
- if (detached)
- target_announce_detach (from_tty);
- x86_cleanup_dregs ();
- switch_to_no_thread ();
- detach_inferior (inf);
- maybe_unpush_target ();
- }
- /* Try to determine the executable filename.
- EXE_NAME_RET is a pointer to a buffer whose size is EXE_NAME_MAX_LEN.
- Upon success, the filename is stored inside EXE_NAME_RET, and
- this function returns nonzero.
- Otherwise, this function returns zero and the contents of
- EXE_NAME_RET is undefined. */
- static int
- windows_get_exec_module_filename (char *exe_name_ret, size_t exe_name_max_len)
- {
- DWORD len;
- HMODULE dh_buf;
- DWORD cbNeeded;
- cbNeeded = 0;
- #ifdef __x86_64__
- if (windows_process.wow64_process)
- {
- if (!EnumProcessModulesEx (windows_process.handle,
- &dh_buf, sizeof (HMODULE), &cbNeeded,
- LIST_MODULES_32BIT)
- || !cbNeeded)
- return 0;
- }
- else
- #endif
- {
- if (!EnumProcessModules (windows_process.handle,
- &dh_buf, sizeof (HMODULE), &cbNeeded)
- || !cbNeeded)
- return 0;
- }
- /* We know the executable is always first in the list of modules,
- which we just fetched. So no need to fetch more. */
- #ifdef __CYGWIN__
- {
- /* Cygwin prefers that the path be in /x/y/z format, so extract
- the filename into a temporary buffer first, and then convert it
- to POSIX format into the destination buffer. */
- cygwin_buf_t *pathbuf = (cygwin_buf_t *) alloca (exe_name_max_len * sizeof (cygwin_buf_t));
- len = GetModuleFileNameEx (current_process_handle,
- dh_buf, pathbuf, exe_name_max_len);
- if (len == 0)
- error (_("Error getting executable filename: %u."),
- (unsigned) GetLastError ());
- if (cygwin_conv_path (CCP_WIN_W_TO_POSIX, pathbuf, exe_name_ret,
- exe_name_max_len) < 0)
- error (_("Error converting executable filename to POSIX: %d."), errno);
- }
- #else
- len = GetModuleFileNameEx (windows_process.handle,
- dh_buf, exe_name_ret, exe_name_max_len);
- if (len == 0)
- error (_("Error getting executable filename: %u."),
- (unsigned) GetLastError ());
- #endif
- return 1; /* success */
- }
- /* The pid_to_exec_file target_ops method for this platform. */
- char *
- windows_nat_target::pid_to_exec_file (int pid)
- {
- static char path[__PMAX];
- #ifdef __CYGWIN__
- /* Try to find exe name as symlink target of /proc/<pid>/exe. */
- int nchars;
- char procexe[sizeof ("/proc/4294967295/exe")];
- xsnprintf (procexe, sizeof (procexe), "/proc/%u/exe", pid);
- nchars = readlink (procexe, path, sizeof(path));
- if (nchars > 0 && nchars < sizeof (path))
- {
- path[nchars] = '\0'; /* Got it */
- return path;
- }
- #endif
- /* If we get here then either Cygwin is hosed, this isn't a Cygwin version
- of gdb, or we're trying to debug a non-Cygwin windows executable. */
- if (!windows_get_exec_module_filename (path, sizeof (path)))
- path[0] = '\0';
- return path;
- }
- /* Print status information about what we're accessing. */
- void
- windows_nat_target::files_info ()
- {
- struct inferior *inf = current_inferior ();
- gdb_printf ("\tUsing the running image of %s %s.\n",
- inf->attach_flag ? "attached" : "child",
- target_pid_to_str (inferior_ptid).c_str ());
- }
- /* Modify CreateProcess parameters for use of a new separate console.
- Parameters are:
- *FLAGS: DWORD parameter for general process creation flags.
- *SI: STARTUPINFO structure, for which the console window size and
- console buffer size is filled in if GDB is running in a console.
- to create the new console.
- The size of the used font is not available on all versions of
- Windows OS. Furthermore, the current font might not be the default
- font, but this is still better than before.
- If the windows and buffer sizes are computed,
- SI->DWFLAGS is changed so that this information is used
- by CreateProcess function. */
- static void
- windows_set_console_info (STARTUPINFO *si, DWORD *flags)
- {
- HANDLE hconsole = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
- if (hconsole != INVALID_HANDLE_VALUE)
- {
- CONSOLE_SCREEN_BUFFER_INFO sbinfo;
- COORD font_size;
- CONSOLE_FONT_INFO cfi;
- GetCurrentConsoleFont (hconsole, FALSE, &cfi);
- font_size = GetConsoleFontSize (hconsole, cfi.nFont);
- GetConsoleScreenBufferInfo(hconsole, &sbinfo);
- si->dwXSize = sbinfo.srWindow.Right - sbinfo.srWindow.Left + 1;
- si->dwYSize = sbinfo.srWindow.Bottom - sbinfo.srWindow.Top + 1;
- if (font_size.X)
- si->dwXSize *= font_size.X;
- else
- si->dwXSize *= 8;
- if (font_size.Y)
- si->dwYSize *= font_size.Y;
- else
- si->dwYSize *= 12;
- si->dwXCountChars = sbinfo.dwSize.X;
- si->dwYCountChars = sbinfo.dwSize.Y;
- si->dwFlags |= STARTF_USESIZE | STARTF_USECOUNTCHARS;
- }
- *flags |= CREATE_NEW_CONSOLE;
- }
- #ifndef __CYGWIN__
- /* Function called by qsort to sort environment strings. */
- static int
- envvar_cmp (const void *a, const void *b)
- {
- const char **p = (const char **) a;
- const char **q = (const char **) b;
- return strcasecmp (*p, *q);
- }
- #endif
- #ifdef __CYGWIN__
- static void
- clear_win32_environment (char **env)
- {
- int i;
- size_t len;
- wchar_t *copy = NULL, *equalpos;
- for (i = 0; env[i] && *env[i]; i++)
- {
- len = mbstowcs (NULL, env[i], 0) + 1;
- copy = (wchar_t *) xrealloc (copy, len * sizeof (wchar_t));
- mbstowcs (copy, env[i], len);
- equalpos = wcschr (copy, L'=');
- if (equalpos)
- *equalpos = L'\0';
- SetEnvironmentVariableW (copy, NULL);
- }
- xfree (copy);
- }
- #endif
- #ifndef __CYGWIN__
- /* Redirection of inferior I/O streams for native MS-Windows programs.
- Unlike on Unix, where this is handled by invoking the inferior via
- the shell, on MS-Windows we need to emulate the cmd.exe shell.
- The official documentation of the cmd.exe redirection features is here:
- http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx
- (That page talks about Windows XP, but there's no newer
- documentation, so we assume later versions of cmd.exe didn't change
- anything.)
- Caveat: the documentation on that page seems to include a few lies.
- For example, it describes strange constructs 1<&2 and 2<&1, which
- seem to work only when 1>&2 resp. 2>&1 would make sense, and so I
- think the cmd.exe parser of the redirection symbols simply doesn't
- care about the < vs > distinction in these cases. Therefore, the
- supported features are explicitly documented below.
- The emulation below aims at supporting all the valid use cases
- supported by cmd.exe, which include:
- < FILE redirect standard input from FILE
- 0< FILE redirect standard input from FILE
- <&N redirect standard input from file descriptor N
- 0<&N redirect standard input from file descriptor N
- > FILE redirect standard output to FILE
- >> FILE append standard output to FILE
- 1>> FILE append standard output to FILE
- >&N redirect standard output to file descriptor N
- 1>&N redirect standard output to file descriptor N
- >>&N append standard output to file descriptor N
- 1>>&N append standard output to file descriptor N
- 2> FILE redirect standard error to FILE
- 2>> FILE append standard error to FILE
- 2>&N redirect standard error to file descriptor N
- 2>>&N append standard error to file descriptor N
- Note that using N > 2 in the above construct is supported, but
- requires that the corresponding file descriptor be open by some
- means elsewhere or outside GDB. Also note that using ">&0" or
- "<&2" will generally fail, because the file descriptor redirected
- from is normally open in an incompatible mode (e.g., FD 0 is open
- for reading only). IOW, use of such tricks is not recommended;
- you are on your own.
- We do NOT support redirection of file descriptors above 2, as in
- "3>SOME-FILE", because MinGW compiled programs don't (supporting
- that needs special handling in the startup code that MinGW
- doesn't have). Pipes are also not supported.
- As for invalid use cases, where the redirection contains some
- error, the emulation below will detect that and produce some
- error and/or failure. But the behavior in those cases is not
- bug-for-bug compatible with what cmd.exe does in those cases.
- That's because what cmd.exe does then is not well defined, and
- seems to be a side effect of the cmd.exe parsing of the command
- line more than anything else. For example, try redirecting to an
- invalid file name, as in "> foo:bar".
- There are also minor syntactic deviations from what cmd.exe does
- in some corner cases. For example, it doesn't support the likes
- of "> &foo" to mean redirect to file named literally "&foo"; we
- do support that here, because that, too, sounds like some issue
- with the cmd.exe parser. Another nicety is that we support
- redirection targets that use file names with forward slashes,
- something cmd.exe doesn't -- this comes in handy since GDB
- file-name completion can be used when typing the command line for
- the inferior. */
- /* Support routines for redirecting standard handles of the inferior. */
- /* Parse a single redirection spec, open/duplicate the specified
- file/fd, and assign the appropriate value to one of the 3 standard
- file descriptors. */
- static int
- redir_open (const char *redir_string, int *inp, int *out, int *err)
- {
- int *fd, ref_fd = -2;
- int mode;
- const char *fname = redir_string + 1;
- int rc = *redir_string;
- switch (rc)
- {
- case '0':
- fname++;
- /* FALLTHROUGH */
- case '<':
- fd = inp;
- mode = O_RDONLY;
- break;
- case '1': case '2':
- fname++;
- /* FALLTHROUGH */
- case '>':
- fd = (rc == '2') ? err : out;
- mode = O_WRONLY | O_CREAT;
- if (*fname == '>')
- {
- fname++;
- mode |= O_APPEND;
- }
- else
- mode |= O_TRUNC;
- break;
- default:
- return -1;
- }
- if (*fname == '&' && '0' <= fname[1] && fname[1] <= '9')
- {
- /* A reference to a file descriptor. */
- char *fdtail;
- ref_fd = (int) strtol (fname + 1, &fdtail, 10);
- if (fdtail > fname + 1 && *fdtail == '\0')
- {
- /* Don't allow redirection when open modes are incompatible. */
- if ((ref_fd == 0 && (fd == out || fd == err))
- || ((ref_fd == 1 || ref_fd == 2) && fd == inp))
- {
- errno = EPERM;
- return -1;
- }
- if (ref_fd == 0)
- ref_fd = *inp;
- else if (ref_fd == 1)
- ref_fd = *out;
- else if (ref_fd == 2)
- ref_fd = *err;
- }
- else
- {
- errno = EBADF;
- return -1;
- }
- }
- else
- fname++; /* skip the separator space */
- /* If the descriptor is already open, close it. This allows
- multiple specs of redirections for the same stream, which is
- somewhat nonsensical, but still valid and supported by cmd.exe.
- (But cmd.exe only opens a single file in this case, the one
- specified by the last redirection spec on the command line.) */
- if (*fd >= 0)
- _close (*fd);
- if (ref_fd == -2)
- {
- *fd = _open (fname, mode, _S_IREAD | _S_IWRITE);
- if (*fd < 0)
- return -1;
- }
- else if (ref_fd == -1)
- *fd = -1; /* reset to default destination */
- else
- {
- *fd = _dup (ref_fd);
- if (*fd < 0)
- return -1;
- }
- /* _open just sets a flag for O_APPEND, which won't be passed to the
- inferior, so we need to actually move the file pointer. */
- if ((mode & O_APPEND) != 0)
- _lseek (*fd, 0L, SEEK_END);
- return 0;
- }
- /* Canonicalize a single redirection spec and set up the corresponding
- file descriptor as specified. */
- static int
- redir_set_redirection (const char *s, int *inp, int *out, int *err)
- {
- char buf[__PMAX + 2 + 5]; /* extra space for quotes & redirection string */
- char *d = buf;
- const char *start = s;
- int quote = 0;
- *d++ = *s++; /* copy the 1st character, < or > or a digit */
- if ((*start == '>' || *start == '1' || *start == '2')
- && *s == '>')
- {
- *d++ = *s++;
- if (*s == '>' && *start != '>')
- *d++ = *s++;
- }
- else if (*start == '0' && *s == '<')
- *d++ = *s++;
- /* cmd.exe recognizes "&N" only immediately after the redirection symbol. */
- if (*s != '&')
- {
- while (isspace (*s)) /* skip whitespace before file name */
- s++;
- *d++ = ' '; /* separate file name with a single space */
- }
- /* Copy the file name. */
- while (*s)
- {
- /* Remove quoting characters from the file name in buf[]. */
- if (*s == '"') /* could support '..' quoting here */
- {
- if (!quote)
- quote = *s++;
- else if (*s == quote)
- {
- quote = 0;
- s++;
- }
- else
- *d++ = *s++;
- }
- else if (*s == '\\')
- {
- if (s[1] == '"') /* could support '..' here */
- s++;
- *d++ = *s++;
- }
- else if (isspace (*s) && !quote)
- break;
- else
- *d++ = *s++;
- if (d - buf >= sizeof (buf) - 1)
- {
- errno = ENAMETOOLONG;
- return 0;
- }
- }
- *d = '\0';
- /* Windows doesn't allow redirection characters in file names, so we
- can bail out early if they use them, or if there's no target file
- name after the redirection symbol. */
- if (d[-1] == '>' || d[-1] == '<')
- {
- errno = ENOENT;
- return 0;
- }
- if (redir_open (buf, inp, out, err) == 0)
- return s - start;
- return 0;
- }
- /* Parse the command line for redirection specs and prepare the file
- descriptors for the 3 standard streams accordingly. */
- static bool
- redirect_inferior_handles (const char *cmd_orig, char *cmd,
- int *inp, int *out, int *err)
- {
- const char *s = cmd_orig;
- char *d = cmd;
- int quote = 0;
- bool retval = false;
- while (isspace (*s))
- *d++ = *s++;
- while (*s)
- {
- if (*s == '"') /* could also support '..' quoting here */
- {
- if (!quote)
- quote = *s;
- else if (*s == quote)
- quote = 0;
- }
- else if (*s == '\\')
- {
- if (s[1] == '"') /* escaped quote char */
- s++;
- }
- else if (!quote)
- {
- /* Process a single redirection candidate. */
- if (*s == '<' || *s == '>'
- || ((*s == '1' || *s == '2') && s[1] == '>')
- || (*s == '0' && s[1] == '<'))
- {
- int skip = redir_set_redirection (s, inp, out, err);
- if (skip <= 0)
- return false;
- retval = true;
- s += skip;
- }
- }
- if (*s)
- *d++ = *s++;
- }
- *d = '\0';
- return retval;
- }
- #endif /* !__CYGWIN__ */
- /* Start an inferior windows child process and sets inferior_ptid to its pid.
- EXEC_FILE is the file to run.
- ALLARGS is a string containing the arguments to the program.
- ENV is the environment vector to pass. Errors reported with error(). */
- void
- windows_nat_target::create_inferior (const char *exec_file,
- const std::string &origallargs,
- char **in_env, int from_tty)
- {
- STARTUPINFO si;
- #ifdef __CYGWIN__
- cygwin_buf_t real_path[__PMAX];
- cygwin_buf_t shell[__PMAX]; /* Path to shell */
- cygwin_buf_t infcwd[__PMAX];
- const char *sh;
- cygwin_buf_t *toexec;
- cygwin_buf_t *cygallargs;
- cygwin_buf_t *args;
- char **old_env = NULL;
- PWCHAR w32_env;
- size_t len;
- int tty;
- int ostdin, ostdout, ostderr;
- #else /* !__CYGWIN__ */
- char shell[__PMAX]; /* Path to shell */
- const char *toexec;
- char *args, *allargs_copy;
- size_t args_len, allargs_len;
- int fd_inp = -1, fd_out = -1, fd_err = -1;
- HANDLE tty = INVALID_HANDLE_VALUE;
- bool redirected = false;
- char *w32env;
- char *temp;
- size_t envlen;
- int i;
- size_t envsize;
- char **env;
- #endif /* !__CYGWIN__ */
- const char *allargs = origallargs.c_str ();
- PROCESS_INFORMATION pi;
- BOOL ret;
- DWORD flags = 0;
- const std::string &inferior_tty = current_inferior ()->tty ();
- if (!exec_file)
- error (_("No executable specified, use `target exec'."));
- const char *inferior_cwd = current_inferior ()->cwd ().c_str ();
- std::string expanded_infcwd;
- if (*inferior_cwd == '\0')
- inferior_cwd = nullptr;
- else
- {
- expanded_infcwd = gdb_tilde_expand (inferior_cwd);
- /* Mirror slashes on inferior's cwd. */
- std::replace (expanded_infcwd.begin (), expanded_infcwd.end (),
- '/', '\\');
- inferior_cwd = expanded_infcwd.c_str ();
- }
- memset (&si, 0, sizeof (si));
- si.cb = sizeof (si);
- if (new_group)
- flags |= CREATE_NEW_PROCESS_GROUP;
- if (new_console)
- windows_set_console_info (&si, &flags);
- #ifdef __CYGWIN__
- if (!useshell)
- {
- flags |= DEBUG_ONLY_THIS_PROCESS;
- if (cygwin_conv_path (CCP_POSIX_TO_WIN_W, exec_file, real_path,
- __PMAX * sizeof (cygwin_buf_t)) < 0)
- error (_("Error starting executable: %d"), errno);
- toexec = real_path;
- #ifdef __USEWIDE
- len = mbstowcs (NULL, allargs, 0) + 1;
- if (len == (size_t) -1)
- error (_("Error starting executable: %d"), errno);
- cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t));
- mbstowcs (cygallargs, allargs, len);
- #else /* !__USEWIDE */
- cygallargs = allargs;
- #endif
- }
- else
- {
- sh = get_shell ();
- if (cygwin_conv_path (CCP_POSIX_TO_WIN_W, sh, shell, __PMAX) < 0)
- error (_("Error starting executable via shell: %d"), errno);
- #ifdef __USEWIDE
- len = sizeof (L" -c 'exec '") + mbstowcs (NULL, exec_file, 0)
- + mbstowcs (NULL, allargs, 0) + 2;
- cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t));
- swprintf (cygallargs, len, L" -c 'exec %s %s'", exec_file, allargs);
- #else /* !__USEWIDE */
- len = (sizeof (" -c 'exec '") + strlen (exec_file)
- + strlen (allargs) + 2);
- cygallargs = (char *) alloca (len);
- xsnprintf (cygallargs, len, " -c 'exec %s %s'", exec_file, allargs);
- #endif /* __USEWIDE */
- toexec = shell;
- flags |= DEBUG_PROCESS;
- }
- if (inferior_cwd != NULL
- && cygwin_conv_path (CCP_POSIX_TO_WIN_W, inferior_cwd,
- infcwd, strlen (inferior_cwd)) < 0)
- error (_("Error converting inferior cwd: %d"), errno);
- #ifdef __USEWIDE
- args = (cygwin_buf_t *) alloca ((wcslen (toexec) + wcslen (cygallargs) + 2)
- * sizeof (wchar_t));
- wcscpy (args, toexec);
- wcscat (args, L" ");
- wcscat (args, cygallargs);
- #else /* !__USEWIDE */
- args = (cygwin_buf_t *) alloca (strlen (toexec) + strlen (cygallargs) + 2);
- strcpy (args, toexec);
- strcat (args, " ");
- strcat (args, cygallargs);
- #endif /* !__USEWIDE */
- #ifdef CW_CVT_ENV_TO_WINENV
- /* First try to create a direct Win32 copy of the POSIX environment. */
- w32_env = (PWCHAR) cygwin_internal (CW_CVT_ENV_TO_WINENV, in_env);
- if (w32_env != (PWCHAR) -1)
- flags |= CREATE_UNICODE_ENVIRONMENT;
- else
- /* If that fails, fall back to old method tweaking GDB's environment. */
- #endif /* CW_CVT_ENV_TO_WINENV */
- {
- /* Reset all Win32 environment variables to avoid leftover on next run. */
- clear_win32_environment (environ);
- /* Prepare the environment vars for CreateProcess. */
- old_env = environ;
- environ = in_env;
- cygwin_internal (CW_SYNC_WINENV);
- w32_env = NULL;
- }
- if (inferior_tty.empty ())
- tty = ostdin = ostdout = ostderr = -1;
- else
- {
- tty = open (inferior_tty.c_str (), O_RDWR | O_NOCTTY);
- if (tty < 0)
- {
- print_sys_errmsg (inferior_tty.c_str (), errno);
- ostdin = ostdout = ostderr = -1;
- }
- else
- {
- ostdin = dup (0);
- ostdout = dup (1);
- ostderr = dup (2);
- dup2 (tty, 0);
- dup2 (tty, 1);
- dup2 (tty, 2);
- }
- }
- windows_init_thread_list ();
- ret = CreateProcess (0,
- args, /* command line */
- NULL, /* Security */
- NULL, /* thread */
- TRUE, /* inherit handles */
- flags, /* start flags */
- w32_env, /* environment */
- inferior_cwd != NULL ? infcwd : NULL, /* current
- directory */
- &si,
- &pi);
- if (w32_env)
- /* Just free the Win32 environment, if it could be created. */
- free (w32_env);
- else
- {
- /* Reset all environment variables to avoid leftover on next run. */
- clear_win32_environment (in_env);
- /* Restore normal GDB environment variables. */
- environ = old_env;
- cygwin_internal (CW_SYNC_WINENV);
- }
- if (tty >= 0)
- {
- ::close (tty);
- dup2 (ostdin, 0);
- dup2 (ostdout, 1);
- dup2 (ostderr, 2);
- ::close (ostdin);
- ::close (ostdout);
- ::close (ostderr);
- }
- #else /* !__CYGWIN__ */
- allargs_len = strlen (allargs);
- allargs_copy = strcpy ((char *) alloca (allargs_len + 1), allargs);
- if (strpbrk (allargs_copy, "<>") != NULL)
- {
- int e = errno;
- errno = 0;
- redirected =
- redirect_inferior_handles (allargs, allargs_copy,
- &fd_inp, &fd_out, &fd_err);
- if (errno)
- warning (_("Error in redirection: %s."), safe_strerror (errno));
- else
- errno = e;
- allargs_len = strlen (allargs_copy);
- }
- /* If not all the standard streams are redirected by the command
- line, use INFERIOR_TTY for those which aren't. */
- if (!inferior_tty.empty ()
- && !(fd_inp >= 0 && fd_out >= 0 && fd_err >= 0))
- {
- SECURITY_ATTRIBUTES sa;
- sa.nLength = sizeof(sa);
- sa.lpSecurityDescriptor = 0;
- sa.bInheritHandle = TRUE;
- tty = CreateFileA (inferior_tty.c_str (), GENERIC_READ | GENERIC_WRITE,
- 0, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
- if (tty == INVALID_HANDLE_VALUE)
- warning (_("Warning: Failed to open TTY %s, error %#x."),
- inferior_tty.c_str (), (unsigned) GetLastError ());
- }
- if (redirected || tty != INVALID_HANDLE_VALUE)
- {
- if (fd_inp >= 0)
- si.hStdInput = (HANDLE) _get_osfhandle (fd_inp);
- else if (tty != INVALID_HANDLE_VALUE)
- si.hStdInput = tty;
- else
- si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
- if (fd_out >= 0)
- si.hStdOutput = (HANDLE) _get_osfhandle (fd_out);
- else if (tty != INVALID_HANDLE_VALUE)
- si.hStdOutput = tty;
- else
- si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
- if (fd_err >= 0)
- si.hStdError = (HANDLE) _get_osfhandle (fd_err);
- else if (tty != INVALID_HANDLE_VALUE)
- si.hStdError = tty;
- else
- si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
- si.dwFlags |= STARTF_USESTDHANDLES;
- }
- toexec = exec_file;
- /* Build the command line, a space-separated list of tokens where
- the first token is the name of the module to be executed.
- To avoid ambiguities introduced by spaces in the module name,
- we quote it. */
- args_len = strlen (toexec) + 2 /* quotes */ + allargs_len + 2;
- args = (char *) alloca (args_len);
- xsnprintf (args, args_len, "\"%s\" %s", toexec, allargs_copy);
- flags |= DEBUG_ONLY_THIS_PROCESS;
- /* CreateProcess takes the environment list as a null terminated set of
- strings (i.e. two nulls terminate the list). */
- /* Get total size for env strings. */
- for (envlen = 0, i = 0; in_env[i] && *in_env[i]; i++)
- envlen += strlen (in_env[i]) + 1;
- envsize = sizeof (in_env[0]) * (i + 1);
- env = (char **) alloca (envsize);
- memcpy (env, in_env, envsize);
- /* Windows programs expect the environment block to be sorted. */
- qsort (env, i, sizeof (char *), envvar_cmp);
- w32env = (char *) alloca (envlen + 1);
- /* Copy env strings into new buffer. */
- for (temp = w32env, i = 0; env[i] && *env[i]; i++)
- {
- strcpy (temp, env[i]);
- temp += strlen (temp) + 1;
- }
- /* Final nil string to terminate new env. */
- *temp = 0;
- windows_init_thread_list ();
- ret = CreateProcessA (0,
- args, /* command line */
- NULL, /* Security */
- NULL, /* thread */
- TRUE, /* inherit handles */
- flags, /* start flags */
- w32env, /* environment */
- inferior_cwd, /* current directory */
- &si,
- &pi);
- if (tty != INVALID_HANDLE_VALUE)
- CloseHandle (tty);
- if (fd_inp >= 0)
- _close (fd_inp);
- if (fd_out >= 0)
- _close (fd_out);
- if (fd_err >= 0)
- _close (fd_err);
- #endif /* !__CYGWIN__ */
- if (!ret)
- error (_("Error creating process %s, (error %u)."),
- exec_file, (unsigned) GetLastError ());
- #ifdef __x86_64__
- BOOL wow64;
- if (IsWow64Process (pi.hProcess, &wow64))
- windows_process.wow64_process = wow64;
- #endif
- CloseHandle (pi.hThread);
- CloseHandle (pi.hProcess);
- if (useshell && shell[0] != '\0')
- saw_create = -1;
- else
- saw_create = 0;
- do_initial_windows_stuff (pi.dwProcessId, 0);
- /* windows_continue (DBG_CONTINUE, -1, 0); */
- }
- void
- windows_nat_target::mourn_inferior ()
- {
- (void) windows_continue (DBG_CONTINUE, -1, 0);
- x86_cleanup_dregs();
- if (open_process_used)
- {
- CHECK (CloseHandle (windows_process.handle));
- open_process_used = 0;
- }
- windows_process.siginfo_er.ExceptionCode = 0;
- inf_child_target::mourn_inferior ();
- }
- /* Send a SIGINT to the process group. This acts just like the user typed a
- ^C on the controlling terminal. */
- void
- windows_nat_target::interrupt ()
- {
- DEBUG_EVENTS ("GenerateConsoleCtrlEvent (CTRLC_EVENT, 0)");
- CHECK (GenerateConsoleCtrlEvent (CTRL_C_EVENT,
- windows_process.current_event.dwProcessId));
- registers_changed (); /* refresh register state */
- }
- /* Helper for windows_xfer_partial that handles memory transfers.
- Arguments are like target_xfer_partial. */
- static enum target_xfer_status
- windows_xfer_memory (gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST memaddr, ULONGEST len, ULONGEST *xfered_len)
- {
- SIZE_T done = 0;
- BOOL success;
- DWORD lasterror = 0;
- if (writebuf != NULL)
- {
- DEBUG_MEM ("write target memory, %s bytes at %s",
- pulongest (len), core_addr_to_string (memaddr));
- success = WriteProcessMemory (windows_process.handle,
- (LPVOID) (uintptr_t) memaddr, writebuf,
- len, &done);
- if (!success)
- lasterror = GetLastError ();
- FlushInstructionCache (windows_process.handle,
- (LPCVOID) (uintptr_t) memaddr, len);
- }
- else
- {
- DEBUG_MEM ("read target memory, %s bytes at %s",
- pulongest (len), core_addr_to_string (memaddr));
- success = ReadProcessMemory (windows_process.handle,
- (LPCVOID) (uintptr_t) memaddr, readbuf,
- len, &done);
- if (!success)
- lasterror = GetLastError ();
- }
- *xfered_len = (ULONGEST) done;
- if (!success && lasterror == ERROR_PARTIAL_COPY && done > 0)
- return TARGET_XFER_OK;
- else
- return success ? TARGET_XFER_OK : TARGET_XFER_E_IO;
- }
- void
- windows_nat_target::kill ()
- {
- CHECK (TerminateProcess (windows_process.handle, 0));
- for (;;)
- {
- if (!windows_continue (DBG_CONTINUE, -1, 1))
- break;
- if (!wait_for_debug_event (&windows_process.current_event, INFINITE))
- break;
- if (windows_process.current_event.dwDebugEventCode
- == EXIT_PROCESS_DEBUG_EVENT)
- break;
- }
- target_mourn_inferior (inferior_ptid); /* Or just windows_mourn_inferior? */
- }
- void
- windows_nat_target::close ()
- {
- DEBUG_EVENTS ("inferior_ptid=%d\n", inferior_ptid.pid ());
- }
- /* Convert pid to printable format. */
- std::string
- windows_nat_target::pid_to_str (ptid_t ptid)
- {
- if (ptid.lwp () != 0)
- return string_printf ("Thread %d.0x%lx", ptid.pid (), ptid.lwp ());
- return normal_pid_to_str (ptid);
- }
- static enum target_xfer_status
- windows_xfer_shared_libraries (struct target_ops *ops,
- enum target_object object, const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, ULONGEST len,
- ULONGEST *xfered_len)
- {
- auto_obstack obstack;
- const char *buf;
- LONGEST len_avail;
- if (writebuf)
- return TARGET_XFER_E_IO;
- obstack_grow_str (&obstack, "<library-list>\n");
- for (windows_solib &so : solibs)
- windows_xfer_shared_library (so.name.c_str (),
- (CORE_ADDR) (uintptr_t) so.load_addr,
- &so.text_offset,
- target_gdbarch (), &obstack);
- obstack_grow_str0 (&obstack, "</library-list>\n");
- buf = (const char *) obstack_finish (&obstack);
- len_avail = strlen (buf);
- if (offset >= len_avail)
- len= 0;
- else
- {
- if (len > len_avail - offset)
- len = len_avail - offset;
- memcpy (readbuf, buf + offset, len);
- }
- *xfered_len = (ULONGEST) len;
- return len != 0 ? TARGET_XFER_OK : TARGET_XFER_EOF;
- }
- /* Helper for windows_nat_target::xfer_partial that handles signal info. */
- static enum target_xfer_status
- windows_xfer_siginfo (gdb_byte *readbuf, ULONGEST offset, ULONGEST len,
- ULONGEST *xfered_len)
- {
- char *buf = (char *) &windows_process.siginfo_er;
- size_t bufsize = sizeof (windows_process.siginfo_er);
- #ifdef __x86_64__
- EXCEPTION_RECORD32 er32;
- if (windows_process.wow64_process)
- {
- buf = (char *) &er32;
- bufsize = sizeof (er32);
- er32.ExceptionCode = windows_process.siginfo_er.ExceptionCode;
- er32.ExceptionFlags = windows_process.siginfo_er.ExceptionFlags;
- er32.ExceptionRecord
- = (uintptr_t) windows_process.siginfo_er.ExceptionRecord;
- er32.ExceptionAddress
- = (uintptr_t) windows_process.siginfo_er.ExceptionAddress;
- er32.NumberParameters = windows_process.siginfo_er.NumberParameters;
- int i;
- for (i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
- er32.ExceptionInformation[i]
- = windows_process.siginfo_er.ExceptionInformation[i];
- }
- #endif
- if (windows_process.siginfo_er.ExceptionCode == 0)
- return TARGET_XFER_E_IO;
- if (readbuf == nullptr)
- return TARGET_XFER_E_IO;
- if (offset > bufsize)
- return TARGET_XFER_E_IO;
- if (offset + len > bufsize)
- len = bufsize - offset;
- memcpy (readbuf, buf + offset, len);
- *xfered_len = len;
- return TARGET_XFER_OK;
- }
- enum target_xfer_status
- windows_nat_target::xfer_partial (enum target_object object,
- const char *annex, gdb_byte *readbuf,
- const gdb_byte *writebuf, ULONGEST offset,
- ULONGEST len, ULONGEST *xfered_len)
- {
- switch (object)
- {
- case TARGET_OBJECT_MEMORY:
- return windows_xfer_memory (readbuf, writebuf, offset, len, xfered_len);
- case TARGET_OBJECT_LIBRARIES:
- return windows_xfer_shared_libraries (this, object, annex, readbuf,
- writebuf, offset, len, xfered_len);
- case TARGET_OBJECT_SIGNAL_INFO:
- return windows_xfer_siginfo (readbuf, offset, len, xfered_len);
- default:
- if (beneath () == NULL)
- {
- /* This can happen when requesting the transfer of unsupported
- objects before a program has been started (and therefore
- with the current_target having no target beneath). */
- return TARGET_XFER_E_IO;
- }
- return beneath ()->xfer_partial (object, annex,
- readbuf, writebuf, offset, len,
- xfered_len);
- }
- }
- /* Provide thread local base, i.e. Thread Information Block address.
- Returns 1 if ptid is found and sets *ADDR to thread_local_base. */
- bool
- windows_nat_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr)
- {
- windows_thread_info *th;
- th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT);
- if (th == NULL)
- return false;
- if (addr != NULL)
- *addr = th->thread_local_base;
- return true;
- }
- ptid_t
- windows_nat_target::get_ada_task_ptid (long lwp, ULONGEST thread)
- {
- return ptid_t (inferior_ptid.pid (), lwp, 0);
- }
- /* Implementation of the to_thread_name method. */
- const char *
- windows_nat_target::thread_name (struct thread_info *thr)
- {
- return windows_process.thread_rec (thr->ptid,
- DONT_INVALIDATE_CONTEXT)->name.get ();
- }
- void _initialize_windows_nat ();
- void
- _initialize_windows_nat ()
- {
- x86_dr_low.set_control = cygwin_set_dr7;
- x86_dr_low.set_addr = cygwin_set_dr;
- x86_dr_low.get_addr = cygwin_get_dr;
- x86_dr_low.get_status = cygwin_get_dr6;
- x86_dr_low.get_control = cygwin_get_dr7;
- /* x86_dr_low.debug_register_length field is set by
- calling x86_set_debug_register_length function
- in processor windows specific native file. */
- add_inf_child_target (&the_windows_nat_target);
- #ifdef __CYGWIN__
- cygwin_internal (CW_SET_DOS_FILE_WARNING, 0);
- #endif
- add_com ("signal-event", class_run, signal_event_command, _("\
- Signal a crashed process with event ID, to allow its debugging.\n\
- This command is needed in support of setting up GDB as JIT debugger on \
- MS-Windows. The command should be invoked from the GDB command line using \
- the '-ex' command-line option. The ID of the event that blocks the \
- crashed process will be supplied by the Windows JIT debugging mechanism."));
- #ifdef __CYGWIN__
- add_setshow_boolean_cmd ("shell", class_support, &useshell, _("\
- Set use of shell to start subprocess."), _("\
- Show use of shell to start subprocess."), NULL,
- NULL,
- NULL, /* FIXME: i18n: */
- &setlist, &showlist);
- add_setshow_boolean_cmd ("cygwin-exceptions", class_support,
- &cygwin_exceptions, _("\
- Break when an exception is detected in the Cygwin DLL itself."), _("\
- Show whether gdb breaks on exceptions in the Cygwin DLL itself."), NULL,
- NULL,
- NULL, /* FIXME: i18n: */
- &setlist, &showlist);
- #endif
- add_setshow_boolean_cmd ("new-console", class_support, &new_console, _("\
- Set creation of new console when creating child process."), _("\
- Show creation of new console when creating child process."), NULL,
- NULL,
- NULL, /* FIXME: i18n: */
- &setlist, &showlist);
- add_setshow_boolean_cmd ("new-group", class_support, &new_group, _("\
- Set creation of new group when creating child process."), _("\
- Show creation of new group when creating child process."), NULL,
- NULL,
- NULL, /* FIXME: i18n: */
- &setlist, &showlist);
- add_setshow_boolean_cmd ("debugexec", class_support, &debug_exec, _("\
- Set whether to display execution in child process."), _("\
- Show whether to display execution in child process."), NULL,
- NULL,
- NULL, /* FIXME: i18n: */
- &setlist, &showlist);
- add_setshow_boolean_cmd ("debugevents", class_support, &debug_events, _("\
- Set whether to display kernel events in child process."), _("\
- Show whether to display kernel events in child process."), NULL,
- NULL,
- NULL, /* FIXME: i18n: */
- &setlist, &showlist);
- add_setshow_boolean_cmd ("debugmemory", class_support, &debug_memory, _("\
- Set whether to display memory accesses in child process."), _("\
- Show whether to display memory accesses in child process."), NULL,
- NULL,
- NULL, /* FIXME: i18n: */
- &setlist, &showlist);
- add_setshow_boolean_cmd ("debugexceptions", class_support,
- &debug_exceptions, _("\
- Set whether to display kernel exceptions in child process."), _("\
- Show whether to display kernel exceptions in child process."), NULL,
- NULL,
- NULL, /* FIXME: i18n: */
- &setlist, &showlist);
- init_w32_command_list ();
- add_cmd ("selector", class_info, display_selectors,
- _("Display selectors infos."),
- &info_w32_cmdlist);
- if (!initialize_loadable ())
- {
- /* This will probably fail on Windows 9x/Me. Let the user know
- that we're missing some functionality. */
- warning(_("\
- cannot automatically find executable file or library to read symbols.\n\
- Use \"file\" or \"dll\" command to load executable/libraries directly."));
- }
- }
- /* Hardware watchpoint support, adapted from go32-nat.c code. */
- /* Pass the address ADDR to the inferior in the I'th debug register.
- Here we just store the address in dr array, the registers will be
- actually set up when windows_continue is called. */
- static void
- cygwin_set_dr (int i, CORE_ADDR addr)
- {
- if (i < 0 || i > 3)
- internal_error (__FILE__, __LINE__,
- _("Invalid register %d in cygwin_set_dr.\n"), i);
- dr[i] = addr;
- for (auto &th : thread_list)
- th->debug_registers_changed = true;
- }
- /* Pass the value VAL to the inferior in the DR7 debug control
- register. Here we just store the address in D_REGS, the watchpoint
- will be actually set up in windows_wait. */
- static void
- cygwin_set_dr7 (unsigned long val)
- {
- dr[7] = (CORE_ADDR) val;
- for (auto &th : thread_list)
- th->debug_registers_changed = true;
- }
- /* Get the value of debug register I from the inferior. */
- static CORE_ADDR
- cygwin_get_dr (int i)
- {
- return dr[i];
- }
- /* Get the value of the DR6 debug status register from the inferior.
- Here we just return the value stored in dr[6]
- by the last call to thread_rec for current_event.dwThreadId id. */
- static unsigned long
- cygwin_get_dr6 (void)
- {
- return (unsigned long) dr[6];
- }
- /* Get the value of the DR7 debug status register from the inferior.
- Here we just return the value stored in dr[7] by the last call to
- thread_rec for current_event.dwThreadId id. */
- static unsigned long
- cygwin_get_dr7 (void)
- {
- return (unsigned long) dr[7];
- }
- /* Determine if the thread referenced by "ptid" is alive
- by "polling" it. If WaitForSingleObject returns WAIT_OBJECT_0
- it means that the thread has died. Otherwise it is assumed to be alive. */
- bool
- windows_nat_target::thread_alive (ptid_t ptid)
- {
- gdb_assert (ptid.lwp () != 0);
- windows_thread_info *th
- = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT);
- return WaitForSingleObject (th->h, 0) != WAIT_OBJECT_0;
- }
- void _initialize_check_for_gdb_ini ();
- void
- _initialize_check_for_gdb_ini ()
- {
- char *homedir;
- if (inhibit_gdbinit)
- return;
- homedir = getenv ("HOME");
- if (homedir)
- {
- char *p;
- char *oldini = (char *) alloca (strlen (homedir) +
- sizeof ("gdb.ini") + 1);
- strcpy (oldini, homedir);
- p = strchr (oldini, '\0');
- if (p > oldini && !IS_DIR_SEPARATOR (p[-1]))
- *p++ = '/';
- strcpy (p, "gdb.ini");
- if (access (oldini, 0) == 0)
- {
- int len = strlen (oldini);
- char *newini = (char *) alloca (len + 2);
- xsnprintf (newini, len + 2, "%.*s.gdbinit",
- (int) (len - (sizeof ("gdb.ini") - 1)), oldini);
- warning (_("obsolete '%s' found. Rename to '%s'."), oldini, newini);
- }
- }
- }
|