LLDB and attach failed while debugging JNI code

You may have encountered following issue while attaching to JVM

error: process exited with status -1 (attach failed ...

You can easily reproduce this issue following way

> export JAVA_HOME=~/opt/java/jdk-18.jdk/Contents/Home/
> lldb $JAVA_HOME/bin/java
(lldb) target create "~/opt/java/jdk-18.jdk/Contents/Home//bin/java"
Current executable set to '~/opt/java/jdk-18.jdk/Contents/Home/bin/java' (x86_64).
(lldb) run
error: process exited with status -1 (attach failed (Not allowed to attach to process.
Look in the console messages (Console.app), near the debugserver entries, when the 
attach failed. The subsystem that denied the attach permission will likely have 
logged an informative message about why it was denied.))
(lldb)

You can fix it. It’s just a matter of few steps

——————

1. Create code signing certificate

First of all, you need signing certificate that can sign code. Create one using Keychain Access.

Applications -> Utilities -> Keychain Access.app

once started, follow these steps:

– Keychain Access -> Certificate Assistant -> Create a Certificate…
– set Name to jvm-debugger
– select Let me override defaults -> Continue

– make sure to select Code Signing

– create certificate inside System Keychain

——————

2. Make sure the certificate is trusted

Select the certificate and double click it

Make sure to select Trust to Always Trust.

Note! – Restart macOS!

——————

3. Get current entitlements of java

> codesign -d --entitlements :- $JAVA_HOME/bin/java 2>/dev/null \
| XMLLINT_INDENT=" " xmllint -format -recover -

you will get something like this

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
 <dict>
  <key>com.apple.security.cs.debugger</key>
  <true/>
  <key>com.apple.security.cs.allow-jit</key>
  <true/>
  <key>com.apple.security.device.audio-input</key>
  <true/>
  <key>com.apple.security.cs.disable-library-validation</key>
  <true/>
  <key>com.apple.security.cs.allow-dyld-environment-variables</key>
  <true/>
  <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
  <true/>
 </dict>
</plist>

you can save this XML inside jvm.xml

——————

4. Add com.apple.security.get-task-allow entitlement to the list

You want to merge original file (one that you have gotten in previous step) with this one

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
 <dict>
  <key>com.apple.security.get-task-allow</key>
  <true/>
 </dict>
</plist>

That’s what you want to have – debugger.xml

——————

5. sign Java with newly created certificate and new entitlements

> sudo codesign --sign "jvm-debugger" -f --timestamp --options=runtime \
--entitlements ./debugger.xml $JAVA_HOME/bin/java

——————

6. Test whether it works as expected

> lldb $JAVA_HOME/bin/java
(lldb) target create "~/opt/java/jdk-18.jdk/Contents/Home//bin/java"
Current executable set to '~/opt/java/jdk-18.jdk/Contents/Home/bin/java' (x86_64).
(lldb) breakpoint set -n main
Breakpoint 1: 22 locations.
(lldb) run
Process 5511 launched: '~/opt/java/jdk-18.jdk/Contents/Home/bin/java' (x86_64)
Process 5511 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003a80 java`main
java`main:
->  0x100003a80 <+0>: pushq  %rbp
    0x100003a81 <+1>: movq   %rsp, %rbp
    0x100003a84 <+4>: pushq  %r15
    0x100003a86 <+6>: pushq  %r14
Target 0: (java) stopped.
(lldb) process handle --pass true --stop false --notify true SIGSEGV
NAME         PASS   STOP   NOTIFY
===========  =====  =====  ======
SIGSEGV      true   false  true
(lldb) continue
Process 5511 resuming
Process 5511 stopped and restarted: thread 3 received signal: SIGSEGV
Usage: java [options] <mainclass> [args...]
           (to execute a class)
   or  java [options] -jar <jarfile> [args...]
           (to execute a jar file)
   or  java [options] -m <module>[/<mainclass>] [args...]
       java [options] --module <module>[/<mainclass>] [args...]
           (to execute the main class in a module)
   or  java [options] <sourcefile> [args]
           (to execute a single source-file program)

 Arguments following the main class, source file, -jar <jarfile>,
 -m or --module <module>/<mainclass> are passed as the arguments to
 main class.
...
...
Process 5511 exited with status = 1 (0x00000001)
(lldb)