Skip to content

[LLDB] Show exit code on Windows if process can't launch #141290

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 27, 2025

Conversation

Nerixyz
Copy link
Contributor

@Nerixyz Nerixyz commented May 23, 2025

When running a process that would exit before LLDB could stop the target, it would try to interpret (and subsequently format) the exit code as a Win32 error. However, processes on Windows won't return Win32 errors in that case. They will often return an NTSTATUS. One common case for this to occur is when a DLL is missing. In that case, the process will start successfully, but it will exit with STATUS_DLL_NOT_FOUND.
LLDB would previously return "unknown error", because it tried to FormatMessage 0xC0000135 which doesn't work, so it fell back to "unknown error".

This PR changes the error to be the string "Process prematurely exited with {0:x}" and doesn't try to format the exit code. One could FormatMessage an NTSTATUS by passing FORMAT_MESSAGE_FROM_HMODULE and a handle to ntdll.dll, however, I don't think we can get the required format arguments (e.g. the missing DLL name - %hs).

@Nerixyz Nerixyz requested a review from JDevlieghere as a code owner May 23, 2025 20:04
@llvmbot llvmbot added the lldb label May 23, 2025
@llvmbot
Copy link
Member

llvmbot commented May 23, 2025

@llvm/pr-subscribers-lldb

Author: nerix (Nerixyz)

Changes

When running a process that would exit before LLDB could stop the target, it would try to interpret (and subsequently format) the exit code as a Win32 error. However, processes on Windows won't return Win32 errors in that case. They will often return an NTSTATUS. One common case for this to occur is when a DLL is missing. In that case, the process will start successfully, but it will exit with STATUS_DLL_NOT_FOUND.
LLDB would previously return "unknown error", because it tried to FormatMessage 0xC0000135 which doesn't work, so it fell back to "unknown error".

This PR changes the error to be the string "Process prematurely exited with {0:x}" and doesn't try to format the exit code. One could FormatMessage an NTSTATUS by passing FORMAT_MESSAGE_FROM_HMODULE and a handle to ntdll.dll, however, I don't think we can get the required format arguments (e.g. the missing DLL name - %hs).


Full diff: https://github.com/llvm/llvm-project/pull/141290.diff

5 Files Affected:

  • (modified) lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.cpp (+2-1)
  • (added) lldb/test/API/windows/launch/missing-dll/Makefile (+5)
  • (added) lldb/test/API/windows/launch/missing-dll/TestMissingDll.py (+22)
  • (added) lldb/test/API/windows/launch/missing-dll/dummy_dll.c (+1)
  • (added) lldb/test/API/windows/launch/missing-dll/main.c (+6)
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.cpp
index bde72d61b0fee..a0f622fd69902 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.cpp
@@ -483,7 +483,8 @@ void ProcessDebugger::OnExitProcess(uint32_t exit_code) {
   // 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);
+    Status error = Status::FromErrorStringWithFormatv(
+        "Process prematurely exited with {0:x}", exit_code);
     OnDebuggerError(error, 0);
   }
 }
diff --git a/lldb/test/API/windows/launch/missing-dll/Makefile b/lldb/test/API/windows/launch/missing-dll/Makefile
new file mode 100644
index 0000000000000..43e02d1d8f22b
--- /dev/null
+++ b/lldb/test/API/windows/launch/missing-dll/Makefile
@@ -0,0 +1,5 @@
+C_SOURCES := main.c
+DYLIB_C_SOURCES := dummy_dll.c
+DYLIB_NAME := dummy_dll
+
+include Makefile.rules
diff --git a/lldb/test/API/windows/launch/missing-dll/TestMissingDll.py b/lldb/test/API/windows/launch/missing-dll/TestMissingDll.py
new file mode 100644
index 0000000000000..b47040fbf794b
--- /dev/null
+++ b/lldb/test/API/windows/launch/missing-dll/TestMissingDll.py
@@ -0,0 +1,22 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class MissingDllTestCase(TestBase):
+    @skipUnlessWindows
+    def test(self):
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+        dll = self.getBuildArtifact("dummy_dll.dll")
+        self.assertTrue(remove_file(dll))
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        launch_info = lldb.SBLaunchInfo(None)
+        launch_info.SetWorkingDirectory(self.get_process_working_directory())
+
+        error = lldb.SBError()
+        target.Launch(launch_info, error)
+        self.assertFailure(error, "Process prematurely exited with 0xc0000135")
diff --git a/lldb/test/API/windows/launch/missing-dll/dummy_dll.c b/lldb/test/API/windows/launch/missing-dll/dummy_dll.c
new file mode 100644
index 0000000000000..61bde26534966
--- /dev/null
+++ b/lldb/test/API/windows/launch/missing-dll/dummy_dll.c
@@ -0,0 +1 @@
+__declspec(dllexport) void SomeFunction(void) {}
diff --git a/lldb/test/API/windows/launch/missing-dll/main.c b/lldb/test/API/windows/launch/missing-dll/main.c
new file mode 100644
index 0000000000000..c1e0d3222c48b
--- /dev/null
+++ b/lldb/test/API/windows/launch/missing-dll/main.c
@@ -0,0 +1,6 @@
+__declspec(dllimport) void SomeFunction(void);
+
+int main(void) {
+    SomeFunction();
+    return 0;
+}

Copy link

github-actions bot commented May 23, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@Nerixyz Nerixyz force-pushed the fix/lldb-win-early-exit branch from 31aab0a to aacbcb4 Compare May 23, 2025 20:07
@DavidSpickett
Copy link
Collaborator

DavidSpickett commented May 27, 2025

I see the magic exit value listed in https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55.

I wondered if we could say exited with NTSTATUS <value>, but I'm not sure if all exit codes in this scenario would be NTSATUS or only a subset.

Not a big deal though, googling "windows exit code 0xC0000135" gets you pretty close to what it means.

@Nerixyz Nerixyz force-pushed the fix/lldb-win-early-exit branch from aacbcb4 to 339bae7 Compare May 27, 2025 15:30
@Nerixyz
Copy link
Contributor Author

Nerixyz commented May 27, 2025

I wondered if we could say exited with NTSTATUS <value>, but I'm not sure if all exit codes in this scenario would be NTSATUS or only a subset.

That's the part where I'm not sure either, but as you mentioned, googling for the exit code usually gets you to the right destination.

Copy link
Collaborator

@DavidSpickett DavidSpickett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. This is a nice improvement thanks for working on it.

@DavidSpickett
Copy link
Collaborator

That's the part where I'm not sure either

This PR is strictly better than before, so it's fine as it is.

@JDevlieghere
Copy link
Member

@Nerixyz Do you need someone to merge this on your behalf?

@Nerixyz
Copy link
Contributor Author

Nerixyz commented May 27, 2025

@Nerixyz Do you need someone to merge this on your behalf?

Yes.

@JDevlieghere JDevlieghere merged commit c0a8723 into llvm:main May 27, 2025
10 checks passed
@Nerixyz Nerixyz deleted the fix/lldb-win-early-exit branch May 27, 2025 20:17
sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Jun 3, 2025
When running a process that would exit before LLDB could stop the
target, it would try to interpret (and subsequently format) the exit
code as a Win32 error. However, processes on Windows won't return Win32
errors in that case. They will often return an
[NTSTATUS](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55).
One common case for this to occur is when a DLL is missing. In that
case, the process will start successfully, but it will exit with
`STATUS_DLL_NOT_FOUND`.
LLDB would previously return "unknown error", because it tried to
[`FormatMessage`](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage)
`0xC0000135` which doesn't work, so it fell back to "unknown error".

This PR changes the error to be the string "Process prematurely exited
with {0:x}" and doesn't try to format the exit code. One could
`FormatMessage` an `NTSTATUS` by passing `FORMAT_MESSAGE_FROM_HMODULE`
and a handle to `ntdll.dll`, however, I don't think we can get the
required format arguments (e.g. the missing DLL name - `%hs`).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants