Index: cmake/modules/LLDBConfig.cmake =================================================================== --- cmake/modules/LLDBConfig.cmake +++ cmake/modules/LLDBConfig.cmake @@ -418,7 +418,7 @@ # Figure out if lldb could use lldb-server. If so, then we'll # ensure we build lldb-server when an lldb target is being built. -if (CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|FreeBSD|Linux|NetBSD") +if (CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|FreeBSD|Linux|NetBSD|Windows") set(LLDB_CAN_USE_LLDB_SERVER 1) else() set(LLDB_CAN_USE_LLDB_SERVER 0) Index: include/lldb/Host/common/NativeProcessProtocol.h =================================================================== --- include/lldb/Host/common/NativeProcessProtocol.h +++ include/lldb/Host/common/NativeProcessProtocol.h @@ -433,6 +433,8 @@ NativeProcessProtocol(lldb::pid_t pid, int terminal_fd, NativeDelegate &delegate); + void SetID(lldb::pid_t pid) { m_pid = pid; } + // ----------------------------------------------------------- Internal // interface for state handling // ----------------------------------------------------------- Index: include/lldb/Host/windows/PosixApi.h =================================================================== --- include/lldb/Host/windows/PosixApi.h +++ include/lldb/Host/windows/PosixApi.h @@ -74,6 +74,8 @@ #endif // _MSC_VER +#define WNOHANG 1 + // Various useful posix functions that are not present in Windows. We provide // custom implementations. int vasprintf(char **ret, const char *fmt, va_list ap); @@ -101,4 +103,8 @@ inline pid_t fork(void) { LLVM_BUILTIN_UNREACHABLE; } inline pid_t setsid(void) { LLVM_BUILTIN_UNREACHABLE; } +inline pid_t waitpid(pid_t pid, int *status, int options) { + // To be implemented. + return pid_t(-1); +} #endif Index: include/lldb/Target/Platform.h =================================================================== --- include/lldb/Target/Platform.h +++ include/lldb/Target/Platform.h @@ -596,7 +596,7 @@ virtual uint64_t WriteFile(lldb::user_id_t fd, uint64_t offset, const void *src, uint64_t src_len, Status &error) { error.SetErrorStringWithFormat( - "Platform::ReadFile() is not supported in the %s platform", + "Platform::WriteFile() is not supported in the %s platform", GetName().GetCString()); return -1; } Index: lit/Modules/PECOFF/export-dllfunc.yaml =================================================================== --- lit/Modules/PECOFF/export-dllfunc.yaml +++ lit/Modules/PECOFF/export-dllfunc.yaml @@ -6,6 +6,10 @@ # RUN: lldb-test object-file %t.dll | FileCheck -check-prefix=BASIC-CHECK %s # RUN: lldb-test object-file -dep-modules %t.dll | FileCheck -check-prefix=DEPS %s +# BASIC-CHECK: Plugin name: pe-coff + +# timestamp and build id of debug info in the coff header varies. So does its UUID. +# BASIC-CHECK-DAG: UUID: {{[0-9A-F]{7,}[0-9A-F]}}-{{.*}} # BASIC-CHECK: Showing 3 subsections # BASIC-CHECK: Index: 0 Index: packages/Python/lldbsuite/test/dotest.py =================================================================== --- packages/Python/lldbsuite/test/dotest.py +++ packages/Python/lldbsuite/test/dotest.py @@ -1311,7 +1311,7 @@ configuration.dont_do_debugserver_test = "linux" in target_platform or "freebsd" in target_platform or "windows" in target_platform # Don't do lldb-server (llgs) tests on anything except Linux. - configuration.dont_do_llgs_test = not ("linux" in target_platform) + configuration.dont_do_llgs_test = not ("linux" in target_platform) and not ("windows" in target_platform) # Collect tests from the specified testing directories. If a test # subdirectory filter is explicitly specified, limit the search to that Index: packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteAuxvSupport.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteAuxvSupport.py +++ packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteAuxvSupport.py @@ -104,6 +104,7 @@ # tests don't get skipped. # + @skipIfWindows # no auxv support. @llgs_test def test_supports_auxv_llgs(self): self.init_llgs_test() @@ -127,6 +128,7 @@ self.set_inferior_startup_launch() self.auxv_data_is_correct_size() + @skipIfWindows @llgs_test def test_auxv_data_is_correct_size_llgs(self): self.init_llgs_test() @@ -165,6 +167,7 @@ self.set_inferior_startup_launch() self.auxv_keys_look_valid() + @skipIfWindows @llgs_test def test_auxv_keys_look_valid_llgs(self): self.init_llgs_test() @@ -212,6 +215,7 @@ self.set_inferior_startup_launch() self.auxv_chunked_reads_work() + @skipIfWindows @llgs_test def test_auxv_chunked_reads_work_llgs(self): self.init_llgs_test() Index: packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteKill.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteKill.py +++ packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteKill.py @@ -14,17 +14,24 @@ @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def attach_commandline_kill_after_initial_stop(self): + reg_expr = r"^\$X[0-9a-fA-F]+([^#]*)#[0-9A-Fa-f]{2}" + triple = self.dbg.GetSelectedPlatform().GetTriple() + + # No signal support on Windwos. Only W* response is sent. + if re.match(".*-.*-windows", triple): + reg_expr = r"^\$W[0-9a-fA-F]+([^#]*)#[0-9A-Fa-f]{2}" + procs = self.prep_debug_monitor_and_inferior() self.test_sequence.add_log_lines([ "read packet: $k#6b", - {"direction": "send", "regex": r"^\$X[0-9a-fA-F]+([^#]*)#[0-9A-Fa-f]{2}"}, + {"direction": "send", "regex": reg_expr}, ], True) if self.stub_sends_two_stop_notifications_on_kill: # Add an expectation for a second X result for stubs that send two # of these. self.test_sequence.add_log_lines([ - {"direction": "send", "regex": r"^\$X[0-9a-fA-F]+([^#]*)#[0-9A-Fa-f]{2}"}, + {"direction": "send", "regex": reg_expr}, ], True) self.expect_gdbremote_sequence() Index: packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteModuleInfo.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteModuleInfo.py +++ packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteModuleInfo.py @@ -19,9 +19,15 @@ context = self.expect_gdbremote_sequence() info = self.parse_process_info_response(context) + module_path = lldbutil.append_to_process_working_directory(self, "a.out") + + # Replace path separators in the json string either with "\\\\" or "/" on Windows. + triple = self.dbg.GetSelectedPlatform().GetTriple() + if re.match(".*-.*-windows", triple): + module_path = module_path.replace(os.path.sep, '/') + self.test_sequence.add_log_lines([ - 'read packet: $jModulesInfo:[{"file":"%s","triple":"%s"}]]#00' % ( - lldbutil.append_to_process_working_directory(self, "a.out"), + 'read packet: $jModulesInfo:[{"file":"%s","triple":"%s"}]]#00' % (module_path, seven.unhexlify(info["triple"])), {"direction": "send", "regex": r'^\$\[{(.*)}\]\]#[0-9A-Fa-f]{2}', Index: packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteProcessInfo.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteProcessInfo.py +++ packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteProcessInfo.py @@ -182,6 +182,13 @@ self.build() self.qProcessInfo_contains_keys(set(['triple'])) + @skipUnlessPlatform(["windows"]) + @llgs_test + def test_qProcessInfo_contains_triple_ppid_llgs_windows(self): + self.init_llgs_test() + self.build() + self.qProcessInfo_contains_keys(set(['triple', 'parent-pid'])) + @skipUnlessDarwin @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet @@ -209,3 +216,10 @@ self.init_llgs_test() self.build() self.qProcessInfo_does_not_contain_keys(set(['cputype', 'cpusubtype'])) + + @skipUnlessPlatform(["windows"]) + @llgs_test + def test_qProcessInfo_does_not_contain_cputype_cpusubtype_llgs_windows(self): + self.init_llgs_test() + self.build() + self.qProcessInfo_does_not_contain_keys(set(['cputype', 'cpusubtype'])) Index: packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteRegisterState.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteRegisterState.py +++ packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteRegisterState.py @@ -24,12 +24,20 @@ if with_suffix: self.add_thread_suffix_request_packets() self.add_threadinfo_collection_packets() - self.test_sequence.add_log_lines([ - # Start the inferior... - "read packet: $c#63", - # ... match output.... - {"type": "output_match", "regex": self.maybe_strict_output_regex( - r"message:main entered\r\n")}, + + # Skip O* packet test for Windows. Will add it back when pty is supported. + triple = self.dbg.GetSelectedPlatform().GetTriple() + if re.match(".*-.*-windows", triple): + self.test_sequence.add_log_lines([ + # Start the inferior... + "read packet: $c#63"], True) + else: + self.test_sequence.add_log_lines([ + # Start the inferior... + "read packet: $c#63", + # ... match output.... + {"type": "output_match", "regex": self.maybe_strict_output_regex( + r"message:main entered\r\n")}, ], True) # ... then interrupt. self.add_interrupt_packets() Index: packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteSingleStep.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteSingleStep.py +++ packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteSingleStep.py @@ -20,6 +20,7 @@ self.single_step_only_steps_one_instruction( use_Hc_packet=True, step_instruction="s") + @skipIfWindows # No pty support to test any inferior std -i/e/o @llgs_test @expectedFailureAndroid( bugnumber="llvm.org/pr24739", Index: packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py +++ packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py @@ -205,6 +205,11 @@ self.set_inferior_startup_launch() self.stop_reply_reports_multiple_threads(5) + # In current implementation of llgs on Windows, as a response to '\x03' packet, the debugger + # of the native process will trigger a call to DebugBreakProcess that will create a new thread + # to handle the exception debug event. So one more stop thread will be notified to the + # delegate, e.g. llgs. So tests below to assert the stop threads number will all fail. + @expectedFailureAll(oslist=["windows"]) @llgs_test def test_stop_reply_reports_multiple_threads_llgs(self): self.init_llgs_test() @@ -226,6 +231,7 @@ self.set_inferior_startup_launch() self.no_QListThreadsInStopReply_supplies_no_threads(5) + @expectedFailureAll(oslist=["windows"]) @llgs_test def test_no_QListThreadsInStopReply_supplies_no_threads_llgs(self): self.init_llgs_test() @@ -263,6 +269,7 @@ self.set_inferior_startup_launch() self.stop_reply_reports_correct_threads(5) + @expectedFailureAll(oslist=["windows"]) @llgs_test def test_stop_reply_reports_correct_threads_llgs(self): self.init_llgs_test() @@ -287,6 +294,7 @@ self.assertTrue(int(stop_reply_pcs[thread_id], 16) == int(threads_info_pcs[thread_id], 16)) + @expectedFailureAll(oslist=["windows"]) @llgs_test def test_stop_reply_contains_thread_pcs_llgs(self): self.init_llgs_test() Index: packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_qThreadStopInfo.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_qThreadStopInfo.py +++ packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_qThreadStopInfo.py @@ -55,7 +55,15 @@ # Wait until all threads have started. threads = self.wait_for_thread_count(thread_count, timeout_seconds=3) self.assertIsNotNone(threads) - self.assertEqual(len(threads), thread_count) + + # On Windows, there could be more threads spawned. For example, DebugBreakProcess will + # create a new thread from the debugged process to handle an exception event. So here we + # assert 'GreaterEqual' condition. + triple = self.dbg.GetSelectedPlatform().GetTriple() + if re.match(".*-.*-windows", triple): + self.assertGreaterEqual(len(threads), thread_count) + else: + self.assertEqual(len(threads), thread_count) # Grab stop reply for each thread via qThreadStopInfo{tid:hex}. stop_replies = {} @@ -102,7 +110,12 @@ def qThreadStopInfo_works_for_multiple_threads(self, thread_count): (stop_replies, _) = self.gather_stop_replies_via_qThreadStopInfo(thread_count) - self.assertEqual(len(stop_replies), thread_count) + triple = self.dbg.GetSelectedPlatform().GetTriple() + # Consider one more thread created by calling DebugBreakProcess. + if re.match(".*-.*-windows", triple): + self.assertGreaterEqual(len(stop_replies), thread_count) + else: + self.assertEqual(len(stop_replies), thread_count) @debugserver_test def test_qThreadStopInfo_works_for_multiple_threads_debugserver(self): @@ -131,7 +144,13 @@ stop_replies.values()) if stop_reason != 0) # All but one thread should report no stop reason. - self.assertEqual(no_stop_reason_count, thread_count - 1) + triple = self.dbg.GetSelectedPlatform().GetTriple() + + # Consider one more thread created by calling DebugBreakProcess. + if re.match(".*-.*-windows", triple): + self.assertGreaterEqual(no_stop_reason_count, thread_count - 1) + else: + self.assertEqual(no_stop_reason_count, thread_count - 1) # Only one thread should should indicate a stop reason. self.assertEqual(with_stop_reason_count, 1) @@ -173,7 +192,8 @@ self.qThreadStopInfo_has_valid_thread_names(self.THREAD_COUNT, "a.out") # test requires OS with set, equal thread names by default. - @skipUnlessPlatform(["linux"]) + # Windows thread does not have name property, equal names as the process's by default. + @skipUnlessPlatform(["linux", "windows"]) @llgs_test def test_qThreadStopInfo_has_valid_thread_names_llgs(self): self.init_llgs_test() Index: packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_vCont.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_vCont.py +++ packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_vCont.py @@ -105,6 +105,7 @@ self.single_step_only_steps_one_instruction( use_Hc_packet=True, step_instruction="vCont;s") + @skipIfWindows # No pty support to test O* & I* notification packets. @llgs_test @expectedFailureAndroid( bugnumber="llvm.org/pr24739", @@ -136,6 +137,7 @@ self.single_step_only_steps_one_instruction( use_Hc_packet=False, step_instruction="vCont;s:{thread}") + @skipIfWindows # No pty support to test O* & I* notification packets. @llgs_test @expectedFailureAndroid( bugnumber="llvm.org/pr24739", Index: packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py +++ packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py @@ -158,6 +158,7 @@ self.build() self.inferior_print_exit() + @skipIfWindows # No pty support to test any inferior output @llgs_test @expectedFlakeyLinux("llvm.org/pr25652") def test_inferior_print_exit_llgs(self): @@ -436,6 +437,7 @@ self.targetHasAVX(), "Advanced Vector Extensions" in register_sets) + @expectedFailureAll(oslist=["windows"]) # no avx for now. @llgs_test def test_qRegisterInfo_contains_avx_registers_llgs(self): self.init_llgs_test() @@ -465,6 +467,7 @@ self.set_inferior_startup_launch() self.qThreadInfo_contains_thread() + @expectedFailureAll(oslist=["windows"]) # expect one more thread stopped @llgs_test def test_qThreadInfo_contains_thread_launch_llgs(self): self.init_llgs_test() @@ -480,6 +483,7 @@ self.set_inferior_startup_attach() self.qThreadInfo_contains_thread() + @expectedFailureAll(oslist=["windows"]) # expect one more thread stopped @llgs_test def test_qThreadInfo_contains_thread_attach_llgs(self): self.init_llgs_test() @@ -523,6 +527,7 @@ self.set_inferior_startup_launch() self.qThreadInfo_matches_qC() + @expectedFailureAll(oslist=["windows"]) # expect one more thread stopped @llgs_test def test_qThreadInfo_matches_qC_launch_llgs(self): self.init_llgs_test() @@ -538,6 +543,7 @@ self.set_inferior_startup_attach() self.qThreadInfo_matches_qC() + @expectedFailureAll(oslist=["windows"]) # expect one more thread stopped @llgs_test def test_qThreadInfo_matches_qC_attach_llgs(self): self.init_llgs_test() @@ -668,6 +674,7 @@ self.set_inferior_startup_launch() self.Hg_switches_to_3_threads() + @expectedFailureAll(oslist=["windows"]) # expect 4 threads @llgs_test def test_Hg_switches_to_3_threads_launch_llgs(self): self.init_llgs_test() @@ -683,6 +690,7 @@ self.set_inferior_startup_attach() self.Hg_switches_to_3_threads() + @expectedFailureAll(oslist=["windows"]) # expecting one more thread @llgs_test def test_Hg_switches_to_3_threads_attach_llgs(self): self.init_llgs_test() @@ -812,6 +820,7 @@ # expectations about fixed signal numbers. self.Hc_then_Csignal_signals_correct_thread(self.TARGET_EXC_BAD_ACCESS) + @skipIfWindows # no SIGSEGV support @llgs_test def test_Hc_then_Csignal_signals_correct_thread_launch_llgs(self): self.init_llgs_test() @@ -880,6 +889,7 @@ self.set_inferior_startup_launch() self.m_packet_reads_memory() + @skipIfWindows # No pty support to test any inferior output @llgs_test def test_m_packet_reads_memory_llgs(self): self.init_llgs_test() @@ -970,6 +980,7 @@ self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_code_address_as_executable() + @skipIfWindows # No pty support to test any inferior output @llgs_test def test_qMemoryRegionInfo_reports_code_address_as_executable_llgs(self): self.init_llgs_test() @@ -1035,6 +1046,7 @@ self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_stack_address_as_readable_writeable() + @skipIfWindows # No pty support to test any inferior output @llgs_test def test_qMemoryRegionInfo_reports_stack_address_as_readable_writeable_llgs( self): @@ -1100,6 +1112,7 @@ self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_heap_address_as_readable_writeable() + @skipIfWindows # No pty support to test any inferior output @llgs_test def test_qMemoryRegionInfo_reports_heap_address_as_readable_writeable_llgs( self): @@ -1252,6 +1265,7 @@ self.set_inferior_startup_launch() self.breakpoint_set_and_remove_work(want_hardware=False) + @skipIfWindows # No pty support to test any inferior output @llgs_test @expectedFlakeyLinux("llvm.org/pr25652") def test_software_breakpoint_set_and_remove_work_llgs(self): @@ -1389,6 +1403,7 @@ self.set_inferior_startup_launch() self.written_M_content_reads_back_correctly() + @skipIfWindows # No pty support to test any inferior output @llgs_test @expectedFlakeyLinux("llvm.org/pr25652") def test_written_M_content_reads_back_correctly_llgs(self): Index: packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py +++ packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py @@ -235,6 +235,10 @@ # Remote platforms don't support named pipe based port negotiation use_named_pipe = False + triple = self.dbg.GetSelectedPlatform().GetTriple() + if re.match(".*-.*-windows", triple): + self.skipTest("Remotely testing is not supported on Windows yet.") + # Grab the ppid from /proc/[shell pid]/stat err, retcode, shell_stat = self.run_platform_command( "cat /proc/$$/stat") @@ -260,6 +264,10 @@ # Remove if it's there. self.debug_monitor_exe = re.sub(r' \(deleted\)$', '', exe) else: + # Need to figure out how to create a named pipe on Windows. + if platform.system() == 'Windows': + use_named_pipe = False + self.debug_monitor_exe = get_lldb_server_exe() if not self.debug_monitor_exe: self.skipTest("lldb-server exe not found") Index: packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py +++ packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py @@ -926,6 +926,12 @@ # Convert text pids to ints process_ids = [int(text_pid) for text_pid in text_process_ids if text_pid != ''] + elif platform.system() == 'Windows': + output = subprocess.check_output( + "for /f \"tokens=2 delims=,\" %F in ('tasklist /nh /fi \"PID ne 0\" /fo csv') do @echo %~F", shell=True).decode("utf-8") + text_process_ids = output.split('\n')[1:] + process_ids = [int(text_pid) + for text_pid in text_process_ids if text_pid != ''] # elif {your_platform_here}: # fill in process_ids as a list of int type process IDs running on # the local system. Index: packages/Python/lldbsuite/test/tools/lldb-server/main.cpp =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/main.cpp +++ packages/Python/lldbsuite/test/tools/lldb-server/main.cpp @@ -11,14 +11,16 @@ #include #include #include +#if !defined(_WIN32) #include -#include #include +#include +#endif +#include #include #include #include #include -#include #include #if defined(__APPLE__) @@ -30,6 +32,30 @@ #include #endif +#if defined(_WIN32) +#include + +typedef HANDLE pthread_t; +typedef HANDLE pthread_mutex_t; + +static unsigned int sleep(unsigned int seconds) { + ::Sleep(seconds * 1000); + return 0; +} + +static int pthread_mutex_lock(pthread_mutex_t *mutex) { + ::WaitForSingleObject(*mutex, INFINITE); + return 0; +} + +static int pthread_mutex_unlock(pthread_mutex_t *mutex) { + ::ReleaseMutex(*mutex); + return 0; +} + +#define PTHREAD_MUTEX_INITIALIZER ::CreateMutex(NULL, FALSE, NULL) +#endif + static const char *const RETVAL_PREFIX = "retval:"; static const char *const SLEEP_PREFIX = "sleep:"; static const char *const STDERR_PREFIX = "stderr:"; @@ -63,7 +89,11 @@ static volatile char g_c2 = '1'; static void print_pid() { +#if defined(_WIN32) + fprintf(stderr, "PID: %d\n", ::GetCurrentProcessId()); +#else fprintf(stderr, "PID: %d\n", getpid()); +#endif } static void print_thread_id() { @@ -81,12 +111,17 @@ #elif defined(__NetBSD__) // Technically lwpid_t is 32-bit signed integer printf("%" PRIx64, static_cast(_lwp_self())); +#elif defined(_WIN32) + printf("%" PRIx64, static_cast(::GetCurrentThreadId())); #else printf("{no-tid-support}"); #endif } static void signal_handler(int signo) { +#if defined(_WIN32) + // No signal support on Windows. +#else const char *signal_name = nullptr; switch (signo) { case SIGUSR1: @@ -136,6 +171,7 @@ fprintf(stderr, "failed to set signal handler: errno=%d\n", errno); exit(1); } +#endif } static void swap_chars() { @@ -152,7 +188,11 @@ pthread_mutex_unlock(&g_print_mutex); } +#if !defined(_WIN32) static void *thread_func(void *arg) { +#else +static DWORD thread_func(void *arg) { +#endif static pthread_mutex_t s_thread_index_mutex = PTHREAD_MUTEX_INITIALIZER; static int s_thread_index = 1; @@ -207,7 +247,11 @@ sleep_seconds_remaining = sleep(sleep_seconds_remaining); } +#if defined(_WIN32) + return 0; +#else return nullptr; +#endif } int main(int argc, char **argv) { @@ -217,6 +261,7 @@ std::unique_ptr heap_array_up; int return_value = 0; +#if !defined(_WIN32) // Set the signal handler. sig_t sig_result = signal(SIGALRM, signal_handler); if (sig_result == SIG_ERR) { @@ -235,6 +280,7 @@ fprintf(stderr, "failed to set SIGUSR1 handler: errno=%d\n", errno); exit(1); } +#endif // Process command line args. for (int i = 1; i < argc; ++i) { @@ -329,12 +375,21 @@ if (std::strstr(argv[i] + strlen(THREAD_PREFIX), THREAD_COMMAND_NEW)) { // Create a new thread. pthread_t new_thread; +#if !defined(_WIN32) const int err = ::pthread_create(&new_thread, nullptr, thread_func, nullptr); if (err) { fprintf(stderr, "pthread_create() failed with error code %d\n", err); exit(err); } +#else + new_thread = ::CreateThread(NULL, 0, thread_func, nullptr, 0, nullptr); + if (new_thread == NULL) { + fprintf(stderr, "CreateThread() failed with error code %d\n", + ::GetLastError()); + exit(3); + } +#endif threads.push_back(new_thread); } else if (std::strstr(argv[i] + strlen(THREAD_PREFIX), THREAD_COMMAND_PRINT_IDS)) { @@ -362,6 +417,13 @@ } } +#if defined(_WIN32) + { + ::WaitForMultipleObjects(threads.size(), threads.data(), TRUE, INFINITE); + ::CloseHandle(g_print_mutex); + ::CloseHandle(g_jump_buffer_mutex); + } +#else // If we launched any threads, join them for (std::vector::iterator it = threads.begin(); it != threads.end(); ++it) { @@ -370,6 +432,7 @@ if (err != 0) fprintf(stderr, "pthread_join() failed with error code %d\n", err); } +#endif return return_value; } Index: packages/Python/lldbsuite/test/tools/lldb-server/thread-name/TestGdbRemoteThreadName.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/thread-name/TestGdbRemoteThreadName.py +++ packages/Python/lldbsuite/test/tools/lldb-server/thread-name/TestGdbRemoteThreadName.py @@ -29,6 +29,7 @@ kv_dict = self.parse_key_val_dict(context.get("key_vals_text")) self.assertEqual(expected_name, kv_dict.get("name")) + @skipIfWindows # the test is not updated for Windows. @llgs_test def test(self): """ Make sure lldb-server can retrieve inferior thread name""" Index: source/Host/common/File.cpp =================================================================== --- source/Host/common/File.cpp +++ source/Host/common/File.cpp @@ -675,6 +675,10 @@ else if (open_options & eOpenOptionCanCreate) mode |= O_CREAT; +#ifdef _WIN32 + mode |= O_BINARY; +#endif + return mode; } Index: source/Host/common/Socket.cpp =================================================================== --- source/Host/common/Socket.cpp +++ source/Host/common/Socket.cpp @@ -423,6 +423,15 @@ if (sock == kInvalidSocketValue) SetLastError(error); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) { + log->Printf( + "Socket::CreateSocket() (socket = %" PRIu64 + ", domain = %i, type = %i, protocol = %i, child_processes_inherit = %i)" + " (error = %s)", + static_cast(sock), domain, type, protocol, + child_processes_inherit, error.AsCString()); + } return sock; } Index: source/Host/common/SocketAddress.cpp =================================================================== --- source/Host/common/SocketAddress.cpp +++ source/Host/common/SocketAddress.cpp @@ -28,6 +28,7 @@ #include #include "lldb/Host/PosixApi.h" +#include "lldb/Utility/Status.h" // WindowsXP needs an inet_ntop implementation #ifdef _WIN32 @@ -255,6 +256,15 @@ service_ptr = service_ptr->ai_next) { addr_list.emplace_back(SocketAddress(service_ptr)); } + } else if (err) { + // Consume the error here. + Status error; +#ifdef _WIN32 + error.SetError(err, lldb::eErrorTypeWin32); +#else + error.SetError(err, lldb::eErrorTypePOSIX); +#endif + printf("SocketAddress::GetAddressInfo - %s\n", error.AsCString()); } if (service_info_list) Index: source/Host/windows/Host.cpp =================================================================== --- source/Host/windows/Host.cpp +++ source/Host/windows/Host.cpp @@ -169,7 +169,23 @@ GetProcessExecutableAndTriple(handle, process_info); // Need to read the PEB to get parent process and command line arguments. - return true; + + AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); + if (!snapshot.IsValid()) + return false; + + PROCESSENTRY32W pe; + pe.dwSize = sizeof(PROCESSENTRY32W); + if (Process32FirstW(snapshot.get(), &pe)) { + do { + if (pe.th32ProcessID == pid) { + process_info.SetParentProcessID(pe.th32ParentProcessID); + return true; + } + } while (Process32NextW(snapshot.get(), &pe)); + } + + return false; } HostThread Host::StartMonitoringChildProcess( Index: source/Host/windows/HostInfoWindows.cpp =================================================================== --- source/Host/windows/HostInfoWindows.cpp +++ source/Host/windows/HostInfoWindows.cpp @@ -95,6 +95,7 @@ if (!::GetComputerNameW(buffer, &dwSize)) return false; + s.clear(); return llvm::convertWideToUTF8(buffer, s); } Index: source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h =================================================================== --- source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h +++ source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h @@ -290,6 +290,8 @@ llvm::Optional m_deps_filespec; typedef llvm::object::OwningBinary OWNBINType; llvm::Optional m_owningbin; + + lldb_private::UUID m_uuid; }; #endif // liblldb_ObjectFilePECOFF_h_ Index: source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp =================================================================== --- source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -128,18 +128,27 @@ if (pe_signature != IMAGE_NT_SIGNATURE) return false; if (ParseCOFFHeader(data, &offset, coff_header)) { - ArchSpec spec; + ModuleSpec module_spec(file); + ArchSpec &spec = module_spec.GetArchitecture(); + + lldb_private::UUID &uuid = module_spec.GetUUID(); + if (!uuid.IsValid()) { + if (auto Result = llvm::sys::fs::md5_contents(file.GetPath())) + uuid = + UUID::fromOptionalData(llvm::ArrayRef(Result->Bytes)); + } + if (coff_header.machine == MachineAmd64) { spec.SetTriple("x86_64-pc-windows"); - specs.Append(ModuleSpec(file, spec)); + specs.Append(module_spec); } else if (coff_header.machine == MachineX86) { spec.SetTriple("i386-pc-windows"); - specs.Append(ModuleSpec(file, spec)); + specs.Append(module_spec); spec.SetTriple("i686-pc-windows"); - specs.Append(ModuleSpec(file, spec)); + specs.Append(module_spec); } else if (coff_header.machine == MachineArmNt) { spec.SetTriple("arm-pc-windows"); - specs.Append(ModuleSpec(file, spec)); + specs.Append(module_spec); } } } @@ -838,7 +847,18 @@ } } -UUID ObjectFilePECOFF::GetUUID() { return UUID(); } +UUID ObjectFilePECOFF::GetUUID() { + if (m_uuid.IsValid()) + return m_uuid; + + // Use the object content's MD5 as UUID + auto Result = llvm::sys::fs::md5_contents(GetFileSpec().GetPath()); + if (!Result) + return UUID(); + + m_uuid = UUID::fromOptionalData(llvm::ArrayRef(Result->Bytes)); + return m_uuid; +} uint32_t ObjectFilePECOFF::ParseDependentModules() { ModuleSP module_sp(GetModule()); Index: source/Plugins/Process/Utility/CMakeLists.txt =================================================================== --- source/Plugins/Process/Utility/CMakeLists.txt +++ source/Plugins/Process/Utility/CMakeLists.txt @@ -42,6 +42,9 @@ RegisterContextPOSIX_s390x.cpp RegisterContextPOSIX_x86.cpp RegisterContextThreadMemory.cpp + RegisterContextWindows_i386.cpp + RegisterContextWindows_wow64.cpp + RegisterContextWindows_x86_64.cpp RegisterInfoPOSIX_arm.cpp RegisterInfoPOSIX_arm64.cpp RegisterInfoPOSIX_ppc64le.cpp Index: source/Plugins/Process/Utility/RegisterContextWindows_i386.h =================================================================== --- /dev/null +++ source/Plugins/Process/Utility/RegisterContextWindows_i386.h @@ -0,0 +1,36 @@ +//===-- RegisterContextWindows_i386.h ---------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindows_i386_H_ +#define liblldb_RegisterContextWindows_i386_H_ + +#include "RegisterInfoInterface.h" + +class RegisterContextWindows_i386 : public lldb_private::RegisterInfoInterface { +public: + RegisterContextWindows_i386(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override { + return m_register_info_p; + } + + uint32_t GetRegisterCount() const override { return m_register_info_count; } + + uint32_t GetUserRegisterCount() const override { + return m_user_register_count; + } + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; + uint32_t m_user_register_count; +}; + +#endif Index: source/Plugins/Process/Utility/RegisterContextWindows_i386.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Utility/RegisterContextWindows_i386.cpp @@ -0,0 +1,111 @@ +//===-- RegisterContextWindows_i386.cpp -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextWindows_i386.h" +#include "RegisterContext_x86.h" +#include "lldb-x86-register-enums.h" + +#include + +using namespace lldb_private; +using namespace lldb; + +// Declare our g_register_infos structure. +typedef struct _GPR { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t esp; + uint32_t ss; +} GPR; + +#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname)) + +// clang-format off +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { \ + #reg, alt,sizeof(((GPR *)nullptr)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, \ + {kind1, kind2, kind3, kind4, lldb_##reg##_i386 }, nullptr, nullptr, \ + nullptr, 0 \ + } + +// clang-format off +static RegisterInfo g_register_infos_i386[] = { +// General purpose registers EH_Frame DWARF Generic Process Plugin +// =========================== ================== ================ ========================= ==================== + DEFINE_GPR(eax, nullptr, ehframe_eax_i386, dwarf_eax_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ebx, nullptr, ehframe_ebx_i386, dwarf_ebx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ecx, nullptr, ehframe_ecx_i386, dwarf_ecx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(edx, nullptr, ehframe_edx_i386, dwarf_edx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(edi, nullptr, ehframe_edi_i386, dwarf_edi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(esi, nullptr, ehframe_esi_i386, dwarf_esi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ebp, "fp", ehframe_ebp_i386, dwarf_ebp_i386, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(esp, "sp", ehframe_esp_i386, dwarf_esp_i386, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(eip, "pc", ehframe_eip_i386, dwarf_eip_i386, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(eflags, "flags", ehframe_eflags_i386, dwarf_eflags_i386, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(cs, nullptr, LLDB_INVALID_REGNUM, dwarf_cs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs, nullptr, LLDB_INVALID_REGNUM, dwarf_fs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs, nullptr, LLDB_INVALID_REGNUM, dwarf_gs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ss, nullptr, LLDB_INVALID_REGNUM, dwarf_ss_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ds, nullptr, LLDB_INVALID_REGNUM, dwarf_ds_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(es, nullptr, LLDB_INVALID_REGNUM, dwarf_es_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + }; + +static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return g_register_infos_i386; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return static_cast(sizeof(g_register_infos_i386) / + sizeof(g_register_infos_i386[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +static uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return static_cast(sizeof(g_register_infos_i386) / + sizeof(g_register_infos_i386[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextWindows_i386::RegisterContextWindows_i386( + const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)), + m_user_register_count(GetUserRegisterInfoCount(target_arch)) { +} + +size_t RegisterContextWindows_i386::GetGPRSize() const { return sizeof(GPR); } Index: source/Plugins/Process/Utility/RegisterContextWindows_wow64.h =================================================================== --- /dev/null +++ source/Plugins/Process/Utility/RegisterContextWindows_wow64.h @@ -0,0 +1,37 @@ +//===-- RegisterContextWindows_wow64.h --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindows_wow64_H_ +#define liblldb_RegisterContextWindows_wow64_H_ + +#include "RegisterInfoInterface.h" + +class RegisterContextWindows_wow64 + : public lldb_private::RegisterInfoInterface { +public: + RegisterContextWindows_wow64(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override { + return m_register_info_p; + } + + uint32_t GetRegisterCount() const override { return m_register_info_count; } + + uint32_t GetUserRegisterCount() const override { + return m_user_register_count; + } + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; + uint32_t m_user_register_count; +}; + +#endif Index: source/Plugins/Process/Utility/RegisterContextWindows_wow64.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Utility/RegisterContextWindows_wow64.cpp @@ -0,0 +1,111 @@ +//===-- RegisterContextWindows_wow64.cpp ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextWindows_wow64.h" +#include "RegisterContext_x86.h" +#include "lldb-x86-register-enums.h" + +#include + +using namespace lldb_private; +using namespace lldb; + +// Declare our g_register_infos structure. +typedef struct _GPR { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t esp; + uint32_t ss; +} GPR; + +#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname)) + +// clang-format off +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { \ + #reg, alt, sizeof(((GPR *)nullptr)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, \ + {kind1, kind2, kind3, kind4, lldb_##reg##_i386 }, nullptr, nullptr, \ + nullptr, 0 \ + } + +// clang-format off +static RegisterInfo g_register_infos_wow64[] = { +// General purpose registers EH_Frame DWARF Generic Process Plugin +// =========================== ================== ================ ========================= ==================== + DEFINE_GPR(eax, nullptr, ehframe_eax_i386, dwarf_eax_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ebx, nullptr, ehframe_ebx_i386, dwarf_ebx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ecx, nullptr, ehframe_ecx_i386, dwarf_ecx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(edx, nullptr, ehframe_edx_i386, dwarf_edx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(edi, nullptr, ehframe_edi_i386, dwarf_edi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(esi, nullptr, ehframe_esi_i386, dwarf_esi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ebp, "fp", ehframe_ebp_i386, dwarf_ebp_i386, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(esp, "sp", ehframe_esp_i386, dwarf_esp_i386, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(eip, "pc", ehframe_eip_i386, dwarf_eip_i386, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(eflags, "flags", ehframe_eflags_i386, dwarf_eflags_i386, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(cs, nullptr, LLDB_INVALID_REGNUM, dwarf_cs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs, nullptr, LLDB_INVALID_REGNUM, dwarf_fs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs, nullptr, LLDB_INVALID_REGNUM, dwarf_gs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ss, nullptr, LLDB_INVALID_REGNUM, dwarf_ss_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ds, nullptr, LLDB_INVALID_REGNUM, dwarf_ds_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(es, nullptr, LLDB_INVALID_REGNUM, dwarf_es_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + }; + +static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return g_register_infos_wow64; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return static_cast(sizeof(g_register_infos_wow64) / + sizeof(g_register_infos_wow64[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +static uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return static_cast(sizeof(g_register_infos_wow64) / + sizeof(g_register_infos_wow64[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextWindows_wow64::RegisterContextWindows_wow64( + const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)), + m_user_register_count(GetUserRegisterInfoCount(target_arch)) { +} + +size_t RegisterContextWindows_wow64::GetGPRSize() const { return sizeof(GPR); } Index: source/Plugins/Process/Utility/RegisterContextWindows_x86_64.h =================================================================== --- /dev/null +++ source/Plugins/Process/Utility/RegisterContextWindows_x86_64.h @@ -0,0 +1,37 @@ +//===-- RegisterContextWindows_x86_64.h --- ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindows_x86_64_H_ +#define liblldb_RegisterContextWindows_x86_64_H_ + +#include "RegisterInfoInterface.h" + +class RegisterContextWindows_x86_64 + : public lldb_private::RegisterInfoInterface { +public: + RegisterContextWindows_x86_64(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override { + return m_register_info_p; + } + + uint32_t GetRegisterCount() const override { return m_register_info_count; } + + uint32_t GetUserRegisterCount() const override { + return m_user_register_count; + } + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; + uint32_t m_user_register_count; +}; + +#endif Index: source/Plugins/Process/Utility/RegisterContextWindows_x86_64.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Utility/RegisterContextWindows_x86_64.cpp @@ -0,0 +1,161 @@ +//===-- RegisterContextWindows_x86_64.cpp -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextWindows_x86_64.h" +#include "RegisterContextWindows_wow64.h" +#include "RegisterContext_x86.h" +#include "lldb-x86-register-enums.h" + +#include + +using namespace lldb_private; +using namespace lldb; + +typedef struct _GPR { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t rbp; + uint64_t rbx; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rsi; + uint64_t rdi; + uint64_t rip; + uint64_t rflags; + uint64_t rsp; + uint16_t cs; + uint16_t ds; + uint16_t es; + uint16_t fs; + uint16_t gs; + uint16_t ss; +} GPR; + +#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname)) + +// clang-format off +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { \ + #reg, alt, sizeof(((GPR *)nullptr)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, \ + {kind1, kind2, kind3, kind4, lldb_##reg##_x86_64 }, nullptr, nullptr, \ + nullptr, 0 \ + } + +// clang-format off +static RegisterInfo g_register_infos_x86_64[] = { +// General purpose registers EH_Frame DWARF Generic Process Plugin +// =========================== ================== ================ ========================= ==================== + DEFINE_GPR(rax, nullptr, dwarf_rax_x86_64, dwarf_rax_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbx, nullptr, dwarf_rbx_x86_64, dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rcx, "arg4", dwarf_rcx_x86_64, dwarf_rcx_x86_64, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdx, "arg3", dwarf_rdx_x86_64, dwarf_rdx_x86_64, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdi, "arg1", dwarf_rdi_x86_64, dwarf_rdi_x86_64, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsi, "arg2", dwarf_rsi_x86_64, dwarf_rsi_x86_64, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbp, "fp", dwarf_rbp_x86_64, dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsp, "sp", dwarf_rsp_x86_64, dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(r8, "arg5", dwarf_r8_x86_64, dwarf_r8_x86_64, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM), + DEFINE_GPR(r9, "arg6", dwarf_r9_x86_64, dwarf_r9_x86_64, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM), + DEFINE_GPR(r10, nullptr, dwarf_r10_x86_64, dwarf_r10_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r11, nullptr, dwarf_r11_x86_64, dwarf_r11_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r12, nullptr, dwarf_r12_x86_64, dwarf_r12_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r13, nullptr, dwarf_r13_x86_64, dwarf_r13_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r14, nullptr, dwarf_r14_x86_64, dwarf_r14_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r15, nullptr, dwarf_r15_x86_64, dwarf_r15_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rip, "pc", dwarf_rip_x86_64, dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(rflags, "flags", dwarf_rflags_x86_64, dwarf_rflags_x86_64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(cs, nullptr, dwarf_cs_x86_64, dwarf_cs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs, nullptr, dwarf_fs_x86_64, dwarf_fs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs, nullptr, dwarf_gs_x86_64, dwarf_gs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ss, nullptr, dwarf_ss_x86_64, dwarf_ss_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ds, nullptr, dwarf_ds_x86_64, dwarf_ds_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(es, nullptr, dwarf_es_x86_64, dwarf_es_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), +}; + +static std::vector &GetPrivateRegisterInfoVector() { + static std::vector g_register_infos; + return g_register_infos; +} + +static std::unique_ptr g_private_reg_interface = nullptr; + +static const RegisterInfo * +GetRegisterInfo_wow64(const lldb_private::ArchSpec &arch) { + // A wow64 register info is the same as the i386's. + std::vector &g_register_infos = + GetPrivateRegisterInfoVector(); + + if (g_register_infos.empty()) { + g_private_reg_interface.reset( + new RegisterContextWindows_wow64(arch)); + const RegisterInfo *base_info = g_private_reg_interface->GetRegisterInfo(); + g_register_infos.insert(g_register_infos.end(), &base_info[0], + &base_info[g_private_reg_interface->GetRegisterCount()]); + } + + return g_register_infos.data(); + } + +static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return GetRegisterInfo_wow64(target_arch); + case llvm::Triple::x86_64: + return g_register_infos_x86_64; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + assert(g_private_reg_interface && + "wow64 register info not yet filled."); + return static_cast(g_private_reg_interface->GetRegisterCount()); + case llvm::Triple::x86_64: + return static_cast(sizeof(g_register_infos_x86_64) / + sizeof(g_register_infos_x86_64[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +static uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + assert(g_private_reg_interface && + "wow64 register info not yet filled."); + return static_cast(g_private_reg_interface->GetUserRegisterCount()); + case llvm::Triple::x86_64: + return static_cast(sizeof(g_register_infos_x86_64) / + sizeof(g_register_infos_x86_64[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextWindows_x86_64::RegisterContextWindows_x86_64( + const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)), + m_user_register_count(GetUserRegisterInfoCount(target_arch)) { +} + +size_t RegisterContextWindows_x86_64::GetGPRSize() const { return sizeof(GPR); } Index: source/Plugins/Process/Windows/Common/CMakeLists.txt =================================================================== --- source/Plugins/Process/Windows/Common/CMakeLists.txt +++ source/Plugins/Process/Windows/Common/CMakeLists.txt @@ -1,6 +1,13 @@ set(PROC_WINDOWS_COMMON_SOURCES DebuggerThread.cpp LocalDebugDelegate.cpp + NativeProcessWindows.cpp + NativeRegisterContextWindows.cpp + NativeRegisterContextWindows_i386.cpp + NativeRegisterContextWindows_wow64.cpp + NativeRegisterContextWindows_x86_64.cpp + NativeThreadWindows.cpp + ProcessDebugger.cpp ProcessWindows.cpp ProcessWindowsLog.cpp RegisterContextWindows.cpp Index: source/Plugins/Process/Windows/Common/DebuggerThread.cpp =================================================================== --- source/Plugins/Process/Windows/Common/DebuggerThread.cpp +++ source/Plugins/Process/Windows/Common/DebuggerThread.cpp @@ -346,7 +346,8 @@ // we use simply to wake up the DebuggerThread so that we can close out the // debug loop. if (m_pid_to_detach != 0 && - info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) { + (info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT || + info.ExceptionRecord.ExceptionCode == 0x4000001FL /* WOW64 STATUS_WX86_BREAKPOINT */)) { LLDB_LOG(log, "Breakpoint exception is cue to detach from process {0:x}", m_pid_to_detach.load()); ::DebugActiveProcessStop(m_pid_to_detach); Index: source/Plugins/Process/Windows/Common/NativeProcessWindows.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeProcessWindows.h @@ -0,0 +1,191 @@ +//===-- NativeProcessWindows.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeProcessWindows_h_ +#define liblldb_NativeProcessWindows_h_ + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/lldb-forward.h" +#include "llvm/Support/Mutex.h" + +#include "IDebugDelegate.h" +#include "ProcessDebugger.h" + +namespace lldb_private { + +class HostProcess; +class ProcessWindowsData; +class NativeProcessWindows; +class NativeThreadWindows; +class NativeDebugDelegate; + +typedef std::shared_ptr NativeDebugDelegateSP; + +//------------------------------------------------------------------ +// NativeProcessWindows +//------------------------------------------------------------------ +class NativeProcessWindows : public NativeProcessProtocol, + public ProcessDebugger { + +public: + class Factory : public NativeProcessProtocol::Factory { + public: + llvm::Expected> + Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate, + MainLoop &mainloop) const override; + + llvm::Expected> + Attach(lldb::pid_t pid, NativeDelegate &native_delegate, + MainLoop &mainloop) const override; + }; + + Status DoLaunch(ProcessLaunchInfo &launch_info); + + Status DoAttachToProcessWithID(lldb::pid_t pid, + const ProcessAttachInfo &attach_info); + + Status Resume(const ResumeActionList &resume_actions) override; + + Status Halt() override; + + Status Detach() override; + + Status Signal(int signo) override; + + Status Interrupt() override; + + Status Kill() override; + + Status IgnoreSignals(llvm::ArrayRef signals) override; + + Status GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) override; + + Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, + size_t &bytes_written) override; + + Status AllocateMemory(size_t size, uint32_t permissions, + lldb::addr_t &addr) override; + + Status DeallocateMemory(lldb::addr_t addr) override; + + lldb::addr_t GetSharedLibraryInfoAddress() override; + + bool IsAlive() const override; + + size_t UpdateThreads() override; + + const ArchSpec &GetArchitecture() const override { return m_arch; } + + void SetArchitecture(const ArchSpec &arch_spec) { m_arch = arch_spec; } + + Status SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) override; + + Status RemoveBreakpoint(lldb::addr_t addr, bool hardware = false) override; + + llvm::ErrorOr> + GetAuxvData() const override; + + Status GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) override; + + Status GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) override; + + // Overrides + void OnExitProcess(uint32_t exit_code) override; + void OnDebuggerConnected(lldb::addr_t image_base) override; + ExceptionResult OnDebugException(bool first_chance, + const ExceptionRecord &record) override; + void OnCreateThread(const HostThread &thread) override; + void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override; + +protected: + NativeThreadWindows *GetThreadByID(lldb::tid_t thread_id); + + bool FindSoftwareBreakpoint(lldb::addr_t addr); + + void StopThread(lldb::tid_t thread_id, lldb::StopReason reason, + std::string description = ""); + + void SetStopReasonForThread(NativeThreadWindows &thread, + lldb::StopReason reason, + std::string description = ""); + +private: + ArchSpec m_arch; + NativeProcessWindows(lldb::pid_t pid, int terminal_fd, + NativeDelegate &delegate, const ArchSpec &arch); +}; + +//------------------------------------------------------------------ +// NativeDebugDelegate +//------------------------------------------------------------------ +class NativeDebugDelegate : public IDebugDelegate { +public: + NativeDebugDelegate(NativeProcessWindows *process) : m_process(process) {} + + void OnExitProcess(uint32_t exit_code) { + if (m_process) + m_process->OnExitProcess(exit_code); + } + + void OnDebuggerConnected(lldb::addr_t image_base) { + if (m_process) + m_process->OnDebuggerConnected(image_base); + } + + ExceptionResult OnDebugException(bool first_chance, + const ExceptionRecord &record) { + if (m_process) + return m_process->OnDebugException(first_chance, record); + return ExceptionResult::MaskException; + } + + void OnCreateThread(const HostThread &thread) { + if (m_process) + m_process->OnCreateThread(thread); + } + + void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) { + if (m_process) + m_process->OnExitThread(thread_id, exit_code); + } + + void OnLoadDll(const lldb_private::ModuleSpec &module_spec, + lldb::addr_t module_addr) { + if (m_process) + m_process->OnLoadDll(module_spec, module_addr); + } + + void OnUnloadDll(lldb::addr_t module_addr) { + if (m_process) + m_process->OnUnloadDll(module_addr); + } + + void OnDebugString(const std::string &string) { + if (m_process) + m_process->OnDebugString(string); + } + + void OnDebuggerError(const Status &error, uint32_t type) { + if (m_process) + return m_process->OnDebuggerError(error, type); + } + +private: + NativeProcessWindows *m_process; +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeProcessWindows_h_ Index: source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp @@ -0,0 +1,549 @@ +//===-- NativeProcessWindows.cpp --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/windows/windows.h" +#include + +#include "NativeProcessWindows.h" +#include "NativeThreadWindows.h" + +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostNativeProcessBase.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Host/windows/AutoHandle.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Process.h" +#include "llvm/Support/Error.h" + +#include "lldb/Utility/State.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/raw_ostream.h" + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "ProcessWindowsLog.h" + +#include + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +namespace { +Status GetProcessBaseAddress(lldb::pid_t pid, lldb::addr_t &base_addr) { + Status error; + base_addr = LLDB_INVALID_ADDRESS; + + AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid)); + if (snapshot.IsValid()) { + MODULEENTRY32W me; + me.dwSize = sizeof(MODULEENTRY32W); + if (Module32FirstW(snapshot.get(), &me)) { + // The first module is always the EXE or DLL itself. + base_addr = (addr_t)me.modBaseAddr; + return Status(); + } + } + + error.SetError(::GetLastError(), lldb::ErrorType::eErrorTypeWin32); + return error; +} + +} // namespace + +namespace lldb_private { +class ProcessWindowsData { +public: + ProcessWindowsData(bool stop_at_entry) : m_stop_at_entry(stop_at_entry) { + m_initial_stop_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); + } + + ~ProcessWindowsData() { ::CloseHandle(m_initial_stop_event); } + + Status m_launch_error; + DebuggerThreadSP m_debugger; + StopInfoSP m_pending_stop_info; + HANDLE m_initial_stop_event = nullptr; + bool m_initial_stop_received = false; + bool m_stop_at_entry; + std::map m_new_threads; + std::set m_exited_threads; +}; + +NativeProcessWindows::NativeProcessWindows(lldb::pid_t pid, int terminal_fd, + NativeDelegate &delegate, + const ArchSpec &arch) + : NativeProcessProtocol(pid, terminal_fd, delegate), ProcessDebugger(), + m_arch(arch) {} + +Status NativeProcessWindows::Resume(const ResumeActionList &resume_actions) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + Status error; + llvm::sys::ScopedLock lock(m_mutex); + + StateType state = GetState(); + if (state == eStateStopped || state == eStateCrashed) { + LLDB_LOG(log, "process {0} is in state {1}. Resuming...", + GetDebuggedProcess().GetProcessId(), state); + + // Resume the debug loop. + ContinueDebugger(); + + LLDB_LOG(log, "resuming {0} threads.", m_threads.size()); + + bool failed = false; + for (uint32_t i = 0; i < m_threads.size(); ++i) { + auto thread = static_cast(m_threads[i].get()); + const ResumeAction *const action = + resume_actions.GetActionForThread(thread->GetID(), true); + if (action == nullptr) + continue; + + switch (action->state) { + case eStateRunning: + case eStateStepping: { + Status result = thread->DoResume(action->state); + if (result.Fail()) { + failed = true; + LLDB_LOG(log, + "Trying to resume thread at index {0}, but failed with " + "error {1}.", + i, result); + } + break; + } + case eStateSuspended: + case eStateStopped: + llvm_unreachable("Unexpected state"); + + default: + return Status( + "NativeProcessWindows::%s (): unexpected state %s specified " + "for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString(action->state), GetID(), + thread->GetID()); + } + } + + if (failed) { + error.SetErrorString("NativeProcessWindows::DoResume failed"); + return error; + } else { + SetState(eStateRunning); + } + } else { + LLDB_LOG(log, "error: process {0} is in state {1}. Returning...", + GetDebuggedProcess().GetProcessId(), GetState()); + } + + return error; +} + +NativeThreadWindows * +NativeProcessWindows::GetThreadByID(lldb::tid_t thread_id) { + return static_cast( + NativeProcessProtocol::GetThreadByID(thread_id)); +} + +Status NativeProcessWindows::Halt() { + bool caused_stop = false; + StateType state = GetState(); + if (state != eStateStopped) + return HaltProcess(caused_stop); + return Status(); +} + +Status NativeProcessWindows::Detach() { + Status error; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + StateType state = GetState(); + if (state != eStateExited && state != eStateDetached) { + error = DetachProcess(); + if (error.Success()) + SetState(eStateDetached); + else + LLDB_LOG(log, "Detaching process error: {0}", error); + } else { + error.SetErrorStringWithFormat("error: process {0} in state = {1}, but " + "cannot detach it in this state.", + GetID(), state); + LLDB_LOG(log, "error: {0}", error); + } + return error; +} + +Status NativeProcessWindows::Signal(int signo) { + Status error; + error.SetErrorString("Windows does not support sending signals to processes"); + return error; +} + +Status NativeProcessWindows::Interrupt() { return Halt(); } + +Status NativeProcessWindows::Kill() { + Status error; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + StateType state = GetState(); + if (state != eStateExited && state != eStateDetached) + return DestroyProcess(); + else { + error.SetErrorStringWithFormat( + "cannot destroy process {0} while state = {1}", GetID(), state); + LLDB_LOG(log, "error: {0}", error); + } + return error; +} + +Status NativeProcessWindows::IgnoreSignals(llvm::ArrayRef signals) { + return Status(); +} + +Status NativeProcessWindows::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + return ProcessDebugger::GetMemoryRegionInfo(load_addr, range_info); +} + +Status NativeProcessWindows::ReadMemory(lldb::addr_t addr, void *buf, + size_t size, size_t &bytes_read) { + return ProcessDebugger::ReadMemory(addr, buf, size, bytes_read); +} + +Status NativeProcessWindows::WriteMemory(lldb::addr_t addr, const void *buf, + size_t size, size_t &bytes_written) { + return ProcessDebugger::WriteMemory(addr, buf, size, bytes_written); +} + +Status NativeProcessWindows::AllocateMemory(size_t size, uint32_t permissions, + lldb::addr_t &addr) { + return ProcessDebugger::AllocateMemory(size, permissions, addr); +} + +Status NativeProcessWindows::DeallocateMemory(lldb::addr_t addr) { + return ProcessDebugger::DeallocateMemory(addr); +} + +lldb::addr_t NativeProcessWindows::GetSharedLibraryInfoAddress() { return 0; } + +bool NativeProcessWindows::IsAlive() const { + StateType state = GetState(); + switch (state) { + case eStateCrashed: + case eStateDetached: + case eStateExited: + case eStateInvalid: + case eStateUnloaded: + return false; + default: + return true; + } +} + +void NativeProcessWindows::SetStopReasonForThread(NativeThreadWindows &thread, + lldb::StopReason reason, + std::string description) { + SetCurrentThreadID(thread.GetID()); + + ThreadStopInfo stop_info; + stop_info.reason = reason; + + // No signal support on Windows but required to provide a 'valid' signum. + if (reason == StopReason::eStopReasonException) { + stop_info.details.exception.type = 0; + stop_info.details.exception.data_count = 0; + } else { + stop_info.details.signal.signo = SIGTRAP; + } + + thread.SetStopReason(stop_info, description); +} + +void NativeProcessWindows::StopThread(lldb::tid_t thread_id, + lldb::StopReason reason, + std::string description) { + NativeThreadWindows *thread = GetThreadByID(thread_id); + if (!thread) + return; + + for (uint32_t i = 0; i < m_threads.size(); ++i) { + auto t = static_cast(m_threads[i].get()); + Status error = t->DoStop(); + if (error.Fail()) + exit(1); + } + SetStopReasonForThread(*thread, reason, description); +} + +size_t NativeProcessWindows::UpdateThreads() { return m_threads.size(); } + +llvm::ErrorOr> +NativeProcessWindows::GetAuxvData() const { + // Not available on this target. + return nullptr; +} + +bool NativeProcessWindows::FindSoftwareBreakpoint(lldb::addr_t addr) { + auto it = m_software_breakpoints.find(addr); + if (it == m_software_breakpoints.end()) + return false; + return true; +} + +Status NativeProcessWindows::SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) { + if (hardware) + return SetHardwareBreakpoint(addr, size); + return SetSoftwareBreakpoint(addr, size); +} + +Status NativeProcessWindows::RemoveBreakpoint(lldb::addr_t addr, + bool hardware) { + if (hardware) + return RemoveHardwareBreakpoint(addr); + return RemoveSoftwareBreakpoint(addr); +} + +Status NativeProcessWindows::GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) { + FileSpec module_file_spec(module_path); + FileSystem::Instance().Resolve(module_file_spec); + return Status(); +} + +Status +NativeProcessWindows::GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) { + return GetProcessBaseAddress(m_pid, load_addr); +} + +void NativeProcessWindows::OnExitProcess(uint32_t exit_code) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + LLDB_LOG(log, "Process {0} exited with code {1}", GetID(), exit_code); + + ProcessDebugger::OnExitProcess(exit_code); + + // No signal involved. It is just an exit event. + WaitStatus wait_status(WaitStatus::Exit, exit_code); + SetExitStatus(wait_status, true); + + // Notify the native delegate. + SetState(eStateExited, true); +} + +void NativeProcessWindows::OnDebuggerConnected(lldb::addr_t image_base) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + LLDB_LOG(log, "Debugger connected to process {0}. Image base = {1:x}", + GetDebuggedProcess().GetProcessId(), image_base); + + // This is the earliest chance we can resolve the process ID and architecutre + // if we don't know them yet. + if (GetID() == LLDB_INVALID_PROCESS_ID) + SetID(GetDebuggedProcess().GetProcessId()); + + if (GetArchitecture().GetMachine() == llvm::Triple::UnknownArch) { + ProcessInstanceInfo process_info; + if (!Host::GetProcessInfo(GetDebuggedProcess().GetProcessId(), + process_info)) { + LLDB_LOG(log, "Cannot get process information during debugger connecting " + "to process"); + return; + } + SetArchitecture(process_info.GetArchitecture()); + } + + // The very first one shall always be the matin thread. + assert(m_threads.empty()); + m_threads.push_back( + llvm::make_unique(*this, GetMainThread())); +} + +ExceptionResult +NativeProcessWindows::OnDebugException(bool first_chance, + const ExceptionRecord &record) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_EXCEPTION); + llvm::sys::ScopedLock lock(m_mutex); + + // Let the debugger establish the internal status. + ProcessDebugger::OnDebugException(first_chance, record); + + static bool initial_stop = false; + if (!first_chance) { + SetState(eStateStopped, false); + } + + ExceptionResult result = ExceptionResult::SendToApplication; + switch (record.GetExceptionCode()) { + case EXCEPTION_SINGLE_STEP: + case 0x4000001EL: // WOW64 STATUS_WX86_SINGLE_STEP + StopThread(record.GetThreadID(), StopReason::eStopReasonTrace); + SetState(eStateStopped, true); + + // Continue the debugger. + return ExceptionResult::MaskException; + + case EXCEPTION_BREAKPOINT: + case 0x4000001FL: // WOW64 STATUS_WX86_BREAKPOINT + + if (FindSoftwareBreakpoint(record.GetExceptionAddress())) { + LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.", + record.GetExceptionAddress()); + + StopThread(record.GetThreadID(), StopReason::eStopReasonBreakpoint); + + if (NativeThreadWindows *stop_thread = + GetThreadByID(record.GetThreadID())) { + auto ®ister_context = stop_thread->GetRegisterContext(); + // The current EIP is AFTER the BP opcode, which is one byte '0xCC' + uint64_t pc = register_context.GetPC() - 1; + register_context.SetPC(pc); + } + + SetState(eStateStopped, true); + return ExceptionResult::MaskException; + } + + if (!initial_stop) { + initial_stop = true; + LLDB_LOG( + log, + "Hit loader breakpoint at address {0:x}, setting initial stop event.", + record.GetExceptionAddress()); + + // We are required to report the reason for the first stop after + // launching or being attached. + if (NativeThreadWindows *thread = GetThreadByID(record.GetThreadID())) + SetStopReasonForThread(*thread, StopReason::eStopReasonBreakpoint); + + // Do not notify the native delegate (e.g. llgs) since at this moment + // the program hasn't returned from Factory::Launch() and the delegate + // might not have an valid native process to operate on. + SetState(eStateStopped, false); + + // Hit the initial stop. Continue the application. + return ExceptionResult::BreakInDebugger; + } + + // Fall through + default: + LLDB_LOG(log, + "Debugger thread reported exception {0:x} at address {1:x} " + "(first_chance={2})", + record.GetExceptionCode(), record.GetExceptionAddress(), + first_chance); + + { + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " + << llvm::format_hex(record.GetExceptionCode(), 8) + << " encountered at address " + << llvm::format_hex(record.GetExceptionAddress(), 8); + StopThread(record.GetThreadID(), StopReason::eStopReasonException, + desc_stream.str().c_str()); + + SetState(eStateStopped, true); + } + + // For non-breakpoints, give the application a chance to handle the + // exception first. + if (first_chance) + result = ExceptionResult::SendToApplication; + else + result = ExceptionResult::BreakInDebugger; + } + + return result; +} + +void NativeProcessWindows::OnCreateThread(const HostThread &new_thread) { + llvm::sys::ScopedLock lock(m_mutex); + m_threads.push_back( + llvm::make_unique(*this, new_thread)); +} + +void NativeProcessWindows::OnExitThread(lldb::tid_t thread_id, + uint32_t exit_code) { + llvm::sys::ScopedLock lock(m_mutex); + NativeThreadWindows *thread = GetThreadByID(thread_id); + if (!thread) + return; + + for (auto t = m_threads.begin(); t != m_threads.end();) { + if ((*t)->GetID() == thread_id) { + static_cast((*t).get())->DoDestroy(); + t = m_threads.erase(t); + } else { + ++t; + } + } +} + +Status NativeProcessWindows::DoLaunch(ProcessLaunchInfo &launch_info) { + DebugDelegateSP delegate(new NativeDebugDelegate(this)); + return LaunchProcess(launch_info, delegate); +} + +Status NativeProcessWindows::DoAttachToProcessWithID( + lldb::pid_t pid, const ProcessAttachInfo &attach_info) { + DebugDelegateSP delegate(new NativeDebugDelegate(this)); + Status error = AttachProcess(pid, attach_info, delegate); + if (error.Success()) + SetID(GetDebuggedProcess().GetProcessId()); + return error; +} + +llvm::Expected> +NativeProcessWindows::Factory::Launch( + ProcessLaunchInfo &launch_info, + NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop) const { + // The process id and process architecture will be updated upon successfully + // launching. + std::unique_ptr process_up(new NativeProcessWindows( + LLDB_INVALID_PROCESS_ID, + launch_info.GetPTY().ReleaseMasterFileDescriptor(), native_delegate, + launch_info.GetArchitecture())); + + Status error = process_up->DoLaunch(launch_info); + if (error.Fail()) + return error.ToError(); + + return std::move(process_up); +} + +llvm::Expected> +NativeProcessWindows::Factory::Attach( + lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop) const { + Status error; + // Query the process architecture to ensure that we could have a valid + // register context later. + ProcessInstanceInfo process_info; + if (!Host::GetProcessInfo(pid, process_info)) { + error.SetErrorString("Cannot get process information"); + return error.ToError(); + } + + ProcessAttachInfo attach_info; + attach_info.SetProcessID(pid); + attach_info.SetArchitecture(process_info.GetArchitecture()); + + // Set pty master fd invalid since we can query it from nowhere. + std::unique_ptr process_up(new NativeProcessWindows( + pid, -1, native_delegate, process_info.GetArchitecture())); + + error = process_up->DoAttachToProcessWithID(pid, attach_info); + if (error.Fail()) + return error.ToError(); + + return std::move(process_up); +} +} // namespace lldb_private Index: source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.h @@ -0,0 +1,40 @@ +//===-- NativeRegisterContextWindows.h --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeRegisterContextWindows_h_ +#define liblldb_NativeRegisterContextWindows_h_ + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Utility/DataBufferHeap.h" + +namespace lldb_private { + +class NativeThreadWindows; + +class NativeRegisterContextWindows : public NativeRegisterContextRegisterInfo { +public: + NativeRegisterContextWindows::NativeRegisterContextWindows( + NativeThreadProtocol &native_thread, + RegisterInfoInterface *reg_info_interface_p); + + static std::unique_ptr + CreateHostNativeRegisterContextWindows(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + +protected: + Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp, + const size_t data_size); + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp, + const size_t data_size); +}; + +} // namespace lldb_private + +#endif // liblldb_NativeRegisterContextWindows_h_ Index: source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.cpp @@ -0,0 +1,103 @@ +//===-- NativeRegisterContextWindows.cpp ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Utility/Log.h" + +#include "NativeRegisterContextWindows.h" +#include "NativeThreadWindows.h" +#include "ProcessWindowsLog.h" + +using namespace lldb; +using namespace lldb_private; + +NativeRegisterContextWindows::NativeRegisterContextWindows( + NativeThreadProtocol &thread, RegisterInfoInterface *reg_info_interface_p) + : NativeRegisterContextRegisterInfo(thread, reg_info_interface_p) {} + +Status +NativeRegisterContextWindows::ReadAllRegisterValues(lldb::DataBufferSP &data_sp, + const size_t data_size) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + + data_sp.reset(new DataBufferHeap(data_size, 0)); + if (!data_sp) { + error.SetErrorStringWithFormat( + "failed to allocate DataBufferHeap instance of size %" PRIu64, + data_size); + return error; + } + + ::CONTEXT tls_context; + memset(&tls_context, 0, sizeof(tls_context)); + + NativeThreadWindows *wthread = static_cast(&m_thread); + auto host_thread = wthread->GetHostThread(); + tls_context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + if (!::GetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), + &tls_context)) { + LLDB_LOG(log, "GetThreadContext failed with error {0}", ::GetLastError()); + return Status(::GetLastError(), eErrorTypeWin32); + } + LLDB_LOG(log, "successfully updated the register values."); + + uint8_t *dst = data_sp->GetBytes(); + if (dst == nullptr) { + error.SetErrorStringWithFormat("DataBufferHeap instance of size %" PRIu64 + " returned a null pointer", + data_size); + return error; + } + + ::memcpy(dst, &tls_context, data_size); + return error; +} + +Status NativeRegisterContextWindows::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp, const size_t data_size) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextWindows::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != data_size) { + error.SetErrorStringWithFormatv( + "data_sp contained mismatched data size, expected {0}, actual {1}", + data_size, data_sp->GetByteSize()); + return error; + } + + uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error.SetErrorStringWithFormat("NativeRegisterContextWindows::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + + ::CONTEXT tls_context; + memcpy(&tls_context, data_sp->GetBytes(), data_size); + + NativeThreadWindows *wthread = static_cast(&m_thread); + auto host_thread = wthread->GetHostThread(); + if (!::SetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), + &tls_context)) { + LLDB_LOG(log, "SetThreadContext failed with error {0}", ::GetLastError()); + return Status(::GetLastError(), eErrorTypeWin32); + } + return error; +} Index: source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.h @@ -0,0 +1,74 @@ +//===-- NativeRegisterContextWindows_i386.h ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN64) +#ifndef liblldb_NativeRegisterContextWindows_i386_h_ +#define liblldb_NativeRegisterContextWindows_i386_h_ + +#include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +#include "NativeRegisterContextWindows.h" + +namespace lldb_private { + +class NativeThreadWindows; + +class NativeRegisterContextWindows_i386 : public NativeRegisterContextWindows { +public: + NativeRegisterContextWindows_i386(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + + uint32_t GetRegisterSetCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool ClearHardwareWatchpoint(uint32_t wp_index) override; + + Status ClearAllHardwareWatchpoints() override; + + Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, + uint32_t wp_index); + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + +protected: + Status GPRRead(const uint32_t reg, RegisterValue ®_value); + Status GPRWrite(const uint32_t reg, const RegisterValue ®_value); + +private: + bool IsGPR(uint32_t reg_index) const; +}; + +} // namespace lldb_private + +#endif // liblldb_NativeRegisterContextWindows_i386_h_ +#endif // defined(_WIN64) Index: source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp @@ -0,0 +1,340 @@ +//===-- NativeRegisterContextWindows_i386.cpp -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN32) && !defined(_WIN64) + +#include "NativeRegisterContextWindows_i386.h" + +#include "NativeThreadWindows.h" +#include "Plugins/Process/Utility/RegisterContextWindows_i386.h" +#include "ProcessWindowsLog.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { +static const uint32_t g_gpr_regnums_i386[] = { + lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, lldb_edx_i386, + lldb_edi_i386, lldb_esi_i386, lldb_ebp_i386, lldb_esp_i386, + lldb_eip_i386, lldb_eflags_i386, lldb_cs_i386, lldb_fs_i386, + lldb_gs_i386, lldb_ss_i386, lldb_ds_i386, lldb_es_i386, + LLDB_INVALID_REGNUM // Register sets must be terminated with this flag. +}; +size_t k_num_gprs = llvm::array_lengthof(g_gpr_regnums_i386) - 1; + +static const RegisterSet g_reg_sets_i386[] = { + {"General Purpose Registers", "gpr", k_num_gprs, g_gpr_regnums_i386}, +}; +size_t k_num_register_sets = llvm::array_lengthof(g_reg_sets_i386); + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) { + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 4) && + "Register setting path assumes this is a 32-bit host"); + return new RegisterContextWindows_i386(target_arch); +} + +} // namespace + +#define REG_CONTEXT_SIZE sizeof(::CONTEXT) + +std::unique_ptr +NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { + return llvm::make_unique(target_arch, + native_thread); +} + +NativeRegisterContextWindows_i386::NativeRegisterContextWindows_i386( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) + : NativeRegisterContextWindows(native_thread, + CreateRegisterInfoInterface(target_arch)) {} + +bool NativeRegisterContextWindows_i386::IsGPR(uint32_t reg_index) const { + return (reg_index < k_first_alias_i386); +} + +uint32_t NativeRegisterContextWindows_i386::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextWindows_i386::GetRegisterSet(uint32_t set_index) const { + if (set_index >= k_num_register_sets) + return nullptr; + return &g_reg_sets_i386[set_index]; +} + +Status NativeRegisterContextWindows_i386::GPRRead(const uint32_t reg, + RegisterValue ®_value) { + Status error; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + + ::CONTEXT tls_context; + NativeThreadWindows *wthread = static_cast(&m_thread); + + auto host_thread = wthread->GetHostThread(); + memset(&tls_context, 0, sizeof(tls_context)); + tls_context.ContextFlags = + CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; + if (!::GetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), + &tls_context)) { + LLDB_LOG(log, + "GetThreadContext failed with error {0} while caching register " + "values.", + ::GetLastError()); + return Status("error"); + } + LLDB_LOG(log, "successfully updated the register values."); + + switch (reg) { + case lldb_eax_i386: + reg_value.SetUInt32(tls_context.Eax); + break; + case lldb_ebx_i386: + reg_value.SetUInt32(tls_context.Ebx); + break; + case lldb_esi_i386: + reg_value.SetUInt32(tls_context.Esi); + break; + case lldb_ebp_i386: + reg_value.SetUInt32(tls_context.Ebp); + break; + case lldb_esp_i386: + reg_value.SetUInt32(tls_context.Esp); + break; + case lldb_eip_i386: + reg_value.SetUInt32(tls_context.Eip); + break; + case lldb_eflags_i386: + reg_value.SetUInt32(tls_context.EFlags); + break; + case lldb_cs_i386: + reg_value.SetUInt32(tls_context.SegCs); + break; + case lldb_fs_i386: + reg_value.SetUInt32(tls_context.SegFs); + break; + case lldb_gs_i386: + reg_value.SetUInt32(tls_context.SegGs); + break; + case lldb_ss_i386: + reg_value.SetUInt32(tls_context.SegSs); + break; + case lldb_ds_i386: + reg_value.SetUInt32(tls_context.SegDs); + break; + case lldb_es_i386: + reg_value.SetUInt32(tls_context.SegEs); + break; + } + + return error; +} + +Status +NativeRegisterContextWindows_i386::GPRWrite(const uint32_t reg, + const RegisterValue ®_value) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + + ::CONTEXT tls_context; + memset(&tls_context, 0, sizeof(tls_context)); + + NativeThreadWindows *wthread = static_cast(&m_thread); + auto host_thread = wthread->GetHostThread(); + tls_context.ContextFlags = + CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; + if (!::GetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), + &tls_context)) { + LLDB_LOG(log, + "GetThreadContext failed with error {0} while caching register " + "values.", + ::GetLastError()); + return Status("error"); + } + LLDB_LOG(log, "successfully updated the register values."); + + switch (reg) { + case lldb_eax_i386: + tls_context.Eax = reg_value.GetAsUInt32(); + break; + case lldb_ebx_i386: + tls_context.Ebx = reg_value.GetAsUInt32(); + break; + case lldb_ecx_i386: + tls_context.Ecx = reg_value.GetAsUInt32(); + break; + case lldb_edx_i386: + tls_context.Edx = reg_value.GetAsUInt32(); + break; + case lldb_edi_i386: + tls_context.Edi = reg_value.GetAsUInt32(); + break; + case lldb_esi_i386: + tls_context.Esi = reg_value.GetAsUInt32(); + break; + case lldb_ebp_i386: + tls_context.Ebp = reg_value.GetAsUInt32(); + break; + case lldb_esp_i386: + tls_context.Esp = reg_value.GetAsUInt32(); + break; + case lldb_eip_i386: + tls_context.Eip = reg_value.GetAsUInt32(); + break; + case lldb_eflags_i386: + tls_context.EFlags = reg_value.GetAsUInt32(); + break; + case lldb_cs_i386: + tls_context.SegCs = reg_value.GetAsUInt32(); + break; + case lldb_fs_i386: + tls_context.SegFs = reg_value.GetAsUInt32(); + break; + case lldb_gs_i386: + tls_context.SegGs = reg_value.GetAsUInt32(); + break; + case lldb_ss_i386: + tls_context.SegSs = reg_value.GetAsUInt32(); + break; + case lldb_ds_i386: + tls_context.SegDs = reg_value.GetAsUInt32(); + break; + case lldb_es_i386: + tls_context.SegEs = reg_value.GetAsUInt32(); + break; + } + + bool success = ::SetThreadContext( + host_thread.GetNativeThread().GetSystemHandle(), &tls_context); + + if (!success) + return Status(::GetLastError(), eErrorTypeWin32); + + return error; +} + +Status +NativeRegisterContextWindows_i386::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRRead(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_i386::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRRead(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_i386::ReadAllRegisterValues( + lldb::DataBufferSP &data_sp) { + return NativeRegisterContextWindows::ReadAllRegisterValues(data_sp, + REG_CONTEXT_SIZE); +} + +Status NativeRegisterContextWindows_i386::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + return NativeRegisterContextWindows::WriteAllRegisterValues(data_sp, + REG_CONTEXT_SIZE); +} + +Status NativeRegisterContextWindows_i386::IsWatchpointHit(uint32_t wp_index, + bool &is_hit) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_i386::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_i386::IsWatchpointVacant(uint32_t wp_index, + bool &is_vacant) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_i386::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + return Status("unimplemented"); +} + +bool NativeRegisterContextWindows_i386::ClearHardwareWatchpoint( + uint32_t wp_index) { + return false; +} + +Status NativeRegisterContextWindows_i386::ClearAllHardwareWatchpoints() { + return Status("unimplemented"); +} + +uint32_t NativeRegisterContextWindows_i386::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextWindows_i386::GetWatchpointAddress(uint32_t wp_index) { + return LLDB_INVALID_ADDRESS; +} + +uint32_t NativeRegisterContextWindows_i386::NumSupportedHardwareWatchpoints() { + // Not implemented + return 0; +} + +#endif Index: source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_wow64.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_wow64.h @@ -0,0 +1,74 @@ +//===-- NativeRegisterContextWindows_wow64.h --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN64) +#ifndef liblldb_NativeRegisterContextWindows_wow64_h_ +#define liblldb_NativeRegisterContextWindows_wow64_h_ + +#include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +#include "NativeRegisterContextWindows.h" + +namespace lldb_private { + +class NativeThreadWindows; + +class NativeRegisterContextWindows_wow64 : public NativeRegisterContextWindows { +public: + NativeRegisterContextWindows_wow64(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + + uint32_t GetRegisterSetCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool ClearHardwareWatchpoint(uint32_t wp_index) override; + + Status ClearAllHardwareWatchpoints() override; + + Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, + uint32_t wp_index); + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + +protected: + Status GPRRead(const uint32_t reg, RegisterValue ®_value); + Status GPRWrite(const uint32_t reg, const RegisterValue ®_value); + +private: + bool IsGPR(uint32_t reg_index) const; +}; + +} // namespace lldb_private + +#endif // liblldb_NativeRegisterContextWindows_wow64_h_ +#endif // defined(_WIN64) Index: source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_wow64.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_wow64.cpp @@ -0,0 +1,402 @@ +//===-- NativeRegisterContextWindows_wow64.cpp ----- ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN64) + +#include "NativeRegisterContextWindows_wow64.h" + +#include "NativeThreadWindows.h" +#include "Plugins/Process/Utility/RegisterContextWindows_wow64.h" +#include "ProcessWindowsLog.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { +static const uint32_t g_gpr_regnums_wow64[] = { + lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, lldb_edx_i386, + lldb_edi_i386, lldb_esi_i386, lldb_ebp_i386, lldb_esp_i386, + lldb_eip_i386, lldb_eflags_i386, lldb_cs_i386, lldb_fs_i386, + lldb_gs_i386, lldb_ss_i386, lldb_ds_i386, lldb_es_i386, + LLDB_INVALID_REGNUM // Register sets must be terminated with this flag. +}; +static size_t k_num_gprs = llvm::array_lengthof(g_gpr_regnums_wow64) - 1; + +static const RegisterSet g_reg_sets_wow64[] = { + {"General Purpose Registers", "gpr", + llvm::array_lengthof(g_gpr_regnums_wow64) - 1, g_gpr_regnums_wow64}, +}; +static size_t k_num_register_sets = llvm::array_lengthof(g_gpr_regnums_wow64); + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) { + assert((target_arch.GetAddressByteSize() == 4 && + HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + return new RegisterContextWindows_wow64(target_arch); +} + +} // namespace + +#define REG_CONTEXT_SIZE sizeof(::WOW64_CONTEXT) + +NativeRegisterContextWindows_wow64::NativeRegisterContextWindows_wow64( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) + : NativeRegisterContextWindows(native_thread, + CreateRegisterInfoInterface(target_arch)) {} + +bool NativeRegisterContextWindows_wow64::IsGPR(uint32_t reg_index) const { + return (reg_index < k_first_alias_i386); +} + +uint32_t NativeRegisterContextWindows_wow64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextWindows_wow64::GetRegisterSet(uint32_t set_index) const { + if (set_index >= k_num_register_sets) + return nullptr; + return &g_reg_sets_wow64[set_index]; +} + +Status NativeRegisterContextWindows_wow64::GPRRead(const uint32_t reg, + RegisterValue ®_value) { + Status error; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + + ::WOW64_CONTEXT tls_context; + NativeThreadWindows *wthread = static_cast(&m_thread); + + auto host_thread = wthread->GetHostThread(); + memset(&tls_context, 0, sizeof(tls_context)); + tls_context.ContextFlags = + WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS; + if (!::Wow64GetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), + &tls_context)) { + LLDB_LOG(log, + "GetThreadContext failed with error {0} while caching register " + "values.", + ::GetLastError()); + return Status("error"); + } + LLDB_LOG(log, "successfully updated the register values."); + + switch (reg) { + case lldb_eax_i386: + reg_value.SetUInt32(tls_context.Eax); + break; + case lldb_ebx_i386: + reg_value.SetUInt32(tls_context.Ebx); + break; + case lldb_esi_i386: + reg_value.SetUInt32(tls_context.Esi); + break; + case lldb_ebp_i386: + reg_value.SetUInt32(tls_context.Ebp); + break; + case lldb_esp_i386: + reg_value.SetUInt32(tls_context.Esp); + break; + case lldb_eip_i386: + reg_value.SetUInt32(tls_context.Eip); + break; + case lldb_eflags_i386: + reg_value.SetUInt32(tls_context.EFlags); + break; + case lldb_cs_i386: + reg_value.SetUInt32(tls_context.SegCs); + break; + case lldb_fs_i386: + reg_value.SetUInt32(tls_context.SegFs); + break; + case lldb_gs_i386: + reg_value.SetUInt32(tls_context.SegGs); + break; + case lldb_ss_i386: + reg_value.SetUInt32(tls_context.SegSs); + break; + case lldb_ds_i386: + reg_value.SetUInt32(tls_context.SegDs); + break; + case lldb_es_i386: + reg_value.SetUInt32(tls_context.SegEs); + break; + } + + return error; +} + +Status +NativeRegisterContextWindows_wow64::GPRWrite(const uint32_t reg, + const RegisterValue ®_value) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + + ::WOW64_CONTEXT tls_context; + memset(&tls_context, 0, sizeof(tls_context)); + + NativeThreadWindows *wthread = static_cast(&m_thread); + auto host_thread = wthread->GetHostThread(); + tls_context.ContextFlags = + WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS; + if (!::Wow64GetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), + &tls_context)) { + LLDB_LOG(log, + "GetThreadContext failed with error {0} while caching register " + "values.", + ::GetLastError()); + return Status("error"); + } + LLDB_LOG(log, "successfully updated the register values."); + + switch (reg) { + case lldb_eax_i386: + tls_context.Eax = reg_value.GetAsUInt32(); + break; + case lldb_ebx_i386: + tls_context.Ebx = reg_value.GetAsUInt32(); + break; + case lldb_ecx_i386: + tls_context.Ecx = reg_value.GetAsUInt32(); + break; + case lldb_edx_i386: + tls_context.Edx = reg_value.GetAsUInt32(); + break; + case lldb_edi_i386: + tls_context.Edi = reg_value.GetAsUInt32(); + break; + case lldb_esi_i386: + tls_context.Esi = reg_value.GetAsUInt32(); + break; + case lldb_ebp_i386: + tls_context.Ebp = reg_value.GetAsUInt32(); + break; + case lldb_esp_i386: + tls_context.Esp = reg_value.GetAsUInt32(); + break; + case lldb_eip_i386: + tls_context.Eip = reg_value.GetAsUInt32(); + break; + case lldb_eflags_i386: + tls_context.EFlags = reg_value.GetAsUInt32(); + break; + case lldb_cs_i386: + tls_context.SegCs = reg_value.GetAsUInt32(); + break; + case lldb_fs_i386: + tls_context.SegFs = reg_value.GetAsUInt32(); + break; + case lldb_gs_i386: + tls_context.SegGs = reg_value.GetAsUInt32(); + break; + case lldb_ss_i386: + tls_context.SegSs = reg_value.GetAsUInt32(); + break; + case lldb_ds_i386: + tls_context.SegDs = reg_value.GetAsUInt32(); + break; + case lldb_es_i386: + tls_context.SegEs = reg_value.GetAsUInt32(); + break; + } + + bool success = ::Wow64SetThreadContext( + host_thread.GetNativeThread().GetSystemHandle(), &tls_context); + + if (!success) + return Status(::GetLastError(), eErrorTypeWin32); + + return error; +} + +Status +NativeRegisterContextWindows_wow64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRRead(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_wow64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRWrite(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_wow64::ReadAllRegisterValues( + lldb::DataBufferSP &data_sp) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + const size_t data_size = REG_CONTEXT_SIZE; + + data_sp.reset(new DataBufferHeap(data_size, 0)); + if (!data_sp) { + error.SetErrorStringWithFormat( + "failed to allocate DataBufferHeap instance of size %" PRIu64, + data_size); + return error; + } + + ::WOW64_CONTEXT tls_context; + memset(&tls_context, 0, sizeof(tls_context)); + + NativeThreadWindows *wthread = static_cast(&m_thread); + auto host_thread = wthread->GetHostThread(); + tls_context.ContextFlags = + WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS; + if (!::Wow64GetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), + &tls_context)) { + LLDB_LOG(log, "GetThreadContext failed with error {0}", ::GetLastError()); + return Status(::GetLastError(), eErrorTypeWin32); + } + LLDB_LOG(log, "successfully updated the register values."); + + uint8_t *dst = data_sp->GetBytes(); + if (dst == nullptr) { + error.SetErrorStringWithFormat("DataBufferHeap instance of size %" PRIu64 + " returned a null pointer", + data_size); + return error; + } + + ::memcpy(dst, &tls_context, data_size); + return error; +} + +Status NativeRegisterContextWindows_wow64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + + const size_t data_size = REG_CONTEXT_SIZE; + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextWindows::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != data_size) { + error.SetErrorStringWithFormatv( + "data_sp contained mismatched data size, expected {0}, actual {1}", + data_size, data_sp->GetByteSize()); + return error; + } + + uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error.SetErrorStringWithFormat("NativeRegisterContextWindows::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + + ::WOW64_CONTEXT tls_context; + memcpy(&tls_context, data_sp->GetBytes(), data_size); + + NativeThreadWindows *wthread = static_cast(&m_thread); + auto host_thread = wthread->GetHostThread(); + if (!::Wow64SetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), + &tls_context)) { + LLDB_LOG(log, "SetThreadContext failed with error {0}", ::GetLastError()); + return Status(::GetLastError(), eErrorTypeWin32); + } + return error; +} + +Status NativeRegisterContextWindows_wow64::IsWatchpointHit(uint32_t wp_index, + bool &is_hit) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_wow64::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_wow64::IsWatchpointVacant(uint32_t wp_index, + bool &is_vacant) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_wow64::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + return Status("unimplemented"); +} + +bool NativeRegisterContextWindows_wow64::ClearHardwareWatchpoint( + uint32_t wp_index) { + return false; +} + +Status NativeRegisterContextWindows_wow64::ClearAllHardwareWatchpoints() { + return Status("unimplemented"); +} + +uint32_t NativeRegisterContextWindows_wow64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextWindows_wow64::GetWatchpointAddress(uint32_t wp_index) { + return LLDB_INVALID_ADDRESS; +} + +uint32_t NativeRegisterContextWindows_wow64::NumSupportedHardwareWatchpoints() { + // Not implemented + return 0; +} + +#endif // defined(_WIN64) Index: source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.h @@ -0,0 +1,76 @@ +//===-- NativeRegisterContextWindows_x86_64.h ------------------*- C++//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN64) +#ifndef liblldb_NativeRegisterContextWindows_x86_64_h_ +#define liblldb_NativeRegisterContextWindows_x86_64_h_ + +#include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +#include "NativeRegisterContextWindows.h" + +namespace lldb_private { + +class NativeThreadWindows; + +class NativeRegisterContextWindows_x86_64 + : public NativeRegisterContextWindows { +public: + NativeRegisterContextWindows_x86_64(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + + uint32_t GetRegisterSetCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool ClearHardwareWatchpoint(uint32_t wp_index) override; + + Status ClearAllHardwareWatchpoints() override; + + Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, + uint32_t wp_index); + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + +protected: + Status GPRRead(const uint32_t reg, RegisterValue ®_value); + + Status GPRWrite(const uint32_t reg, const RegisterValue ®_value); + +private: + bool IsGPR(uint32_t reg_index) const; +}; + +} // namespace lldb_private + +#endif // liblldb_NativeRegisterContextWindows_x86_64_h_ +#endif // defined(_WIN64) Index: source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp @@ -0,0 +1,404 @@ +//===-- NativeRegisterContextWindows_x86_64.cpp -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN64) + +#include "NativeRegisterContextWindows_x86_64.h" +#include "NativeRegisterContextWindows_wow64.h" + +#include "NativeThreadWindows.h" +#include "Plugins/Process/Utility/RegisterContextWindows_i386.h" +#include "Plugins/Process/Utility/RegisterContextWindows_x86_64.h" +#include "ProcessWindowsLog.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +#define REG_CONTEXT_SIZE sizeof(::CONTEXT) + +namespace { +static const uint32_t g_gpr_regnums_x86_64[] = { + lldb_rax_x86_64, lldb_rbx_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64, + lldb_rdi_x86_64, lldb_rsi_x86_64, lldb_rbp_x86_64, lldb_rsp_x86_64, + lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64, + lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64, + lldb_rip_x86_64, lldb_rflags_x86_64, lldb_cs_x86_64, lldb_fs_x86_64, + lldb_gs_x86_64, lldb_ss_x86_64, lldb_ds_x86_64, lldb_es_x86_64, + LLDB_INVALID_REGNUM // Register sets must be terminated with this flag +}; +size_t k_num_gprs = llvm::array_lengthof(g_gpr_regnums_x86_64) - 1; + +static const RegisterSet g_reg_sets_x86_64[] = { + {"General Purpose Registers", "gpr", + llvm::array_lengthof(g_gpr_regnums_x86_64) - 1, g_gpr_regnums_x86_64}, +}; +size_t k_num_register_sets = llvm::array_lengthof(g_gpr_regnums_x86_64); + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) { + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + return new RegisterContextWindows_x86_64(target_arch); +} +} // namespace + +std::unique_ptr +NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { + // Register context for a Wow64 application. + if (target_arch.GetAddressByteSize() == 4) + return llvm::make_unique(target_arch, + native_thread); + + // Register context for a native 64-bit application. + return llvm::make_unique(target_arch, + native_thread); +} + +NativeRegisterContextWindows_x86_64::NativeRegisterContextWindows_x86_64( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) + : NativeRegisterContextWindows(native_thread, + CreateRegisterInfoInterface(target_arch)) {} + +bool NativeRegisterContextWindows_x86_64::IsGPR(uint32_t reg_index) const { + return (reg_index < k_first_alias_x86_64); +} + +uint32_t NativeRegisterContextWindows_x86_64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextWindows_x86_64::GetRegisterSet(uint32_t set_index) const { + if (set_index >= k_num_register_sets) + return nullptr; + return &g_reg_sets_x86_64[set_index]; +} + +Status NativeRegisterContextWindows_x86_64::GPRRead(const uint32_t reg, + RegisterValue ®_value) { + Status error; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + + ::CONTEXT tls_context; + NativeThreadWindows *wthread = static_cast(&m_thread); + auto host_thread = wthread->GetHostThread(); + memset(&tls_context, 0, sizeof(tls_context)); + tls_context.ContextFlags = + CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; + if (!::GetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), + &tls_context)) { + LLDB_LOG(log, + "GetThreadContext failed with error {0} while caching register " + "values.", + ::GetLastError()); + return Status("error"); + } + LLDB_LOG(log, "successfully updated the register values."); + + switch (reg) { + case lldb_rax_x86_64: + reg_value.SetUInt64(tls_context.Rax); + break; + case lldb_rbx_x86_64: + reg_value.SetUInt64(tls_context.Rbx); + break; + case lldb_rcx_x86_64: + reg_value.SetUInt64(tls_context.Rcx); + break; + case lldb_rdx_x86_64: + reg_value.SetUInt64(tls_context.Rdx); + break; + case lldb_rdi_x86_64: + reg_value.SetUInt64(tls_context.Rdi); + break; + case lldb_rsi_x86_64: + reg_value.SetUInt64(tls_context.Rsi); + break; + case lldb_rbp_x86_64: + reg_value.SetUInt64(tls_context.Rbp); + break; + case lldb_rsp_x86_64: + reg_value.SetUInt64(tls_context.Rsp); + break; + case lldb_r8_x86_64: + reg_value.SetUInt64(tls_context.R8); + break; + case lldb_r9_x86_64: + reg_value.SetUInt64(tls_context.R9); + break; + case lldb_r10_x86_64: + reg_value.SetUInt64(tls_context.R10); + break; + case lldb_r11_x86_64: + reg_value.SetUInt64(tls_context.R11); + break; + case lldb_r12_x86_64: + reg_value.SetUInt64(tls_context.R12); + break; + case lldb_r13_x86_64: + reg_value.SetUInt64(tls_context.R13); + break; + case lldb_r14_x86_64: + reg_value.SetUInt64(tls_context.R14); + break; + case lldb_r15_x86_64: + reg_value.SetUInt64(tls_context.R15); + break; + case lldb_rip_x86_64: + reg_value.SetUInt64(tls_context.Rip); + break; + case lldb_rflags_x86_64: + reg_value.SetUInt64(tls_context.EFlags | 0x2); // Bit #1 alwyas 1 + break; + case lldb_cs_x86_64: + reg_value.SetUInt16(tls_context.SegCs); + break; + case lldb_fs_x86_64: + reg_value.SetUInt16(tls_context.SegFs); + break; + case lldb_gs_x86_64: + reg_value.SetUInt16(tls_context.SegGs); + break; + case lldb_ss_x86_64: + reg_value.SetUInt16(tls_context.SegSs); + break; + case lldb_ds_x86_64: + reg_value.SetUInt16(tls_context.SegDs); + break; + case lldb_es_x86_64: + reg_value.SetUInt16(tls_context.SegEs); + break; + } + + return error; +} + +Status +NativeRegisterContextWindows_x86_64::GPRWrite(const uint32_t reg, + const RegisterValue ®_value) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + + ::CONTEXT tls_context; + memset(&tls_context, 0, sizeof(tls_context)); + + NativeThreadWindows *wthread = static_cast(&m_thread); + auto host_thread = wthread->GetHostThread(); + tls_context.ContextFlags = + CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; + if (!::GetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), + &tls_context)) { + LLDB_LOG(log, + "GetThreadContext failed with error {0} while caching register " + "values.", + ::GetLastError()); + return Status("error"); + } + LLDB_LOG(log, "successfully updated the register values."); + + switch (reg) { + case lldb_rax_x86_64: + tls_context.Rax = reg_value.GetAsUInt64(); + break; + case lldb_rbx_x86_64: + tls_context.Rbx = reg_value.GetAsUInt64(); + break; + case lldb_rcx_x86_64: + tls_context.Rcx = reg_value.GetAsUInt64(); + break; + case lldb_rdx_x86_64: + tls_context.Rdx = reg_value.GetAsUInt64(); + break; + case lldb_rdi_x86_64: + tls_context.Rdi = reg_value.GetAsUInt64(); + break; + case lldb_rsi_x86_64: + tls_context.Rsi = reg_value.GetAsUInt64(); + break; + case lldb_rbp_x86_64: + tls_context.Rbp = reg_value.GetAsUInt64(); + break; + case lldb_rsp_x86_64: + tls_context.Rsp = reg_value.GetAsUInt64(); + break; + case lldb_r8_x86_64: + tls_context.R8 = reg_value.GetAsUInt64(); + break; + case lldb_r9_x86_64: + tls_context.R9 = reg_value.GetAsUInt64(); + break; + case lldb_r10_x86_64: + tls_context.R10 = reg_value.GetAsUInt64(); + break; + case lldb_r11_x86_64: + tls_context.R11 = reg_value.GetAsUInt64(); + break; + case lldb_r12_x86_64: + tls_context.R12 = reg_value.GetAsUInt64(); + break; + case lldb_r13_x86_64: + tls_context.R13 = reg_value.GetAsUInt64(); + break; + case lldb_r14_x86_64: + tls_context.R14 = reg_value.GetAsUInt64(); + break; + case lldb_r15_x86_64: + tls_context.R15 = reg_value.GetAsUInt64(); + break; + case lldb_rip_x86_64: + tls_context.Rip = reg_value.GetAsUInt64(); + break; + case lldb_rflags_x86_64: + tls_context.EFlags = reg_value.GetAsUInt64(); + break; + case lldb_cs_x86_64: + tls_context.SegCs = reg_value.GetAsUInt16(); + break; + case lldb_fs_x86_64: + tls_context.SegFs = reg_value.GetAsUInt16(); + break; + case lldb_gs_x86_64: + tls_context.SegGs = reg_value.GetAsUInt16(); + break; + case lldb_ss_x86_64: + tls_context.SegSs = reg_value.GetAsUInt16(); + break; + case lldb_ds_x86_64: + tls_context.SegDs = reg_value.GetAsUInt16(); + break; + case lldb_es_x86_64: + tls_context.SegEs = reg_value.GetAsUInt16(); + break; + } + + bool success = ::SetThreadContext( + host_thread.GetNativeThread().GetSystemHandle(), &tls_context); + if (!success) + return Status(::GetLastError(), eErrorTypeWin32); + + return error; +} + +Status +NativeRegisterContextWindows_x86_64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRRead(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_x86_64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRWrite(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_x86_64::ReadAllRegisterValues( + lldb::DataBufferSP &data_sp) { + return NativeRegisterContextWindows::ReadAllRegisterValues(data_sp, + REG_CONTEXT_SIZE); +} + +Status NativeRegisterContextWindows_x86_64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + return NativeRegisterContextWindows::WriteAllRegisterValues(data_sp, + REG_CONTEXT_SIZE); +} + +Status NativeRegisterContextWindows_x86_64::IsWatchpointHit(uint32_t wp_index, + bool &is_hit) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_x86_64::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + return Status("unimplemented"); +} + +Status +NativeRegisterContextWindows_x86_64::IsWatchpointVacant(uint32_t wp_index, + bool &is_vacant) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_x86_64::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + return Status("unimplemented"); +} + +bool NativeRegisterContextWindows_x86_64::ClearHardwareWatchpoint( + uint32_t wp_index) { + return false; +} + +Status NativeRegisterContextWindows_x86_64::ClearAllHardwareWatchpoints() { + return Status("unimplemented"); +} + +uint32_t NativeRegisterContextWindows_x86_64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextWindows_x86_64::GetWatchpointAddress(uint32_t wp_index) { + return LLDB_INVALID_ADDRESS; +} + +uint32_t +NativeRegisterContextWindows_x86_64::NumSupportedHardwareWatchpoints() { + // Not implemented + return 0; +} + +#endif // defined(_WIN64) Index: source/Plugins/Process/Windows/Common/NativeThreadWindows.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeThreadWindows.h @@ -0,0 +1,71 @@ +//===-- NativeThreadWindows.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeThreadWindows_h_ +#define liblldb_NativeThreadWindows_h_ + +#include "lldb/Host/HostThread.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/lldb-private-forward.h" + +#include "NativeRegisterContextWindows.h" + +namespace lldb_private { + +class NativeProcessWindows; + +class NativeThreadWindows : public NativeThreadProtocol { +public: + NativeThreadWindows(NativeProcessWindows &process, const HostThread &thread); + + ~NativeThreadWindows() {} + + Status DoStop(); + Status DoResume(lldb::StateType resume_state); + Status DoDestroy(); + + std::string GetName() override; + + lldb::StateType GetState() override { return m_state; } + + NativeRegisterContextWindows &GetRegisterContext() override { + return *m_reg_context_up; + } + + bool GetStopReason(ThreadStopInfo &stop_info, + std::string &description) override; + + Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, + bool hardware) override; + + Status RemoveWatchpoint(lldb::addr_t addr) override; + + Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + Status RemoveHardwareBreakpoint(lldb::addr_t addr) override; + + void SetStopReason(ThreadStopInfo stop_info, std::string description); + + const HostThread &GetHostThread() { return m_host_thread; } + +protected: + lldb::StateType m_state = lldb::StateType::eStateInvalid; + std::string m_name; + ThreadStopInfo m_stop_info; + std::string m_stop_description; + std::unique_ptr m_reg_context_up; + // Cache address and index of the watchpoints and hardware breakpoints since + // the register context does not. + using IndexMap = std::map; + IndexMap m_watchpoint_index_map; + IndexMap m_hw_breakpoint_index_map; + HostThread m_host_thread; +}; +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeThreadWindows_h_ Index: source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp @@ -0,0 +1,199 @@ +//===-- NativeThreadWindows.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NativeThreadWindows.h" +#include "NativeProcessWindows.h" + +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +#include "lldb/lldb-forward.h" + +using namespace lldb; +using namespace lldb_private; + +NativeThreadWindows::NativeThreadWindows(NativeProcessWindows &process, + const HostThread &thread) + : NativeThreadProtocol(process, thread.GetNativeThread().GetThreadId()), + m_stop_info(), m_stop_description(), m_host_thread(thread) { + m_reg_context_up = + (NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows( + process.GetArchitecture(), *this)); +} + +Status NativeThreadWindows::DoStop() { + if (m_state != eStateStopped) { + DWORD previous_suspend_count = + ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); + if (previous_suspend_count == (DWORD)-1) + return Status(::GetLastError(), eErrorTypeWin32); + + m_state = eStateStopped; + } + return Status(); +} + +Status NativeThreadWindows::DoResume(lldb::StateType resume_state) { + StateType current_state = GetState(); + if (resume_state == current_state) + return Status(); + + if (resume_state == eStateStepping) { + uint32_t flags_index = + GetRegisterContext().ConvertRegisterKindToRegisterNumber( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + uint64_t flags_value = + GetRegisterContext().ReadRegisterAsUnsigned(flags_index, 0); + flags_value |= 0x100; // Set the trap flag on the CPU + GetRegisterContext().WriteRegisterFromUnsigned(flags_index, flags_value); + } + + if (resume_state == eStateStepping || resume_state == eStateRunning) { + DWORD previous_suspend_count = 0; + HANDLE thread_handle = m_host_thread.GetNativeThread().GetSystemHandle(); + do { + // ResumeThread returns -1 on error, or the thread's *previous* suspend + // count on success. This means that the return value is 1 when the thread + // was restarted. Note that DWORD is an unsigned int, so we need to + // explicitly compare with -1. + previous_suspend_count = ::ResumeThread(thread_handle); + + if (previous_suspend_count == (DWORD)-1) + return Status(::GetLastError(), eErrorTypeWin32); + + } while (previous_suspend_count > 1); + m_state = eStateRunning; + } + + return Status(); +} + +Status NativeThreadWindows::DoDestroy() { + m_state = eStateExited; + m_stop_info.reason = StopReason::eStopReasonThreadExiting; + return Status(); +} + +std::string NativeThreadWindows::GetName() { + if (!m_name.empty()) + return m_name; + + // Name is not a property of the Windows thread. Create one with the + // process's. + NativeProcessProtocol &process = GetProcess(); + ProcessInstanceInfo process_info; + if (Host::GetProcessInfo(process.GetID(), process_info)) { + std::string process_name(process_info.GetName()); + m_name = process_name; + } + return m_name; +} + +void NativeThreadWindows::SetStopReason(ThreadStopInfo stop_info, + std::string description) { + m_state = eStateStopped; + m_stop_info = stop_info; + m_stop_description = description; +} + +bool NativeThreadWindows::GetStopReason(ThreadStopInfo &stop_info, + std::string &description) { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + + switch (m_state) { + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + case eStateUnloaded: + stop_info = m_stop_info; + description = m_stop_description; + return true; + + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + if (log) { + log->Printf("NativeThreadWindows::%s tid %" PRIu64 + " in state %s cannot answer stop reason", + __FUNCTION__, GetID(), StateAsCString(m_state)); + } + return false; + } + llvm_unreachable("unhandled StateType!"); +} + +Status NativeThreadWindows::SetWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags, bool hardware) { + if (!hardware) + return Status("not implemented"); + + if (m_state == eStateLaunching) + return Status(); + Status error = RemoveWatchpoint(addr); + if (error.Fail()) + return error; + + uint32_t index = + m_reg_context_up->SetHardwareWatchpoint(addr, size, watch_flags); + if (index == LLDB_INVALID_INDEX32) + return Status("Setting hardware watchpoint failed."); + + m_watchpoint_index_map.insert({addr, index}); + return Status(); +} + +Status NativeThreadWindows::RemoveWatchpoint(lldb::addr_t addr) { + auto it = m_watchpoint_index_map.find(addr); + if (it == m_watchpoint_index_map.end()) + return Status(); + + uint32_t index = it->second; + m_watchpoint_index_map.erase(it); + if (m_reg_context_up->ClearHardwareWatchpoint(index)) + return Status(); + + return Status("Remove watchpoint failed."); +} + +Status NativeThreadWindows::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + if (m_state == eStateLaunching) + return Status(); + + Status error = RemoveHardwareBreakpoint(addr); + if (error.Fail()) + return error; + + uint32_t index = m_reg_context_up->SetHardwareBreakpoint(addr, size); + if (index == LLDB_INVALID_INDEX32) + return Status("Setting hardware breakpoint failed."); + + m_hw_breakpoint_index_map.insert({addr, index}); + return Status(); +} + +Status NativeThreadWindows::RemoveHardwareBreakpoint(lldb::addr_t addr) { + auto it = m_hw_breakpoint_index_map.find(addr); + if (it == m_hw_breakpoint_index_map.end()) + return Status(); + + uint32_t index = it->second; + m_watchpoint_index_map.erase(it); + if (m_reg_context_up->ClearHardwareWatchpoint(index)) + return Status(); + return Status("Remove hardware breakpoint failed."); +} Index: source/Plugins/Process/Windows/Common/ProcessDebugger.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/ProcessDebugger.h @@ -0,0 +1,84 @@ +//===-- NativeProcessWindows.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessDebugger_h_ +#define liblldb_ProcessDebugger_h_ + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" +#include "llvm/Support/Mutex.h" + +#include "ForwardDecl.h" + +namespace lldb_private { + +class HostProcess; +class HostThread; +class ProcessWindowsData; +class ProcessLaunchInfo; +class ProcessAttachInfo; + +class ProcessDebugger { + +public: + virtual void OnExitProcess(uint32_t exit_code); + virtual void OnDebuggerConnected(lldb::addr_t image_base); + virtual ExceptionResult OnDebugException(bool first_chance, + const ExceptionRecord &record); + virtual void OnCreateThread(const HostThread &thread); + virtual void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code); + virtual void OnLoadDll(const ModuleSpec &module_spec, + lldb::addr_t module_addr); + virtual void OnUnloadDll(lldb::addr_t module_addr); + virtual void OnDebugString(const std::string &string); + virtual void OnDebuggerError(const Status &error, uint32_t type); + +protected: + Status DetachProcess(); + + Status LaunchProcess(ProcessLaunchInfo &launch_info, + DebugDelegateSP delegate); + + Status AttachProcess(lldb::pid_t pid, const ProcessAttachInfo &attach_info, + DebugDelegateSP delegate); + + Status DestroyProcess(); + + Status HaltProcess(bool &caused_stop); + + Status GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info); + + Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read); + + Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, + size_t &bytes_written); + + Status AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr); + + Status DeallocateMemory(lldb::addr_t addr); + + DebuggerThreadSP &GetDebuggerThread(); + HostProcess GetDebuggedProcess() const; + HostThread ProcessDebugger::GetMainThread() const; + void ContinueDebugger(); + + Status WaitForDebuggerConnection(DebuggerThreadSP debugger, + HostProcess &process); + +protected: + llvm::sys::Mutex m_mutex; + +private: + std::unique_ptr m_session_data; +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_ProcessDebugger_h_ Index: source/Plugins/Process/Windows/Common/ProcessDebugger.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/ProcessDebugger.cpp @@ -0,0 +1,640 @@ +//===-- ProcessDebugger.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ProcessDebugger.h" + +// Windows includes +#include "lldb/Host/windows/windows.h" +#include + +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostNativeProcessBase.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Process.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Error.h" + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "ProcessWindowsLog.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { +std::string GetProcessExecutableName(HANDLE process_handle) { + std::vector file_name; + DWORD file_name_size = MAX_PATH; // first guess, not an absolute limit + DWORD copied = 0; + do { + file_name_size *= 2; + file_name.resize(file_name_size); + copied = ::GetModuleFileNameExW(process_handle, NULL, file_name.data(), + file_name_size); + } while (copied >= file_name_size); + file_name.resize(copied); + std::string result; + llvm::convertWideToUTF8(file_name.data(), result); + return result; +} + +std::string GetProcessExecutableName(DWORD pid) { + std::string file_name; + HANDLE process_handle = + ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (process_handle != NULL) { + file_name = GetProcessExecutableName(process_handle); + ::CloseHandle(process_handle); + } + return file_name; +} + +DWORD ConvertLldbToWinApiProtect(uint32_t protect) { + // We also can process a read / write permissions here, but if the debugger + // will make later a write into the allocated memory, it will fail. To get + // around it is possible inside DoWriteMemory to remember memory permissions, + // allow write, write and restore permissions, but for now we process only + // the executable permission. + // + // TODO: Process permissions other than executable + if (protect & ePermissionsExecutable) + return PAGE_EXECUTE_READWRITE; + + return PAGE_READWRITE; +} + +// The Windows page protection bits are NOT independent masks that can be +// bitwise-ORed together. For example, PAGE_EXECUTE_READ is not (PAGE_EXECUTE +// | PAGE_READ). To test for an access type, it's necessary to test for any of +// the bits that provide that access type. +bool IsPageReadable(uint32_t protect) { return (protect & PAGE_NOACCESS) == 0; } + +bool IsPageWritable(uint32_t protect) { + return (protect & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | + PAGE_READWRITE | PAGE_WRITECOPY)) != 0; +} + +bool IsPageExecutable(uint32_t protect) { + return (protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | + PAGE_EXECUTE_WRITECOPY)) != 0; +} +} // namespace + +namespace lldb_private { + +// We store a pointer to this class in the ProcessWindows, so that we don't +// expose Windows-specific types and implementation details from a public +// header file. +class ProcessWindowsData { +public: + ProcessWindowsData(bool stop_at_entry) : m_stop_at_entry(stop_at_entry) { + m_initial_stop_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); + } + + ~ProcessWindowsData() { ::CloseHandle(m_initial_stop_event); } + + Status m_launch_error; + DebuggerThreadSP m_debugger; + StopInfoSP m_pending_stop_info; + HANDLE m_initial_stop_event = nullptr; + bool m_initial_stop_received = false; + bool m_stop_at_entry; + std::map m_new_threads; + std::set m_exited_threads; +}; + +DebuggerThreadSP &ProcessDebugger::GetDebuggerThread() { + return m_session_data->m_debugger; +} + +HostProcess ProcessDebugger::GetDebuggedProcess() const { + return m_session_data->m_debugger->GetProcess(); +} + +HostThread ProcessDebugger::GetMainThread() const { + return m_session_data->m_debugger->GetMainThread(); +} + +void ProcessDebugger::ContinueDebugger() { + + ExceptionRecordSP active_exception = + GetDebuggerThread()->GetActiveException().lock(); + if (active_exception) { + // Resume the process and continue processing debug events. Mask the + // exception so that from the process's view, there is no indication that + // anything happened. + GetDebuggerThread()->ContinueAsyncException(ExceptionResult::MaskException); + } +} + +Status ProcessDebugger::DetachProcess() { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + DebuggerThreadSP debugger_thread; + { + // Acquire the lock only long enough to get the DebuggerThread. + // StopDebugging() will trigger a call back into ProcessWindows which will + // also acquire the lock. Thus we have to release the lock before calling + // StopDebugging(). + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) { + LLDB_LOG(log, "there is no active session."); + return Status(); + } + + debugger_thread = m_session_data->m_debugger; + } + + Status error; + + LLDB_LOG(log, "detaching from process {0}.", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle()); + error = debugger_thread->StopDebugging(false); + + // By the time StopDebugging returns, there is no more debugger thread, so + // we can be assured that no other thread will race for the session data. + m_session_data.reset(); + + return error; +} + +Status ProcessDebugger::LaunchProcess(ProcessLaunchInfo &launch_info, + DebugDelegateSP delegate) { + // Even though m_session_data is accessed here, it is before a debugger + // thread has been kicked off. So there's no race conditions, and it + // shouldn't be necessary to acquire the mutex. + + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + Status result; + + FileSpec working_dir = launch_info.GetWorkingDirectory(); + namespace fs = llvm::sys::fs; + if (working_dir) { + FileSystem::Instance().Resolve(working_dir); + if (!FileSystem::Instance().IsDirectory(working_dir)) { + result.SetErrorStringWithFormat("No such file or directory: %s", + working_dir.GetCString()); + return result; + } + } + + if (!launch_info.GetFlags().Test(eLaunchFlagDebug)) { + StreamString stream; + stream.Printf("ProcessWindows unable to launch '%s'. ProcessWindows can " + "only be used for debug launches.", + launch_info.GetExecutableFile().GetPath().c_str()); + std::string message = stream.GetString(); + result.SetErrorString(message.c_str()); + + LLDB_LOG(log, "error: {0}", message); + return result; + } + + bool stop_at_entry = launch_info.GetFlags().Test(eLaunchFlagStopAtEntry); + m_session_data.reset(new ProcessWindowsData(stop_at_entry)); + m_session_data->m_debugger.reset(new DebuggerThread(delegate)); + DebuggerThreadSP debugger = m_session_data->m_debugger; + + // Kick off the DebugLaunch asynchronously and wait for it to complete. + result = debugger->DebugLaunch(launch_info); + if (result.Fail()) { + LLDB_LOG(log, "failed launching '{0}'. {1}", + launch_info.GetExecutableFile().GetPath(), result); + return result; + } + + HostProcess process; + Status error = WaitForDebuggerConnection(debugger, process); + if (error.Fail()) { + LLDB_LOG(log, "failed launching '{0}'. {1}", + launch_info.GetExecutableFile().GetPath(), error); + return error; + } + + LLDB_LOG(log, "successfully launched '{0}'", + launch_info.GetExecutableFile().GetPath()); + + // We've hit the initial stop. If eLaunchFlagsStopAtEntry was specified, the + // private state should already be set to eStateStopped as a result of + // hitting the initial breakpoint. If it was not set, the breakpoint should + // have already been resumed from and the private state should already be + // eStateRunning. + launch_info.SetProcessID(process.GetProcessId()); + + return result; +} + +Status ProcessDebugger::AttachProcess(lldb::pid_t pid, + const ProcessAttachInfo &attach_info, + DebugDelegateSP delegate) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + m_session_data.reset( + new ProcessWindowsData(!attach_info.GetContinueOnceAttached())); + DebuggerThreadSP debugger(new DebuggerThread(delegate)); + + m_session_data->m_debugger = debugger; + + DWORD process_id = static_cast(pid); + Status error = debugger->DebugAttach(process_id, attach_info); + if (error.Fail()) { + LLDB_LOG( + log, + "encountered an error occurred initiating the asynchronous attach. {0}", + error); + return error; + } + + HostProcess process; + error = WaitForDebuggerConnection(debugger, process); + if (error.Fail()) { + LLDB_LOG(log, + "encountered an error waiting for the debugger to connect. {0}", + error); + return error; + } + + LLDB_LOG(log, "successfully attached to process with pid={0}", process_id); + + // We've hit the initial stop. If eLaunchFlagsStopAtEntry was specified, the + // private state should already be set to eStateStopped as a result of + // hitting the initial breakpoint. If it was not set, the breakpoint should + // have already been resumed from and the private state should already be + // eStateRunning. + return error; +} + +Status ProcessDebugger::DestroyProcess() { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + DebuggerThreadSP debugger_thread; + { + // Acquire this lock inside an inner scope, only long enough to get the + // DebuggerThread. StopDebugging() will trigger a call back into + // ProcessWindows which will acquire the lock again, so we need to not + // deadlock. + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) { + LLDB_LOG(log, "warning: there is no active session."); + return Status(); + } + + debugger_thread = m_session_data->m_debugger; + } + + LLDB_LOG(log, "Shutting down process {0}.", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle()); + Status error = debugger_thread->StopDebugging(true); + + // By the time StopDebugging returns, there is no more debugger thread, so + // we can be assured that no other thread will race for the session data. + m_session_data.reset(); + + return error; +} + +Status ProcessDebugger::HaltProcess(bool &caused_stop) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + Status error; + llvm::sys::ScopedLock lock(m_mutex); + caused_stop = ::DebugBreakProcess(m_session_data->m_debugger->GetProcess() + .GetNativeProcess() + .GetSystemHandle()); + if (!caused_stop) { + error.SetError(::GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "DebugBreakProcess failed with error {0}", error); + } + + return error; +} + +Status ProcessDebugger::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + size_t &bytes_read) { + Status error; + bytes_read = 0; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_MEMORY); + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) { + error.SetErrorString( + "cannot read, there is no active debugger connection."); + LLDB_LOG(log, "error: {0}", error); + return error; + } + + LLDB_LOG(log, "attempting to read {0} bytes from address {1:x}", size, + vm_addr); + + HostProcess process = m_session_data->m_debugger->GetProcess(); + void *addr = reinterpret_cast(vm_addr); + SIZE_T num_of_bytes_read = 0; + if (!::ReadProcessMemory(process.GetNativeProcess().GetSystemHandle(), addr, + buf, size, &num_of_bytes_read)) { + // Reading from the process can fail for a number of reasons - set the + // error code and make sure that the number of bytes read is set back to 0 + // because in some scenarios the value of bytes_read returned from the API + // is garbage. + error.SetError(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "reading failed with error: {0}", error); + } else { + bytes_read = num_of_bytes_read; + } + return error; +} + +Status ProcessDebugger::WriteMemory(lldb::addr_t vm_addr, const void *buf, + size_t size, size_t &bytes_written) { + Status error; + bytes_written = 0; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_MEMORY); + llvm::sys::ScopedLock lock(m_mutex); + LLDB_LOG(log, "attempting to write {0} bytes into address {1:x}", size, + vm_addr); + + if (!m_session_data) { + error.SetErrorString( + "cannot write, there is no active debugger connection."); + LLDB_LOG(log, "error: {0}", error); + return error; + } + + HostProcess process = m_session_data->m_debugger->GetProcess(); + void *addr = reinterpret_cast(vm_addr); + SIZE_T num_of_bytes_written = 0; + lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); + if (::WriteProcessMemory(handle, addr, buf, size, &num_of_bytes_written)) { + FlushInstructionCache(handle, addr, num_of_bytes_written); + bytes_written = num_of_bytes_written; + } else { + error.SetError(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "writing failed with error: {0}", error); + } + return error; +} + +Status ProcessDebugger::AllocateMemory(size_t size, uint32_t permissions, + lldb::addr_t &addr) { + Status error; + addr = LLDB_INVALID_ADDRESS; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_MEMORY); + llvm::sys::ScopedLock lock(m_mutex); + LLDB_LOG(log, "attempting to allocate {0} bytes with permissions {1}", size, + permissions); + + if (!m_session_data) { + error.SetErrorString( + "cannot allocate, there is no active debugger connection"); + LLDB_LOG(log, "error: {0}", error); + return error; + } + + HostProcess process = m_session_data->m_debugger->GetProcess(); + lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); + auto protect = ConvertLldbToWinApiProtect(permissions); + auto result = ::VirtualAllocEx(handle, nullptr, size, MEM_COMMIT, protect); + if (!result) { + error.SetError(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "allocating failed with error: {0}", error); + } else { + addr = reinterpret_cast(result); + } + return error; +} + +Status ProcessDebugger::DeallocateMemory(lldb::addr_t vm_addr) { + Status result; + + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_MEMORY); + llvm::sys::ScopedLock lock(m_mutex); + LLDB_LOG(log, "attempting to deallocate bytes at address {0}", vm_addr); + + if (!m_session_data) { + result.SetErrorString( + "cannot deallocate, there is no active debugger connection"); + LLDB_LOG(log, "error: {0}", result); + return result; + } + + HostProcess process = m_session_data->m_debugger->GetProcess(); + lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); + if (!::VirtualFreeEx(handle, reinterpret_cast(vm_addr), 0, + MEM_RELEASE)) { + result.SetError(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "deallocating failed with error: {0}", result); + } + + return result; +} + +Status ProcessDebugger::GetMemoryRegionInfo(lldb::addr_t vm_addr, + MemoryRegionInfo &info) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_MEMORY); + Status error; + llvm::sys::ScopedLock lock(m_mutex); + info.Clear(); + + if (!m_session_data) { + error.SetErrorString( + "GetMemoryRegionInfo called with no debugging session."); + LLDB_LOG(log, "error: {0}", error); + return error; + } + HostProcess process = m_session_data->m_debugger->GetProcess(); + lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); + if (handle == nullptr || handle == LLDB_INVALID_PROCESS) { + error.SetErrorString( + "GetMemoryRegionInfo called with an invalid target process."); + LLDB_LOG(log, "error: {0}", error); + return error; + } + + LLDB_LOG(log, "getting info for address {0:x}", vm_addr); + + void *addr = reinterpret_cast(vm_addr); + MEMORY_BASIC_INFORMATION mem_info = {}; + SIZE_T result = ::VirtualQueryEx(handle, addr, &mem_info, sizeof(mem_info)); + if (result == 0) { + if (::GetLastError() == ERROR_INVALID_PARAMETER) { + // ERROR_INVALID_PARAMETER is returned if VirtualQueryEx is called with + // an address past the highest accessible address. We should return a + // range from the vm_addr to LLDB_INVALID_ADDRESS + info.GetRange().SetRangeBase(vm_addr); + info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + info.SetReadable(MemoryRegionInfo::eNo); + info.SetExecutable(MemoryRegionInfo::eNo); + info.SetWritable(MemoryRegionInfo::eNo); + info.SetMapped(MemoryRegionInfo::eNo); + return error; + } else { + error.SetError(::GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, + "VirtualQueryEx returned error {0} while getting memory " + "region info for address {1:x}", + error, vm_addr); + return error; + } + } + + // Protect bits are only valid for MEM_COMMIT regions. + if (mem_info.State == MEM_COMMIT) { + const bool readable = IsPageReadable(mem_info.Protect); + const bool executable = IsPageExecutable(mem_info.Protect); + const bool writable = IsPageWritable(mem_info.Protect); + info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetExecutable(executable ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); + info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + } else { + info.SetReadable(MemoryRegionInfo::eNo); + info.SetExecutable(MemoryRegionInfo::eNo); + info.SetWritable(MemoryRegionInfo::eNo); + } + + // AllocationBase is defined for MEM_COMMIT and MEM_RESERVE but not MEM_FREE. + if (mem_info.State != MEM_FREE) { + info.GetRange().SetRangeBase( + reinterpret_cast(mem_info.AllocationBase)); + info.GetRange().SetRangeEnd(reinterpret_cast(mem_info.BaseAddress) + + mem_info.RegionSize); + info.SetMapped(MemoryRegionInfo::eYes); + } else { + // In the unmapped case we need to return the distance to the next block of + // memory. VirtualQueryEx nearly does that except that it gives the + // distance from the start of the page containing vm_addr. + SYSTEM_INFO data; + ::GetSystemInfo(&data); + DWORD page_offset = vm_addr % data.dwPageSize; + info.GetRange().SetRangeBase(vm_addr); + info.GetRange().SetByteSize(mem_info.RegionSize - page_offset); + info.SetMapped(MemoryRegionInfo::eNo); + } + + error.SetError(::GetLastError(), eErrorTypeWin32); + LLDB_LOGV(log, + "Memory region info for address {0}: readable={1}, " + "executable={2}, writable={3}", + vm_addr, info.GetReadable(), info.GetExecutable(), + info.GetWritable()); + return error; +} + +void ProcessDebugger::OnExitProcess(uint32_t exit_code) { + // If the process exits before any initial stop then notify the debugger + // of the error otherwise WaitForDebuggerConnection() will be blocked. + // An example of this issue is when a process fails to load a dependent DLL. + if (m_session_data && !m_session_data->m_initial_stop_received) { + Status error(exit_code, eErrorTypeWin32); + OnDebuggerError(error, 0); + } +} + +void ProcessDebugger::OnDebuggerConnected(lldb::addr_t image_base) {} + +ExceptionResult +ProcessDebugger::OnDebugException(bool first_chance, + const ExceptionRecord &record) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_EXCEPTION); + llvm::sys::ScopedLock lock(m_mutex); + + // FIXME: Without this check, occasionally when running the test suite there + // is + // an issue where m_session_data can be null. It's not clear how this could + // happen but it only surfaces while running the test suite. In order to + // properly diagnose this, we probably need to first figure allow the test + // suite to print out full lldb logs, and then add logging to the process + // plugin. + if (!m_session_data) { + LLDB_LOG(log, + "Debugger thread reported exception {0:x} at address {1:x}, " + "but there is no session.", + record.GetExceptionCode(), record.GetExceptionAddress()); + return ExceptionResult::SendToApplication; + } + + ExceptionResult result = ExceptionResult::SendToApplication; + if ((record.GetExceptionCode() == EXCEPTION_BREAKPOINT || + record.GetExceptionCode() == + 0x4000001FL /*WOW64 STATUS_WX86_BREAKPOINT*/) && + !m_session_data->m_initial_stop_received) { + // Handle breakpoints at the first chance. + result = ExceptionResult::BreakInDebugger; + LLDB_LOG( + log, + "Hit loader breakpoint at address {0:x}, setting initial stop event.", + record.GetExceptionAddress()); + m_session_data->m_initial_stop_received = true; + ::SetEvent(m_session_data->m_initial_stop_event); + } + return result; +} + +void ProcessDebugger::OnCreateThread(const HostThread &thread) { + // Do nothing by default +} + +void ProcessDebugger::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) { + // Do nothing by default +} + +void ProcessDebugger::OnLoadDll(const ModuleSpec &module_spec, + lldb::addr_t module_addr) { + // Do nothing by default +} + +void ProcessDebugger::OnUnloadDll(lldb::addr_t module_addr) { + // Do nothing by default +} + +void ProcessDebugger::OnDebugString(const std::string &string) {} + +void ProcessDebugger::OnDebuggerError(const Status &error, uint32_t type) { + llvm::sys::ScopedLock lock(m_mutex); + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + + if (m_session_data->m_initial_stop_received) { + // This happened while debugging. Do we shutdown the debugging session, + // try to continue, or do something else? + LLDB_LOG(log, + "Error {0} occurred during debugging. Unexpected behavior " + "may result. {1}", + error.GetError(), error); + } else { + // If we haven't actually launched the process yet, this was an error + // launching the process. Set the internal error and signal the initial + // stop event so that the DoLaunch method wakes up and returns a failure. + m_session_data->m_launch_error = error; + ::SetEvent(m_session_data->m_initial_stop_event); + LLDB_LOG(log, + "Error {0} occurred launching the process before the initial " + "stop. {1}", + error.GetError(), error); + return; + } +} + +Status ProcessDebugger::WaitForDebuggerConnection(DebuggerThreadSP debugger, + HostProcess &process) { + Status result; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS | + WINDOWS_LOG_BREAKPOINTS); + LLDB_LOG(log, "Waiting for loader breakpoint."); + + // Block this function until we receive the initial stop from the process. + if (::WaitForSingleObject(m_session_data->m_initial_stop_event, INFINITE) == + WAIT_OBJECT_0) { + LLDB_LOG(log, "hit loader breakpoint, returning."); + + process = debugger->GetProcess(); + return m_session_data->m_launch_error; + } else + return Status(::GetLastError(), eErrorTypeWin32); +} + +} // namespace lldb_private Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -37,6 +37,8 @@ #if defined(__APPLE__) #define DEBUGSERVER_BASENAME "debugserver" +#elif defined(_WIN32) +#define DEBUGSERVER_BASENAME "lldb-server.exe" #else #define DEBUGSERVER_BASENAME "lldb-server" #endif Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -1189,12 +1189,17 @@ void GDBRemoteCommunicationServerCommon:: CreateProcessInfoResponse_DebugServerStyle( const ProcessInstanceInfo &proc_info, StreamString &response) { +#if !defined(_WIN32) response.Printf("pid:%" PRIx64 ";parent-pid:%" PRIx64 ";real-uid:%x;real-gid:%x;effective-uid:%x;effective-gid:%x;", proc_info.GetProcessID(), proc_info.GetParentProcessID(), proc_info.GetUserID(), proc_info.GetGroupID(), proc_info.GetEffectiveUserID(), proc_info.GetEffectiveGroupID()); +#else + response.Printf("pid:%" PRIx64 ";parent-pid:%" PRIx64 ";", + proc_info.GetProcessID(), proc_info.GetParentProcessID()); +#endif const ArchSpec &proc_arch = proc_info.GetArchitecture(); if (proc_arch.IsValid()) { Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -218,8 +218,15 @@ m_process_launch_info.GetFlags().Set(eLaunchFlagDebug); if (should_forward_stdio) { + // No pty support on Windows so far that means neither O* or I* + // notification packets will be generated about the inferior. However + // in most cases the missing notifications do not affect the major + // use of the lldb-server. So here temporarily relax the following + // for Windows. +#if !defined(_WIN32) if (llvm::Error Err = m_process_launch_info.SetUpPtyRedirection()) return Status(std::move(Err)); +#endif } { Index: tools/lldb-server/SystemInitializerLLGS.cpp =================================================================== --- tools/lldb-server/SystemInitializerLLGS.cpp +++ tools/lldb-server/SystemInitializerLLGS.cpp @@ -12,6 +12,9 @@ #include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" using HostObjectFile = ObjectFileMachO; #elif defined(_WIN32) +#include "lldb/Host/windows/windows.h" +#include + #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" using HostObjectFile = ObjectFilePECOFF; #else @@ -21,16 +24,38 @@ using namespace lldb_private; +static uint32_t g_ref_count = 0; + llvm::Error SystemInitializerLLGS::Initialize() { if (auto e = SystemInitializerCommon::Initialize()) return e; HostObjectFile::Initialize(); +#if defined(_WIN32) + if (g_ref_count++ == 0) { + // Require Windows Sockets version 2.2. + auto wVersion = MAKEWORD(2, 2); + WSADATA wsaData; + auto err = WSAStartup(wVersion, &wsaData); + if (err == 0) { + // Check if the WinSock verison is what we expect. + if (wsaData.wVersion < wVersion) { + g_ref_count = 0; + WSACleanup(); + } + } else + g_ref_count = 0; + } +#endif return llvm::Error::success(); } void SystemInitializerLLGS::Terminate() { +#if defined(_WIN32) + if (g_ref_count && --g_ref_count == 0) + WSACleanup(); +#endif HostObjectFile::Terminate(); SystemInitializerCommon::Terminate(); } Index: tools/lldb-server/lldb-gdbserver.cpp =================================================================== --- tools/lldb-server/lldb-gdbserver.cpp +++ tools/lldb-server/lldb-gdbserver.cpp @@ -39,6 +39,8 @@ #include "Plugins/Process/Linux/NativeProcessLinux.h" #elif defined(__NetBSD__) #include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" +#elif defined(_WIN32) +#include "Plugins/Process/Windows/Common/NativeProcessWindows.h" #endif #ifndef LLGS_PROGRAM_NAME @@ -60,6 +62,8 @@ typedef process_linux::NativeProcessLinux::Factory NativeProcessFactory; #elif defined(__NetBSD__) typedef process_netbsd::NativeProcessNetBSD::Factory NativeProcessFactory; +#elif defined(_WIN32) +typedef NativeProcessWindows::Factory NativeProcessFactory; #else // Dummy implementation to make sure the code compiles class NativeProcessFactory : public NativeProcessProtocol::Factory { Index: tools/lldb-server/lldb-platform.cpp =================================================================== --- tools/lldb-server/lldb-platform.cpp +++ tools/lldb-server/lldb-platform.cpp @@ -15,8 +15,9 @@ #include #include #include +#if !defined(_WIN32) #include - +#endif #include #include "llvm/Support/FileSystem.h" @@ -73,6 +74,7 @@ // Watch for signals //---------------------------------------------------------------------- static void signal_handler(int signo) { +#if !defined(_WIN32) switch (signo) { case SIGHUP: // Use SIGINT first, if that does not work, use SIGHUP as a last resort. @@ -84,6 +86,7 @@ abort(); break; } +#endif } static void display_usage(const char *progname, const char *subcommand) { @@ -137,8 +140,10 @@ const char *subcommand = argv[1]; argc--; argv++; +#if !defined(_WIN32) signal(SIGPIPE, SIG_IGN); signal(SIGHUP, signal_handler); +#endif int long_option_index = 0; Status error; std::string listen_host_port; Index: tools/lldb-server/lldb-server.cpp =================================================================== --- tools/lldb-server/lldb-server.cpp +++ tools/lldb-server/lldb-server.cpp @@ -36,6 +36,7 @@ int main_gdbserver(int argc, char *argv[]); int main_platform(int argc, char *argv[]); +namespace llgs { static void initialize() { if (auto e = g_debugger_lifetime->Initialize( llvm::make_unique(), nullptr)) @@ -43,6 +44,7 @@ } static void terminate() { g_debugger_lifetime->Terminate(); } +} // namespace llgs //---------------------------------------------------------------------- // main @@ -61,14 +63,14 @@ switch (argv[1][0]) { case 'g': - initialize(); + llgs::initialize(); main_gdbserver(argc, argv); - terminate(); + llgs::terminate(); break; case 'p': - initialize(); + llgs::initialize(); main_platform(argc, argv); - terminate(); + llgs::terminate(); break; case 'v': fprintf(stderr, "%s\n", lldb_private::GetVersion());