|
1 |
| -# Debugging Android apps using lldb and ds2 |
2 |
| - |
3 |
| -ds2 can run as a debug server on an Android device or emulator to enable remote debugging of native |
4 |
| -code in Android applications. While ds2 is capable of connecting to gdb, this guide will focus on |
5 |
| -using [lldb](https://lldb.llvm.org/). |
6 |
| - |
7 |
| -## Setup |
8 |
| - |
9 |
| -### Installing adb |
10 |
| -Debugging Android requires the [Android Debug Bridge (adb)](https://developer.android.com/tools/adb) |
11 |
| -be installed on your workstation. It comes as part of the Android SDK Platform Tools, which can be |
12 |
| -downloaded [here](https://developer.android.com/tools/releases/platform-tools#downloads) without |
13 |
| -installing Android Studio. |
14 |
| - |
15 |
| -### Enable Device Debugging |
16 |
| -If you are using a physical Android device, debugging must be enabled via the device's |
17 |
| -[developer options](https://developer.android.com/studio/debug/dev-options). |
18 |
| - |
19 |
| -This step is unnecessary if you are using an Android emulator. |
20 |
| - |
21 |
| -### Make the Application Debuggable |
22 |
| -The Android application you intend to debug must have `andriod:debuggable="true"` in its |
23 |
| -`AndroidManifest.xml` file: |
24 |
| -```xml |
25 |
| -<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
26 |
| - ... |
27 |
| - <application |
28 |
| - android:debuggable="true" |
29 |
| - ... |
30 |
| - |
31 |
| -``` |
32 |
| -While the property can be set directly, it is typically set via a debug |
33 |
| -[build variant](https://developer.android.com/build/build-variants). |
34 |
| - |
35 |
| -### Grant the Application Network Permission |
36 |
| -The application you intend to debug must have the `android.permission.INTERNET` permission declared |
37 |
| -in its `AndroidManifest.xml` file: |
38 |
| -```xml |
39 |
| - <uses-permission android:name="android.permission.INTERNET" /> |
40 |
| -``` |
41 |
| -If your application does not have this permission, you cannot debug it using ds2. |
42 |
| - |
43 |
| -This requirement is necessary because: |
44 |
| -1. ds2 must run in the context of an application's sandbox to debug the application |
45 |
| -2. When running in an application's sandbox, ds2 is limited to the set of permissions granted to |
46 |
| -that application |
47 |
| -3. ds2 connects to the debugger via a TCP connection, so it requires network access |
48 |
| - |
49 |
| -There are no other permissions required for ds2 to debug an Android application. |
50 |
| - |
51 |
| -## Running ds2 |
52 |
| - |
53 |
| -### Deploying ds2 to the Android device |
54 |
| -Use adb to deploy the ds2 binary from your workstation to the `/data/local/tmp` directory on your |
55 |
| -Android device: |
56 |
| - |
57 |
| -```bash |
58 |
| -$ adb push /path/to/ds2 /data/local/tmp |
59 |
| -``` |
60 |
| -> **_NOTE:_** For the Android emulator, copy the `x86_64` version of ds2. For an Android device, |
61 |
| -copy the `arm64-v8a` version. |
62 |
| - |
63 |
| -Ensure the binary is executable using `chmod +x` in the device shell: |
64 |
| -```bash |
65 |
| -$ adb shell chmod +x /data/local/tmp/ds2 |
66 |
| -``` |
67 |
| -### Copying ds2 into your application's sandbox |
68 |
| -ds2 can be executed directly from its `/data/local/tmp` location to debug programs launched via |
69 |
| -`adb shell`. However, to debug processes in an Android application, ds2 must be run in the context |
70 |
| -of that application's sandbox using `run-as`. |
71 |
| - |
72 |
| -Android applications can read from the `/data/local/tmp` directory, but, per security policy, they |
73 |
| -cannot execute progams from this location. To work-around this restriction, you must first copy the |
74 |
| -ds2 binary to the application's private storage using `run-as cp`: |
75 |
| -```bash |
76 |
| -$ adb shell run-as com.example.TestApp cp /data/local/tmp/ds2 ./ |
77 |
| -``` |
78 |
| -This command copies the ds2 executable to root of the application's working directory (e.g. |
79 |
| -`/data/user/0/com.example.TestApp`). Once copied, you can execute ds2 from this location to debug |
80 |
| -the application. |
81 |
| - |
82 |
| -To confirm ds2 can run in the sandbox, execute it with no arguments using `run-as`: |
83 |
| -``` |
84 |
| -$ adb shell run-as com.example.TestApp ./ds2 |
85 |
| -Usage: |
86 |
| - ./ds2 [v]ersion |
87 |
| - ./ds2 [g]dbserver [options] |
88 |
| - ./ds2 [p]latform [options] |
89 |
| -``` |
90 |
| -### Port forwarding |
91 |
| -To connect the debugger from your workstation to an instance of ds2 running on your Android device, |
92 |
| -use adb's port forwarding to forward a TCP port to use for the connection: |
93 |
| -```bash |
94 |
| -$ adb forward tpc:5432 tcp:5432 |
95 |
| -``` |
96 |
| -The exact port number you choose doesn't really matter as long as it is not already in use. Make |
97 |
| -note of the port number since you will need it later. |
98 |
| -### Running ds2 |
99 |
| -Launch ds2 on your Android device in "platform" mode. Tell it to listen on the same port number that |
100 |
| -you forwarded with adb. |
101 |
| -```bash |
102 |
| -$ adb shell run-as com.example.TestApp ./ds2 platform --server --listen *:5432 |
103 |
| -``` |
104 |
| -ds2 will now block waiting for an incoming connection from a debugger. If this command fails, make |
105 |
| -sure the application has network permission (see above) and that there isn't already an instance of |
106 |
| -ds2 running with the same port number. |
107 |
| - |
108 |
| -## Debugging with lldb |
109 |
| - |
110 |
| -### Connecting |
111 |
| -You are now ready to connect the debugger. Launch lldb from the command line: |
112 |
| -```bash |
113 |
| -$ lldb |
114 |
| -``` |
115 |
| -From the `(lldb)` prompt, run `platform select remote-android`: |
116 |
| -```bash |
117 |
| -(lldb) platform select remote-android |
118 |
| - Platform: remote-android |
119 |
| - Connected: no |
120 |
| -``` |
121 |
| -Connenct to the running ds2 instance using `platform connect connect://localhost:5432`, specifying |
122 |
| -the same port number that ds2 is listening on in the connect URI: |
123 |
| -```bash |
124 |
| -(lldb) platform connect connect://localhost:5432 |
125 |
| - Platform: remote-android |
126 |
| - Triple: aarch64-unknown-linux-android |
127 |
| -OS Version: 34 (5.10.198-android13-4-00050-g12f3388846c3-ab11920634) |
128 |
| - Hostname: localhost |
129 |
| - Connected: yes |
130 |
| -WorkingDir: /data/user/0/com.example.TestApp |
131 |
| - Kernel: #1 SMP PREEMPT Mon Jun 3 20:51:42 UTC 2024 |
132 |
| -``` |
133 |
| -Note the `WorkingDir` value: it should be the root of your application's data directory. |
134 |
| -### Attaching to a process |
135 |
| -With a platform connection established between lldb and ds2, you can now attach the debugger to a |
136 |
| -running process using its process ID (pid). To determine the pid for the process you wish to debug, |
137 |
| -list the processes running in your applications' sandbox with `platform process list`: |
138 |
| -```bash |
139 |
| -(lldb) platform process list |
140 |
| -2 matching processes were found on "remote-android" |
141 |
| - |
142 |
| -PID PARENT USER TRIPLE NAME |
143 |
| -====== ====== ========== ============================== ============================ |
144 |
| -8298 8296 u0_a284 aarch64-unknown-linux-android ds2 |
145 |
| -8883 1139 u0_a284 aarch64-unknown-linux-android app_process64 |
146 |
| -``` |
147 |
| -Because ds2 is running in your application's sandbox, this command will list only processes that |
148 |
| -are also running in the sandbox. You should see both the ds2 process and an application process for |
149 |
| -each of your application's running processes. If you only see ds2, make sure your application is |
150 |
| -is running and try again. |
151 |
| -
|
152 |
| -Once you know the pid for the process you wish to debug, use the `attach --pid` command to attach |
153 |
| -the debugger to it: |
154 |
| -``` |
155 |
| -(lldb) attach --pid 8883 |
156 |
| -Process 8883 stopped |
157 |
| -* thread #1, name = 'example.TestApp', stop reason = signal SIGSTOP |
158 |
| - frame #0: 0x00000072cc1cad28 libc.so`__epoll_pwait + 8 |
159 |
| -libc.so`__epoll_pwait: |
160 |
| --> 0x72cc1cad28 <+8>: cmn x0, #0x1, lsl #12 ; =0x1000 |
161 |
| - 0x72cc1cad2c <+12>: cneg x0, x0, hi |
162 |
| - 0x72cc1cad30 <+16>: b.hi 0xc6530 ; __set_errno_internal |
163 |
| - 0x72cc1cad34 <+20>: ret |
164 |
| -Executable module set to "/home/user/.lldb/module_cache/remote-android/.cache/00418409-0550-60A6-0094-DA0030D00989/app_process64". |
165 |
| -Architecture set to: aarch64-unknown-linux-android0 |
166 |
| -(lldb) |
167 |
| -``` |
168 |
| -This command spawns an additional ds2 instance (running in gdbserver mode) which attaches to the |
169 |
| -process and sets-up the debug session with lldb. |
170 |
| - |
171 |
| -Once attached, you can debug the process with standard gdb and lldb commands. An lldb tutorial can |
172 |
| -be found at [llvm.org](https://lldb.llvm.org/use/tutorial.html). |
| 1 | +# Debugging Android apps using lldb and ds2 |
| 2 | + |
| 3 | +ds2 can run as a debug server on an Android device or emulator to enable remote debugging of native |
| 4 | +code in Android applications. While ds2 is capable of connecting to gdb, this guide will focus on |
| 5 | +using [lldb](https://lldb.llvm.org/). |
| 6 | + |
| 7 | +## Setup |
| 8 | + |
| 9 | +### Installing adb |
| 10 | +Debugging Android requires the [Android Debug Bridge (adb)](https://developer.android.com/tools/adb) |
| 11 | +be installed on your workstation. It comes as part of the Android SDK Platform Tools, which can be |
| 12 | +downloaded [here](https://developer.android.com/tools/releases/platform-tools#downloads) without |
| 13 | +installing Android Studio. |
| 14 | + |
| 15 | +### Enable Device Debugging |
| 16 | +If you are using a physical Android device, debugging must be enabled via the device's |
| 17 | +[developer options](https://developer.android.com/studio/debug/dev-options). |
| 18 | + |
| 19 | +This step is unnecessary if you are using an Android emulator. |
| 20 | + |
| 21 | +### Make the Application Debuggable |
| 22 | +The Android application you intend to debug must have `andriod:debuggable="true"` in its |
| 23 | +`AndroidManifest.xml` file: |
| 24 | +```xml |
| 25 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| 26 | + ... |
| 27 | + <application |
| 28 | + android:debuggable="true" |
| 29 | + ... |
| 30 | + |
| 31 | +``` |
| 32 | +While the property can be set directly, it is typically set via a debug |
| 33 | +[build variant](https://developer.android.com/build/build-variants). |
| 34 | + |
| 35 | +### Grant the Application Network Permission |
| 36 | +The application you intend to debug must have the `android.permission.INTERNET` permission declared |
| 37 | +in its `AndroidManifest.xml` file: |
| 38 | +```xml |
| 39 | + <uses-permission android:name="android.permission.INTERNET" /> |
| 40 | +``` |
| 41 | +If your application does not have this permission, you cannot debug it using ds2. |
| 42 | + |
| 43 | +This requirement is necessary because: |
| 44 | +1. ds2 must run in the context of an application's sandbox to debug the application |
| 45 | +2. When running in an application's sandbox, ds2 is limited to the set of permissions granted to |
| 46 | +that application |
| 47 | +3. ds2 connects to the debugger via a TCP connection, so it requires network access |
| 48 | + |
| 49 | +There are no other permissions required for ds2 to debug an Android application. |
| 50 | + |
| 51 | +## Running ds2 |
| 52 | + |
| 53 | +### Deploying ds2 to the Android device |
| 54 | +Use adb to deploy the ds2 binary from your workstation to the `/data/local/tmp` directory on your |
| 55 | +Android device: |
| 56 | + |
| 57 | +```bash |
| 58 | +$ adb push /path/to/ds2 /data/local/tmp |
| 59 | +``` |
| 60 | +> **_NOTE:_** For the Android emulator, copy the `x86_64` version of ds2. For an Android device, |
| 61 | +copy the `arm64-v8a` version. |
| 62 | + |
| 63 | +Ensure the binary is executable using `chmod +x` in the device shell: |
| 64 | +```bash |
| 65 | +$ adb shell chmod +x /data/local/tmp/ds2 |
| 66 | +``` |
| 67 | +### Copying ds2 into your application's sandbox |
| 68 | +ds2 can be executed directly from its `/data/local/tmp` location to debug programs launched via |
| 69 | +`adb shell`. However, to debug processes in an Android application, ds2 must be run in the context |
| 70 | +of that application's sandbox using `run-as`. |
| 71 | + |
| 72 | +Android applications can read from the `/data/local/tmp` directory, but, per security policy, they |
| 73 | +cannot execute progams from this location. To work-around this restriction, you must first copy the |
| 74 | +ds2 binary to the application's private storage using `run-as cp`: |
| 75 | +```bash |
| 76 | +$ adb shell run-as com.example.TestApp cp /data/local/tmp/ds2 ./ |
| 77 | +``` |
| 78 | +This command copies the ds2 executable to root of the application's working directory (e.g. |
| 79 | +`/data/user/0/com.example.TestApp`). Once copied, you can execute ds2 from this location to debug |
| 80 | +the application. |
| 81 | + |
| 82 | +To confirm ds2 can run in the sandbox, execute it with no arguments using `run-as`: |
| 83 | +``` |
| 84 | +$ adb shell run-as com.example.TestApp ./ds2 |
| 85 | +Usage: |
| 86 | + ./ds2 [v]ersion |
| 87 | + ./ds2 [g]dbserver [options] |
| 88 | + ./ds2 [p]latform [options] |
| 89 | +``` |
| 90 | +### Port forwarding |
| 91 | +To connect the debugger from your workstation to an instance of ds2 running on your Android device, |
| 92 | +use adb's port forwarding to forward a TCP port to use for the connection: |
| 93 | +```bash |
| 94 | +$ adb forward tpc:5432 tcp:5432 |
| 95 | +``` |
| 96 | +The exact port number you choose doesn't really matter as long as it is not already in use. Make |
| 97 | +note of the port number since you will need it later. |
| 98 | +### Running ds2 |
| 99 | +Launch ds2 on your Android device in "platform" mode. Tell it to listen on the same port number that |
| 100 | +you forwarded with adb. |
| 101 | +```bash |
| 102 | +$ adb shell run-as com.example.TestApp ./ds2 platform --server --listen *:5432 |
| 103 | +``` |
| 104 | +ds2 will now block waiting for an incoming connection from a debugger. If this command fails, make |
| 105 | +sure the application has network permission (see above) and that there isn't already an instance of |
| 106 | +ds2 running with the same port number. |
| 107 | + |
| 108 | +## Debugging with lldb |
| 109 | + |
| 110 | +### Connecting |
| 111 | +You are now ready to connect the debugger. Launch lldb from the command line: |
| 112 | +```bash |
| 113 | +$ lldb |
| 114 | +``` |
| 115 | +From the `(lldb)` prompt, run `platform select remote-android`: |
| 116 | +```bash |
| 117 | +(lldb) platform select remote-android |
| 118 | + Platform: remote-android |
| 119 | + Connected: no |
| 120 | +``` |
| 121 | +Connenct to the running ds2 instance using `platform connect connect://localhost:5432`, specifying |
| 122 | +the same port number that ds2 is listening on in the connect URI: |
| 123 | +```bash |
| 124 | +(lldb) platform connect connect://localhost:5432 |
| 125 | + Platform: remote-android |
| 126 | + Triple: aarch64-unknown-linux-android |
| 127 | +OS Version: 34 (5.10.198-android13-4-00050-g12f3388846c3-ab11920634) |
| 128 | + Hostname: localhost |
| 129 | + Connected: yes |
| 130 | +WorkingDir: /data/user/0/com.example.TestApp |
| 131 | + Kernel: #1 SMP PREEMPT Mon Jun 3 20:51:42 UTC 2024 |
| 132 | +``` |
| 133 | +Note the `WorkingDir` value: it should be the root of your application's data directory. |
| 134 | +### Attaching to a process |
| 135 | +With a platform connection established between lldb and ds2, you can now attach the debugger to a |
| 136 | +running process using its process ID (pid). To determine the pid for the process you wish to debug, |
| 137 | +list the processes running in your applications' sandbox with `platform process list`: |
| 138 | +```bash |
| 139 | +(lldb) platform process list |
| 140 | +2 matching processes were found on "remote-android" |
| 141 | + |
| 142 | +PID PARENT USER TRIPLE NAME |
| 143 | +====== ====== ========== ============================== ============================ |
| 144 | +8298 8296 u0_a284 aarch64-unknown-linux-android ds2 |
| 145 | +8883 1139 u0_a284 aarch64-unknown-linux-android app_process64 |
| 146 | +``` |
| 147 | +Because ds2 is running in your application's sandbox, this command will list only processes that |
| 148 | +are also running in the sandbox. You should see both the ds2 process and an application process for |
| 149 | +each of your application's running processes. If you only see ds2, make sure your application is |
| 150 | +is running and try again. |
| 151 | +
|
| 152 | +Once you know the pid for the process you wish to debug, use the `attach --pid` command to attach |
| 153 | +the debugger to it: |
| 154 | +``` |
| 155 | +(lldb) attach --pid 8883 |
| 156 | +Process 8883 stopped |
| 157 | +* thread #1, name = 'example.TestApp', stop reason = signal SIGSTOP |
| 158 | + frame #0: 0x00000072cc1cad28 libc.so`__epoll_pwait + 8 |
| 159 | +libc.so`__epoll_pwait: |
| 160 | +-> 0x72cc1cad28 <+8>: cmn x0, #0x1, lsl #12 ; =0x1000 |
| 161 | + 0x72cc1cad2c <+12>: cneg x0, x0, hi |
| 162 | + 0x72cc1cad30 <+16>: b.hi 0xc6530 ; __set_errno_internal |
| 163 | + 0x72cc1cad34 <+20>: ret |
| 164 | +Executable module set to "/home/user/.lldb/module_cache/remote-android/.cache/00418409-0550-60A6-0094-DA0030D00989/app_process64". |
| 165 | +Architecture set to: aarch64-unknown-linux-android0 |
| 166 | +(lldb) |
| 167 | +``` |
| 168 | +This command spawns an additional ds2 instance (running in gdbserver mode) which attaches to the |
| 169 | +process and sets-up the debug session with lldb. |
| 170 | + |
| 171 | +Once attached, you can debug the process with standard gdb and lldb commands. An lldb tutorial can |
| 172 | +be found at [llvm.org](https://lldb.llvm.org/use/tutorial.html). |
0 commit comments