Skip to content
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

IN-VM GDB local debugger crashes for ARM toolchain on Windows #3562

Closed
staringatphones opened this issue Oct 29, 2021 · 32 comments
Closed

IN-VM GDB local debugger crashes for ARM toolchain on Windows #3562

staringatphones opened this issue Oct 29, 2021 · 32 comments
Assignees
Labels
Feature: Debugger Status: Internal This is being tracked internally by the Ghidra team
Milestone

Comments

@staringatphones
Copy link

Describe the bug
The "IN-VM GNU gdb local debugger" option on Windows throws an uncaught exception and does not continue when supplied with the official GNU Arm Embedded Toolchain version of GDB.

To Reproduce
Steps to reproduce the behavior:

  1. Install the GNU Arm Embedded Toolchain from the developer.arm.com website. The gcc-arm-none-eabi-10.3-2021.10-win32.exe installer was used. (Link to downloads page, Direct Download Link to installer).
  2. Launch Ghidra.
  3. Import an arm-compiled ELF file into the Ghidra project.
  4. Analyze the ELF using the Ghidra CodeBrowser tool.
  5. Open the analyzed ELF in the Ghidra Debugger tool.
  6. In the "Debugger Targets" pane, click the icon to "Create a new connection to a debugging agent."
  7. Select "IN-VM GNU gdb local debugger" in the main dropdown.
  8. In the "GDB launch command" entry, type "C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10\bin\arm-none-eabi-gdb.exe" (with quotes)
  9. Leave the "Use existing session via new-ui" box unchecked.
  10. Click Connect.
  11. Ghidra throws "Uncaught Exception! UnsatisfiedLinkError - The specified procedure could not be found."

Expected behavior
The ARM-specific GDB executable should launch within Ghidra, which allows the Debugger tool to debug the binary. (Note, a remote GDB server will be necessary to interface with an arm processor. The command target remote hostname:port would then be given to GDB. However, GDB fails to even launch in this scenario.)

Screenshots
Screen Shot 2021-10-29 at 11 37 09 AM

The screenshot above shows the error message. A text file containing the entire contents of the error message is included in the Attachments section below.

Attachments
error-output.txt
This text file contains the complete text of the error given.

Environment (please complete the following information):

  • OS: Windows 10
  • Java Version: 17.0.1
  • Ghidra Version: 10.0.4
  • Ghidra Origin: official ghidra-sre.org distro
@d-millar
Copy link
Collaborator

Hi @staringatphones,

My apologies first off - you said "Windows Command Prompt" in your last post, and somehow I just didn't register the fact that you were running Windows. We are aware of this issue, and a fix (probably based on ConPty) is on our TODO list. May be awhile though.

I'd suggest checking out the previous issues #2908 and #3102 in Issues, and #2721 in Discussions. The basic workaround is to use the "GDB over SSH" agent rather than "IN-VM GDB" and either a WSL client or separate Linux VM running a gdbserver. Definitely, too complicated by half, but....let us know if neither of those will work for you or if you get stuck.

D

P.S. Thanks for closing out the other issue, by the by!

@tommai78101
Copy link
Contributor

tommai78101 commented Feb 18, 2023

Future readers from 2023 and onwards, for anyone who is looking into this issue about how to use the "IN-VM GNU gdb local debugger" in Ghidra 10.2.2 Stable on a Windows operating system:

Please make sure you use DOUBLE BACKSLASHES when you put in the absolute path in the "GDB launch command" text field:

image

Otherwise, you will get an error message: GetLastError() returned 2

The 2 here means ERROR_FILE_NOT_FOUND in Win32 API.

@tommai78101
Copy link
Contributor

Why double backslashes? You would ask. Well, well, well............

This is how Java interprets backslashes, when parsed into string values. The first backslash is to escape the proceeding second backslash.

@d-millar
Copy link
Collaborator

Yes, we should probably try to validate the input for this. As noted, double-backslashes (or forward slashes) will work.

@tommai78101
Copy link
Contributor

tommai78101 commented Feb 21, 2023

On the latest Ghidra 10.2.3, doing the same steps in OP now gives the following 2 errors, depending on whether you have "Use existing session via new-ui" checked or not.

Checked:

ghidra.dbg.error.DebuggerModelTerminatingException: Error while starting GDB: Pty implementation does not support null 
sessions. Try D:\devkitPro\devkitARM\bin\arm-none-eabi-gdb.exe i mi2
---------------------------------------------------
Build Date: 2023-Feb-08 1242 EST
Ghidra Version: 10.2.3
Java Home: D:\Java\jdk-17.35
JVM Version: Eclipse Adoptium 17
OS: Windows 10 10.0 amd64
Workstation: DESKTOP-CV0SBRD

Unchecked:

java.lang.UnsupportedOperationException: ConPTY does not have a name
---------------------------------------------------
Build Date: 2023-Feb-08 1242 EST
Ghidra Version: 10.2.3
Java Home: D:\Java\jdk-17.35
JVM Version: Eclipse Adoptium 17
OS: Windows 10 10.0 amd64
Workstation: DESKTOP-PC

If you used the GDB interpreter modes hinted by the error message -i mi2, you will get this:

ghidra.async.DisposedException: ghidra.dbg.error.DebuggerModelTerminatingException: GDB is terminating. Could not get target endian. Could not get target os. Could not get target architecture.
---------------------------------------------------
Build Date: 2023-Feb-08 1242 EST
Ghidra Version: 10.2.3
Java Home: D:\Java\jdk-17.35
JVM Version: Eclipse Adoptium 17
OS: Windows 10 10.0 amd64
Workstation: DESKTOP-CV0SBRD

Also, strangely enough, my operating system is actually Windows 11 Pro 22H2, not Windows 10 as printed in the text field.

image

Steps I did:

  1. Launch Ghidra v10.2.3 in Windows 11.
  2. Open the debugger.
  3. While waiting on the debugger, open and run the GDB server I have hosted on Windows 11.
  4. When GDB server is listening to a port, open a new debugger target in Ghidra Debugger.
  5. Set the option to IN-VM GNU gdb local debugger.
  6. Regardless of whether you check the checkbox or not for "Use existing session via new-ui", press the Connect button.

I have already verified the GDB server is able to communicate with GDB on a different Linux machine. So this is a Windows-specifc issue. This is more specific to the new changes made for 10.2.3:

* Debugger. Added GDB connector support for Windows (tested with GDB 11.1 on msys64). (GP-869, Issue #2908)

What is not really clear is, whether 7ab2f5d adds the Windows native implemention of ConPty to Ghidra, or Ghidra still has to go through MinGW to be able to connect to the GDB server.

@d-millar
Copy link
Collaborator

Hmmm, well, let's see if we can move the ball down the field a little....

You probably do not want "Use existing session via new-ui". This involves running gdb outside of Ghidra, issuing the command to spawn a new ui, and connectiing to that. For now, let's assume that's more than you need.

I think you were closest to succeeding with the "-i m2". For example, on my box, I am using "GDB launch command" == "C:/msys64/mingw64/bin/gdb -i mi2". The error message you are getting suggests that your version of arm-none-eabi-gdb does not support the commands: "show endian", "show os", and "show architecture". Maybe, try running arm-none-eabi-gdb on your client machine outside of Ghidra and executing those commands. If they are not defined, I think we can fake them out, but I have to remember how to load those definitions early enough in the process to make Ghidra happy.

@d-millar
Copy link
Collaborator

Another option if those commands are not supported is to use "gdb-multiarch".

@tommai78101
Copy link
Contributor

tommai78101 commented Feb 22, 2023

@d-millar I'm able to run arm-none-eabi-gdb in GDB/MI 2 interpreter mode, as output here:

D:\devkitPro\devkitARM\bin> .\arm-none-eabi-gdb.exe -i mi2
D:\devkitPro\devkitARM\bin\arm-none-eabi-gdb.exe: warning: Couldn't determine a path for the index cache directory.
=thread-group-added,id="i1"
~"GNU gdb (GDB) 10.2\n"
~"Copyright (C) 2021 Free Software Foundation, Inc.\n"
~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law."
~"\nType \"show copying\" and \"show warranty\" for details.\n"
~"This GDB was configured as \"--host=x86_64-w64-mingw32 --target=arm-none-eabi\".\n"
~"Type \"show configuration\" for configuration details.\n"
~"For bug reporting instructions, please see:\n"
~"<https://www.gnu.org/software/gdb/bugs/>.\n"
~"Find the GDB manual and other documentation resources online at:\n    <http://www.gnu.org/software/gdb/documentation/>."
~"\n\n"
~"For help, type \"help\".\n"
~"Type \"apropos word\" to search for commands related to \"word\".\n"
(gdb)
echo "Hello world"
&"echo \"Hello world\"\n"
~"\"Hello world\""
^done
(gdb)

This is the same one with the latest compiled gdb-multiarch for Windows:

E:\gdb_multiarch\bin> ./gdb.exe -i mi2
=thread-group-added,id="i1"
~"GNU gdb (GDB) 12.1\n"
~"Copyright (C) 2022 Free Software Foundation, Inc.\n"
~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law."
~"\nType \"show copying\" and \"show warranty\" for details.\n"
~"This GDB was configured as \"x86_64-w64-mingw32\".\n"
~"Type \"show configuration\" for configuration details.\n"
~"For bug reporting instructions, please see:\n"
~"<https://www.gnu.org/software/gdb/bugs/>.\n"
~"Find the GDB manual and other documentation resources online at:\n    <http://www.gnu.org/software/gdb/documentation/>."
~"\n\n"
~"For help, type \"help\".\n"
~"Type \"apropos word\" to search for commands related to \"word\".\n"
(gdb)
echo "hello world"
&"echo \"hello world\"\n"
~"\"hello world\""
^done
(gdb)

I believed all GDB version 6.0+ and above should have GDB/MI 2 by default.

@tommai78101
Copy link
Contributor

@d-millar Oh, just re-read your post. Here are the outputs for the show os, show endian, and show architecture on the arm-none-eabi-gdb:

show os
&"show os\n"
~"The current OS ABI is \"auto\" (currently \"none\").\n"
^done
(gdb)
show endian
&"show endian\n"
~"The target endianness is set automatically (currently little endian).\n"
^done
(gdb)
show architecutre
&"show architecutre\n"
&"Undefined show command: \"architecutre\".  Try \"help show\".\n"
^error,msg="Undefined show command: \"architecutre\".  Try \"help show\"."
(gdb)
show architecture
&"show architecture\n"
~"The target architecture is set to \"auto\" (currently \"arm\").\n"
^done
(gdb)

@d-millar
Copy link
Collaborator

Interesting - all of that is basically good news, i.e. the necessary commands are supported. So, the initial connection must be failing in some less-than-obvious way that is getting masked by the "show" errors. I will dig into the code some more tomorrow - see if I can re-create your issue.

@d-millar
Copy link
Collaborator

OK, this is totally a maybe, but try setting the environment variable HOME to %USERPROFILE%, re-launch Ghidra, and make sure space and slashes are escaped in the cmdline. I just tried this with "C:/Program\ Files\ (x86)/GNU\ Arm\ Embedded\ Toolchain/10\ 2021.10/bin/arm-none-eabi-gdb -i mi2".

@tommai78101
Copy link
Contributor

@d-millar I don't think it works. I still get the same errors Could not connect: GDB is terminating, even when I put the environment variable HOME in both the User Variables and in the System Variables. I even logged out and relogin just to make extra sure the environment variables are loaded up.

@d-millar
Copy link
Collaborator

OK, I thought that was possible but unlikely as I hit problems starting arm-none-eabi-gdb that I hadn't seen before, but they weren' terminating GDB. Are you running using ghidraRun or support/ghidraDebug? Any error messages you get either in the shell for ghidraDebug or the DebugConsole might be helpful - figuring out how it icould be dying is proving to be a bit of a challenge as I can't reproduce the error.

@d-millar
Copy link
Collaborator

One more idea, in launch.properties, try adding "VMARG=-Dagent.gdb.manger.log=true" - this will generate a GDB.log file in your home directory(?).

@tommai78101
Copy link
Contributor

tommai78101 commented Feb 22, 2023

@d-millar Noticed that the flag is spelled incorrectly, so I modified it a bit.

Here's the corrected flag:

VMARGS=-Dagent.gdb.manager.log=true

I tried this flag in the launch.properties file, and nothing happened. So I modified the launch.bat and added:

:: Set GDB logging enabled
set VMARG_LIST=%VMARG_LIST% -Dagent.gdb.manager.log=true

To force it to create the %USERPROFILE%\.ghidra\.ghidra_10.2.3_PUBLIC\GDB.log.

Here are the contents of the GDB.log:

<MI2: =thread-group-added,id="i1"
*CMD: class agent.gdb.manager.impl.cmd.GdbListInferiorsCommand
>MI2: -list-thread-groups

There's not much in here. The output contents are the same if I relaunch Ghidra with .\ghidraDebug.bat.

And here are the contents from the console terminal after running .\ghidraDebug.bat:

Listening for transport dt_socket at address: 18001
INFO  Using log config file: file:/D:/ghidra/ghidra_10.2.3_PUBLIC/support/debug.log4j.xml  (LoggingInitialization.java:51)
INFO  Using log file: C:\Users\fakeUser\.ghidra\.ghidra_10.2.3_PUBLIC\application.log  (LoggingInitialization.java:52)
INFO  Loading user preferences: C:\Users\fakeUser\.ghidra\.ghidra_10.2.3_PUBLIC\preferences  (Preferences.java:117)
INFO  Loading previous preferences: C:\Users\fakeUser\.ghidra\.ghidra_10.2.2_PUBLIC\preferences  (Preferences.java:170)
INFO  Class search complete (1703 ms)  (ClassSearcher.java:276)
INFO  Initializing SSL Context  (SSLContextInitializer.java:76)
INFO  Initializing Random Number Generator...  (SecureRandomFactory.java:37)
INFO  Random Number Generator initialization complete: SHA1PRNG  (SecureRandomFactory.java:41)
INFO  Trust manager disabled, cacerts have not been set  (ApplicationTrustManagerFactory.java:95)
INFO  User fakeUser started Ghidra.  (GhidraRun.java:80)
DEBUG Recovery snapshot timer set to 5 minute(s)  (RecoverySnapshotMgrPlugin.java:170)
INFO  Opening project: D:\Documents\ghidra\MyGhidraTestProject  (DefaultProject.java:134)
INFO  Packed database cache: C:\Users\fakeUser\AppData\Local\Ghidra\packed-db-cache  (PackedDatabaseCache.java:64)
DEBUG Using cached packed database: D:\ghidra\ghidra_10.2.3_PUBLIC\Ghidra\Features\Base\data\typeinfo\generic\generic_clib.gdt  (PackedDatabaseCache.java:364)
INFO  local Windows Pty session. PID = 1136  (LocalWindowsNativeProcessPtySession.java:40)
DEBUG Terminating agent.gdb.manager.impl.GdbManagerImpl@7503781a  (GdbManagerImpl.java:799)
DEBUG Terminating agent.gdb.manager.impl.GdbManagerImpl@7503781a  (GdbManagerImpl.java:799)
DEBUG STDOUT,MI2 reader exiting because java.lang.NumberFormatException: For input string: ""  (GdbManagerImpl.java:161)
ERROR Could not connect: ghidra.dbg.error.DebuggerModelTerminatingException: GDB is terminating  (ConsoleErrorDisplay.java:59)
WARN  Could not get target architecture: java.util.concurrent.CompletionException: ghidra.dbg.error.DebuggerModelTerminatingException: GDB is terminating  (DebuggerObjectModel.java:562)
WARN  Could not get target os: java.util.concurrent.CompletionException: ghidra.dbg.error.DebuggerModelTerminatingException: GDB is terminating  (DebuggerObjectModel.java:562)
WARN  Could not get target endian: java.util.concurrent.CompletionException: ghidra.dbg.error.DebuggerModelTerminatingException: GDB is terminating  (DebuggerObjectModel.java:562)

I did see a NumberFormatException, so perhaps there's something wrong with it?

I'm curious about your steps such that you're not able to reproduce the error. May I take reference on your steps?

@d-millar
Copy link
Collaborator

d-millar commented Feb 23, 2023

Well, I think your assessment may be correct - it might be the NumberFormatException. At a guess, this might be coming from "info proc mappings", but...again, not feeling like that's likely. So, my steps as exactly as I can describe them:

(1) From fresh download of ghidra_10.2.3_PUBLIC, execute ghidraRun.bat (double-clicked)
(2) Set up a project in the usual way, and open the default Debugger tool
(3) From Targets, hit "Create a new connection..."
(4) From the pull-down menu, select "IN-VM GNU gdb local debugger"
(5) For "GDB launch command", enter "C:/Program\ Files\ (x86)/GNU\ Arm\ Embedded\ Toolchain/10\ 2021.10/bin/arm-none-eabi-gdb -i mi2". This is the path I had for the default install from the download link you sent.
(6) Leave "Use existing session via new-ui" unchecked, and hit "Connect"
(7) At this point, in the Objects tree, I have Session->Inferiors->1 - <null>
(8) I can open this node and verify the Environment node, and I can run commands in the Interpreter

It sounds like you have done the same thing, but somewhere between 6 and 7 things went south.

Some things that are different:
I am running Windows Server 2019 (shouldn't make a difference?)
I am using OpenJDK 17.0.1 2021-10-19 LTS (also shouldn't make a difference?)

What do you get if you run "info proc mappings" in arm-none-eabi-gdb? I am getting "Not supported on this target".

@tommai78101
Copy link
Contributor

tommai78101 commented Feb 23, 2023

@d-millar Same here.

(gdb)
info proc mappings
&"info proc mappings\n"
&"Not supported on this target.\n"
^error,msg="Not supported on this target."
(gdb)

However, we can trick GDB to give us info proc mappings:

  1. Put the following code in a text file stored in your desired location of your choice.
define info proc mappings
	echo 0x0 0xFFFFFFFF 0x100000000 0x0 mem \n
end
  1. Go into GDB interpreter mode.
  2. Type source path/to/text/file/containing/info proc mappings

This is what I did in GDB interpreter mode:

(gdb)
define info proc mappings
&"define info proc mappings\n"
~"Type commands for definition of \"info proc mappings\".\n"
~"End with a line saying just \"end\".\n"
~">"
echo 0x0 0xFFFFFFFF 0x100000000 0x0 mem \n
~">"
end
^done
(gdb)
info proc mappings
&"info proc mappings\n"
~"0x0 0xFFFFFFFF 0x100000000 0x0 mem \n"
^done
(gdb)

Would this be helpful to you in some way?

@d-millar
Copy link
Collaborator

LOL - that's my trick. (Glad it's catching on.) Just checking, though, you're not using that (say, from a .gdbinit file) in your current Ghidra tests, correct?

@tommai78101
Copy link
Contributor

tommai78101 commented Feb 23, 2023

@d-millar No. I'm not even aware that you can put this in a .gdbinit some where.

@d-millar
Copy link
Collaborator

Good, I guess - I'd hate to be burned by my own trick. Will drag some extra eyes on the log file tomorrow; see if anyone has a brilliant insight into the current problem.

@tommai78101
Copy link
Contributor

tommai78101 commented Feb 23, 2023

@d-millar Here's a better debug log messages I discovered as I finished setting up the Ghidra development environment and actually launching the debugger on my machine:

INFO  Using log config file: file:/E:/LargeGithubProjects/ghidra/Ghidra/Framework/Generic/bin/main/generic.log4jdev.xml   (LoggingInitialization.java:50) 
INFO  Using log file: C:\Users\fakeUser\.ghidra\.ghidra_10.3_DEV_location_LargeGithubProjects\application.log   (LoggingInitialization.java:51) 
INFO  Loading user preferences: C:\Users\fakeUser\.ghidra\.ghidra_10.3_DEV_location_LargeGithubProjects\preferences   (Preferences.java:122) 
INFO  Searching for classes...   (ClassSearcher.java:257) 
INFO  Class search complete (3579 ms)   (ClassSearcher.java:276) 
INFO  User fakeUser started Ghidra.   (GhidraRun.java:77) 
INFO  local Windows Pty session. PID = 24716   (LocalWindowsNativeProcessPtySession.java:40) 
ERROR Could not connect: ghidra.dbg.error.DebuggerModelTerminatingException: Error while starting GDB: Could not detect GDB's interpreter mode. 
Try D:\devkitPro\devkitARM\bin\arm-none-eabi-gdb.exe -i mi2   (ConsoleErrorDisplay.java:59) 
ERROR Could not get target architecture java.util.concurrent.CompletionException: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
	at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
	at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:761)
	at java.base/java.util.concurrent.CompletableFuture.uniAcceptStage(CompletableFuture.java:735)
	at java.base/java.util.concurrent.CompletableFuture.thenAccept(CompletableFuture.java:2182)
	at agent.gdb.manager.impl.GdbManagerImpl.doExecute(GdbManagerImpl.java:853)
	at agent.gdb.manager.impl.GdbManagerImpl.execute(GdbManagerImpl.java:838)
	at agent.gdb.manager.impl.GdbManagerImpl.consoleCapture(GdbManagerImpl.java:1747)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.lambda$1(GdbModelTargetEnvironment.java:87)
	at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
	at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshArchitecture(GdbModelTargetEnvironment.java:86)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshInternal(GdbModelTargetEnvironment.java:168)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.<init>(GdbModelTargetEnvironment.java:65)
	at agent.gdb.model.impl.GdbModelTargetInferior.<init>(GdbModelTargetInferior.java:100)
	at agent.gdb.model.impl.GdbModelTargetInferiorContainer.getTargetInferior(GdbModelTargetInferiorContainer.java:163)
	at agent.gdb.model.impl.GdbModelTargetInferiorContainer.inferiorAdded(GdbModelTargetInferiorContainer.java:58)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at ghidra.util.datastruct.ListenerMap$ListenerHandler.lambda$0(ListenerMap.java:134)
	at ghidra.util.datastruct.ListenerMap$1.execute(ListenerMap.java:57)
	at ghidra.util.datastruct.ListenerMap$ListenerHandler.invoke(ListenerMap.java:122)
	at jdk.proxy2/jdk.proxy2.$Proxy55.inferiorAdded(Unknown Source)
	at agent.gdb.manager.impl.GdbManagerImpl.lambda$58(GdbManagerImpl.java:470)
	at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
	at agent.gdb.manager.impl.GdbManagerImpl.lambda$68(GdbManagerImpl.java:880)
	at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:757)
	... 27 more
  (DebuggerObjectModel.java:571) 
ERROR Could not get target os java.util.concurrent.CompletionException: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
	at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
	at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:761)
	at java.base/java.util.concurrent.CompletableFuture.uniAcceptStage(CompletableFuture.java:735)
	at java.base/java.util.concurrent.CompletableFuture.thenAccept(CompletableFuture.java:2182)
	at agent.gdb.manager.impl.GdbManagerImpl.doExecute(GdbManagerImpl.java:853)
	at agent.gdb.manager.impl.GdbManagerImpl.execute(GdbManagerImpl.java:838)
	at agent.gdb.manager.impl.GdbManagerImpl.consoleCapture(GdbManagerImpl.java:1747)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.lambda$5(GdbModelTargetEnvironment.java:120)
	at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
	at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshOS(GdbModelTargetEnvironment.java:119)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshInternal(GdbModelTargetEnvironment.java:169)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.<init>(GdbModelTargetEnvironment.java:65)
	at agent.gdb.model.impl.GdbModelTargetInferior.<init>(GdbModelTargetInferior.java:100)
	at agent.gdb.model.impl.GdbModelTargetInferiorContainer.getTargetInferior(GdbModelTargetInferiorContainer.java:163)
	at agent.gdb.model.impl.GdbModelTargetInferiorContainer.inferiorAdded(GdbModelTargetInferiorContainer.java:58)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at ghidra.util.datastruct.ListenerMap$ListenerHandler.lambda$0(ListenerMap.java:134)
	at ghidra.util.datastruct.ListenerMap$1.execute(ListenerMap.java:57)
	at ghidra.util.datastruct.ListenerMap$ListenerHandler.invoke(ListenerMap.java:122)
	at jdk.proxy2/jdk.proxy2.$Proxy55.inferiorAdded(Unknown Source)
	at agent.gdb.manager.impl.GdbManagerImpl.lambda$58(GdbManagerImpl.java:470)
	at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
	at agent.gdb.manager.impl.GdbManagerImpl.lambda$68(GdbManagerImpl.java:880)
	at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:757)
	... 27 more
  (DebuggerObjectModel.java:571) 
ERROR Could not get target endian java.util.concurrent.CompletionException: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
	at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
	at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:761)
	at java.base/java.util.concurrent.CompletableFuture.uniAcceptStage(CompletableFuture.java:735)
	at java.base/java.util.concurrent.CompletableFuture.thenAccept(CompletableFuture.java:2182)
	at agent.gdb.manager.impl.GdbManagerImpl.doExecute(GdbManagerImpl.java:853)
	at agent.gdb.manager.impl.GdbManagerImpl.execute(GdbManagerImpl.java:838)
	at agent.gdb.manager.impl.GdbManagerImpl.consoleCapture(GdbManagerImpl.java:1747)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.lambda$9(GdbModelTargetEnvironment.java:149)
	at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
	at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshEndian(GdbModelTargetEnvironment.java:148)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshInternal(GdbModelTargetEnvironment.java:170)
	at agent.gdb.model.impl.GdbModelTargetEnvironment.<init>(GdbModelTargetEnvironment.java:65)
	at agent.gdb.model.impl.GdbModelTargetInferior.<init>(GdbModelTargetInferior.java:100)
	at agent.gdb.model.impl.GdbModelTargetInferiorContainer.getTargetInferior(GdbModelTargetInferiorContainer.java:163)
	at agent.gdb.model.impl.GdbModelTargetInferiorContainer.inferiorAdded(GdbModelTargetInferiorContainer.java:58)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at ghidra.util.datastruct.ListenerMap$ListenerHandler.lambda$0(ListenerMap.java:134)
	at ghidra.util.datastruct.ListenerMap$1.execute(ListenerMap.java:57)
	at ghidra.util.datastruct.ListenerMap$ListenerHandler.invoke(ListenerMap.java:122)
	at jdk.proxy2/jdk.proxy2.$Proxy55.inferiorAdded(Unknown Source)
	at agent.gdb.manager.impl.GdbManagerImpl.lambda$58(GdbManagerImpl.java:470)
	at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
	at agent.gdb.manager.impl.GdbManagerImpl.lambda$68(GdbManagerImpl.java:880)
	at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:757)
	... 27 more
  (DebuggerObjectModel.java:571) 

It seems like in GdbManagerImpl.java:135, it first enters the while loop. It is then successful at reading the first line, which has the value:

=thread-group-added,id="i1"

And then on the second while loop iteration, just before it attempts to finish invoking reader.readLine(), it throws a NumberFormatException. Thus, I dug a bit even deeper. Here is the second line that has the value:

~"GNU gdb (GDB) 10.2\n"�[K

Github won't show the control character ESC, so here it is in picture form:

image

After the K is parsed, the next character is what throws the NumberFormatException. It seems like GDB interpreter mode terminated its output string too soon here, as Ghidra is expecting a number here, either a 0, 1, or 2, but GDB sends back an empty character.

We all know that when logging into GDB interpreter mode, it outputs the following:

D:\devkitPro\devkitARM\bin> .\arm-none-eabi-gdb.exe -i mi2
=thread-group-added,id="i1"
~"GNU gdb (GDB) 10.2\n" <-----------------------------------------------------------------------
~"Copyright (C) 2021 Free Software Foundation, Inc.\n"
~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law."
~"\nType \"show copying\" and \"show warranty\" for details.\n"
~"This GDB was configured as \"--host=x86_64-w64-mingw32 --target=arm-none-eabi\".\n"
~"Type \"show configuration\" for configuration details.\n"
~"For bug reporting instructions, please see:\n"
~"<https://www.gnu.org/software/gdb/bugs/>.\n"
~"Find the GDB manual and other documentation resources online at:\n    <http://www.gnu.org/software/gdb/documentation/>."
~"\n\n"
~"For help, type \"help\".\n"
~"Type \"apropos word\" to search for commands related to \"word\".\n"
(gdb)

It is the line marked with a <----- that showed where Ghidra couldn't finish parsing and exited too quickly.

Somehow, in the AnsiBufferedInputStream.java:368 in the Debugger-agent-gdb project, where in the parseNumericBuffer(), it is supposely expecting a numeric code when for whatever reason, on Windows operating systems, the GDB doesn't really give any numbers to execute an "Erase In Line" operation.

I'm not even sure whether if it's a bug on the GDB side, a bug on the Debugger-agent-gdb side, or it could be a minor difference due to different operating systems that's not taken into account yet. If anyone else have insights on this issue, it would be great to hear more about this.

@nsadeveloper789
Copy link
Contributor

It might just be a shorthand case we hadn't considered in the ANSI parsing: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797

Looks like ESC[K is same as ESC[0K. Thanks for pointing it out. A lot of this ANSI stuff was introduced in order to handle Windows ConPTY. It inserts a lot of these control sequences when piping output from the child application to the parent console, so I suspect the difference in ANSI codes is due to a difference in Windows version. In any case, an ANSI decode problem like that shouldn't be throwing an exception :/ .

If you're willing to try a quick and dirty patch, where you pointed out in AnsiBufferedInputStream.java, patch the method:

	protected int parseNumericBuffer() {
		String numeric = readAndClearEscBuf();
		if (numeric.isEmpty()) {
			return 0;
		}
		int result = Integer.parseInt(numeric);
		return result;
	}

Other things call that where blank may not imply 0, so I'm not sure this is a solution, but it should at least move us forward troubleshooting.

@tommai78101
Copy link
Contributor

tommai78101 commented Feb 23, 2023

@nsadeveloper789 It worked. Applying the patch made Ghidra able to communicate with GDB successfully on the ARM device. I'll go ahead and create a pull request for this.

@d-millar
Copy link
Collaborator

Wow, score - nice call @nsadeveloper789

@tommai78101
Copy link
Contributor

@d-millar @nsadeveloper789 Made a pull request. I credited @nsadeveloper789 for the patch.

@d-millar
Copy link
Collaborator

Yep - thanks!

@nsadeveloper789
Copy link
Contributor

Glad to know it worked. As mentioned, I'm not certain it's The Right Solution (TM). On the other hand, if it works, then why not? Good news is this only applies to GDB on Windows, so I think it's relatively low risk to just take it. Thanks for submitting the patch!

@ryanmkurtz ryanmkurtz added Status: Internal This is being tracked internally by the Ghidra team and removed Status: Triage Information is being gathered labels Feb 25, 2023
@ryanmkurtz ryanmkurtz added this to the 10.2.4 milestone Feb 25, 2023
@ryanmkurtz ryanmkurtz modified the milestones: 10.2.4, 10.3 May 11, 2023
@StevensND
Copy link

StevensND commented Feb 7, 2024

I don't have the IN-VM GNU gdb local debugger option.

I'm using Ghidra 11.0.1 btw.

Has the name changed or something?. Here's the screenshot

Sin título

In addition, when I modify the GDB launch command and indicate the following path:

C:\\Users\\Stevens\\Desktop\\gdb-multiarch-14.1\\bin\\gdb-multiarch.exe

I get the -i mi2 error shown in this screenshot. I'm using the 14.1 version of gdb-multiarch-windows

EDIT: If I run gdb-multiarch as administrator then I get this error:

com.sun.jna.LastErrorException: GetLastError() returned 740

Build Date: 2024-Jan-30 1212 EST
Ghidra Version: 11.0.1
Java Home: C:\Program Files\Java\jdk-17
JVM Version: Oracle Corporation 17.0.10
OS: Windows 10 10.0 amd64
Workstation: DESKTOP-6N85SK3

@nsadeveloper789
Copy link
Contributor

A few things:

  1. Yes, the name is changed: "gdb" is the new name.
  2. There have been related issues with others using gdb-14.1 (See Can't Connect to GDB Terminal #6107)
  3. You'll need to append -i mi2 to the gdb path (launch command).
  4. Try with and without DOS line endings. This seems to be inconsistent with gdb on Windows, depending on whether it's based on Cygwin or MinGW.

@nsadeveloper789
Copy link
Contributor

I don't have any insight on the 740 error, as I've not tried running this as administrator on Windows.

@StevensND
Copy link

I don't have any insight on the 740 error, as I've not tried running this as administrator on Windows.

I tried the suggestions and I still get the 740 error.

And I need to run gdb-multiarch as administrator to bypass the -i mi2 error.

@nsadeveloper789
Copy link
Contributor

And I need to run gdb-multiarch as administrator to bypass the -i mi2 error.

I'm not sure I understand. Is trying to workaround the -i mi2 thing the only reason you need to run gdb as admin? I wouldn't ordinarily recommend running gdb as admin, unless you need to attach to a process of higher privilege. I suspect the 740 error (operation requires elevation) actually comes earlier in the launch process than whatever error related to -i mi2.

Please try the following and, if it doesn't work, please include all the details from the error message you can:

  1. Do not run gdb as admin. (I actually don't know what config options you're using to do this... In any case, have please have gdb run under the same account as Ghidra.)
  2. Select the "gdb" connector.
  3. Make sure the command line ends with -i mi2. No quotes. If gdb's path contains spaces, you can quote the path to gdb, but the -i mi2 part must be outside the quotes.
  4. Use DOS line endings.

If you indeed need to run gdb with higher privileges than Ghidra, you may try writing a batch script that wraps gdb -i mi2 with runas. In theory, you can then supply the path to that script instead of gdb when configuring the launch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature: Debugger Status: Internal This is being tracked internally by the Ghidra team
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants