android-ndk开发(6): 查看反汇编
2025/05/05
1. 概要
android-ndk 是基于 clang 的工具链, clang 则保持了和 gcc 的高度兼容。 在 Linux 开发机上, GCC 套件里的 objdump 提供了反汇编的功能。 实际上 android-ndk 也提供了一份 objdump, 用它可以反汇编 android 平台的二进制文件。
使用调试器带来的交互式体验, 在 android-ndk 这一交叉编译 - 连接到设备上运行和调试的场景下, 卡顿感比较明显; 相比之下, 查看反汇编代码来辅助 crash 分析, 是 android-ndk 开发过程中会使用到的一个技能。 不求精通汇编, 反正可以问 AI; 而具备查看反汇编的思维则显得更加重要。
2. 查看反汇编
2.1 查看可执行文件的反汇编
还记得上一篇我们搭建的 hello-world 工程吗?
#include <stdio.h>
int main() {
printf("hello, world!\n");
}
#!/bin/bash
NDK=~/soft/android-ndk/r21e
HOST_TAG=darwin-x86_64
$NDK/toolchains/llvm/prebuilt/$HOST_TAG/bin/clang \
-target aarch64-linux-android21 hello.c \
-o hello
adb push hello /data/local/tmp/hello
adb shell "cd /data/local/tmp; chmod +x ./hello; ./hello"
编译后会在当前目录得到 hello
可执行文件。 查看它的反汇编:
/<path/to>/aarch64-linux-android-objdump -d hello
例如我的路径是:
/Users/zz/soft/android-ndk/r21e/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android-objdump -d hello
反汇编结果如下:
hello: file format elf64-littleaarch64
Disassembly of section .plt:
0000000000000560 <printf@plt-0x20>:
560: a9bf7bf0 stp x16, x30, [sp,#-16]!
564: b0000010 adrp x16, 1000 <main+0x9d8>
568: f947de11 ldr x17, [x16,#4024]
56c: 913ee210 add x16, x16, #0xfb8
570: d61f0220 br x17
574: d503201f nop
578: d503201f nop
57c: d503201f nop
0000000000000580 <printf@plt>:
580: b0000010 adrp x16, 1000 <main+0x9d8>
584: f947e211 ldr x17, [x16,#4032]
588: 913f0210 add x16, x16, #0xfc0
58c: d61f0220 br x17
0000000000000590 <__libc_init@plt>:
590: b0000010 adrp x16, 1000 <main+0x9d8>
594: f947e611 ldr x17, [x16,#4040]
598: 913f2210 add x16, x16, #0xfc8
59c: d61f0220 br x17
00000000000005a0 <__cxa_atexit@plt>:
5a0: b0000010 adrp x16, 1000 <main+0x9d8>
5a4: f947ea11 ldr x17, [x16,#4048]
5a8: 913f4210 add x16, x16, #0xfd0
5ac: d61f0220 br x17
Disassembly of section .text:
00000000000005b0 <_start>:
5b0: 910003e0 mov x0, sp
5b4: 14000001 b 5b8 <_start_main>
00000000000005b8 <_start_main>:
5b8: d100c3ff sub sp, sp, #0x30
5bc: a9027bfd stp x29, x30, [sp,#32]
5c0: b0000008 adrp x8, 1000 <main+0x9d8>
5c4: f947f508 ldr x8, [x8,#4072]
5c8: b0000009 adrp x9, 1000 <main+0x9d8>
5cc: f947fd29 ldr x9, [x9,#4088]
5d0: b000000a adrp x10, 1000 <main+0x9d8>
5d4: f947f94a ldr x10, [x10,#4080]
5d8: 9e670100 fmov d0, x8
5dc: 4e181d20 mov v0.d[1], x9
5e0: 3d8003e0 str q0, [sp]
5e4: f9000bea str x10, [sp,#16]
5e8: b0000002 adrp x2, 1000 <main+0x9d8>
5ec: f947f042 ldr x2, [x2,#4064]
5f0: 910003e3 mov x3, sp
5f4: aa1f03e1 mov x1, xzr
5f8: 910083fd add x29, sp, #0x20
5fc: 97ffffe5 bl 590 <__libc_init@plt>
0000000000000600 <__atexit_handler_wrapper>:
600: b4000040 cbz x0, 608 <__atexit_handler_wrapper+0x8>
604: d61f0000 br x0
608: d65f03c0 ret
000000000000060c <atexit>:
60c: 90000008 adrp x8, 0 <note_android_ident-0x218>
610: 91180108 add x8, x8, #0x600
614: d0000002 adrp x2, 2000 <__bss_start>
618: 91000042 add x2, x2, #0x0
61c: aa0003e1 mov x1, x0
620: aa0803e0 mov x0, x8
624: 17ffffdf b 5a0 <__cxa_atexit@plt>
0000000000000628 <main>:
628: d10083ff sub sp, sp, #0x20
62c: a9017bfd stp x29, x30, [sp,#16]
630: 910043fd add x29, sp, #0x10
634: 90000000 adrp x0, 0 <note_android_ident-0x218>
638: 91197000 add x0, x0, #0x65c
63c: 52800008 mov w8, #0x0 // #0
640: b81fc3a8 stur w8, [x29,#-4]
644: 97ffffcf bl 580 <printf@plt>
648: b85fc3a8 ldur w8, [x29,#-4]
64c: 2a0803e0 mov w0, w8
650: a9417bfd ldp x29, x30, [sp,#16]
654: 910083ff add sp, sp, #0x20
658: d65f03c0 ret
也可以封装为脚本: disassemble.sh
:
#!/bin/bash
NDK=~/soft/android-ndk/r21e
HOST_TAG=darwin-x86_64
$NDK/toolchains/llvm/prebuilt/$HOST_TAG/bin/aarch64-linux-android-objdump -d hello
3.2 查看单个 .c/.cpp 文件的反汇编
对于 hello-world 工程, 可执行文件对应到唯一的源代码文件 hello.c。
对于稍微有点内容的工程, 不止一个源代码文件。 拿我本地的 ncv 工程来说, 有5个源文件:
- color.c
- io.c
- main.c
- thread.c
- timer.c
这对应两个事情:
- 使用 cmake 构建比较方便, 使用纯粹的命令行也行, 但是有点麻烦
- 查看反汇编时, 分别对每个 .c 对应的 .o 反汇编, 内容比较清晰; 所有文件编译出的可执行文件 test_ncv 的反汇编, 内容太多, 看不过来
我的 CMakeLists.txt 为:
cmake_minimum_required(VERSION 3.10)
project(ncv C)
add_library(ncv STATIC color.c io.c)
add_executable(test_ncv main.c)
target_link_libraries(test_ncv PUBLIC ncv)
对应的 cmake 构建输出, build/CMakeFiles 目录下的文件:
➜ ncv git:(main) ✗ tree build/CMakeFiles
build/CMakeFiles
├── 3.31.6
│ ├── CMakeCCompiler.cmake
│ ├── CMakeDetermineCompilerABI_C.bin
│ └── CMakeSystem.cmake
├── CMakeConfigureLog.yaml
├── TargetDirectories.txt
├── cmake.check_cache
├── ncv.dir
│ ├── color.c.o
│ └── io.c.o
├── pkgRedirects
├── rules.ninja
└── test_ncv.dir
└── main.c.o
5 directories, 10 files
我们感兴趣的 ncv.dir/color.c.o
和 ncv.dir/io.c.o
. 他们是库目标 ncv
对应的源代码们, 对应的 .o 文件。
执行反汇编:
/Users/zz/soft/android-ndk/r21e/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android-objdump -d build/CMakeFiles/ncv.dir/color.c.o
一些反汇编结果:
Disassembly of section .text.nv21_to_rgb_worker:
0000000000000000 <nv21_to_rgb_worker>:
0: a9bf4ff4 stp x20, x19, [sp,#-16]!
4: 69402408 ldpsw x8, x9, [x0]
8: 6b09011f cmp w8, w9
c: 540014aa b.ge 2a0 <nv21_to_rgb_worker+0x2a0>
10: f940040a ldr x10, [x0,#8]
14: 9000000b adrp x11, 0 <nv21_to_rgb_worker>
18: 9000000c adrp x12, 0 <nv21_to_rgb_worker>
1c: fd400160 ldr d0, [x11]
20: fd400181 ldr d1, [x12]
24: a940314b ldp x11, x12, [x10]
28: 9000000d adrp x13, 0 <nv21_to_rgb_worker>
2c: 9000000e adrp x14, 0 <nv21_to_rgb_worker>
30: fd4001a2 ldr d2, [x13]
34: fd4001c3 ldr d3, [x14]
38: a941396d ldp x13, x14, [x11,#16]
3c: f9400990 ldr x16, [x12,#16]
40: 69462971 ldpsw x17, x10, [x11,#48]
44: b9803180 ldrsw x0, [x12,#48]
48: b940056b ldr w11, [x11,#4]
4c: 9100050f add x15, x8, #0x1
50: 9b113501 madd x1, x8, x17, x13
54: d37ffa2c lsl x12, x17, #1
58: 9b1135f1 madd x17, x15, x17, x13
5c: 910005cd add x13, x14, #0x1
60: 9b004102 madd x2, x8, x0, x16
64: d37ff80e lsl x14, x0, #1
68: 9b0041e0 madd x0, x15, x0, x16
6c: 9100042f add x15, x1, #0x1
70: 91000630 add x16, x17, #0x1
74: 91000851 add x17, x2, #0x2
78: 91000800 add x0, x0, #0x2
7c: 52801fe1 mov w1, #0xff // #255
80: 7100057f cmp w11, #0x1
84: 5400014a b.ge ac <nv21_to_rgb_worker+0xac>
88: 91000908 add x8, x8, #0x2
8c: 8b0c01ef add x15, x15, x12
90: 8b0c0210 add x16, x16, x12
94: 8b0e0231 add x17, x17, x14
98: eb09011f cmp x8, x9
9c: 8b0e0000 add x0, x0, x14
a0: 5400100a b.ge 2a0 <nv21_to_rgb_worker+0x2a0>
a4: 7100057f cmp w11, #0x1
a8: 54ffff0b b.lt 88 <nv21_to_rgb_worker+0x88>
ac: 93417d02 sbfx x2, x8, #1, #31
b0: 9b0a3442 madd x2, x2, x10, x13
b4: aa0003e3 mov x3, x0
b8: aa1103e4 mov x4, x17
bc: aa1003e5 mov x5, x16
c0: aa0f03e6 mov x6, x15
c4: 2a0b03e7 mov w7, w11
c8: 385ff053 ldurb w19, [x2,#-1]
cc: 3c5ff0c4 ldur b4, [x6,#-1]
d0: 38402454 ldrb w20, [x2],#2
d4: 510008e7 sub w7, w7, #0x2
d8: 51020273 sub w19, w19, #0x80
dc: 1e620265 scvtf d5, w19
e0: 7e61d884 ucvtf d4, d4
e4: 1e6008a7 fmul d7, d5, d0
e8: 1e6428e4 fadd d4, d7, d4
ec: 1e780093 fcvtzs w19, d4
f0: 7103fe7f cmp w19, #0xff
f4: 1a81b273 csel w19, w19, w1, lt
f8: 0ab37e73 bic w19, w19, w19, asr #31
fc: 381fe093 sturb w19, [x4,#-2]
100: 3c5ff0c6 ldur b6, [x6,#-1]
104: 51020294 sub w20, w20, #0x80
108: 1e620290 scvtf d16, w20
10c: 1e610a04 fmul d4, d16, d1
110: 7e61d8c6 ucvtf d6, d6
114: 1e6208a5 fmul d5, d5, d2
118: 1e6438c6 fsub d6, d6, d4
11c: 1e6538c6 fsub d6, d6, d5
120: 1e7800d3 fcvtzs w19, d6
124: 7103fe7f cmp w19, #0xff
128: 1a81b273 csel w19, w19, w1, lt
12c: 0ab37e73 bic w19, w19, w19, asr #31
130: 381ff093 sturb w19, [x4,#-1]
134: 3c5ff0d1 ldur b17, [x6,#-1]
138: 1e630a06 fmul d6, d16, d3
13c: 7e61da30 ucvtf d16, d17
140: 1e7028d0 fadd d16, d6, d16
144: 1e780213 fcvtzs w19, d16
148: 7103fe7f cmp w19, #0xff
14c: 1a81b273 csel w19, w19, w1, lt
150: 0ab37e73 bic w19, w19, w19, asr #31
154: 39000093 strb w19, [x4]
158: 3d4000d0 ldr b16, [x6]
15c: 7e61da10 ucvtf d16, d16
160: 1e7028f0 fadd d16, d7, d16
164: 1e780213 fcvtzs w19, d16
168: 7103fe7f cmp w19, #0xff
16c: 1a81b273 csel w19, w19, w1, lt
170: 0ab37e73 bic w19, w19, w19, asr #31
174: 39000493 strb w19, [x4,#1]
178: 3d4000d0 ldr b16, [x6]
17c: 7e61da10 ucvtf d16, d16
180: 1e643a10 fsub d16, d16, d4
184: 1e653a10 fsub d16, d16, d5
188: 1e780213 fcvtzs w19, d16
18c: 7103fe7f cmp w19, #0xff
190: 1a81b273 csel w19, w19, w1, lt
194: 0ab37e73 bic w19, w19, w19, asr #31
198: 39000893 strb w19, [x4,#2]
19c: 3d4000d0 ldr b16, [x6]
1a0: 910008c6 add x6, x6, #0x2
1a4: 7e61da10 ucvtf d16, d16
1a8: 1e7028d0 fadd d16, d6, d16
1ac: 1e780213 fcvtzs w19, d16
1b0: 7103fe7f cmp w19, #0xff
1b4: 1a81b273 csel w19, w19, w1, lt
1b8: 0ab37e73 bic w19, w19, w19, asr #31
1bc: 39000c93 strb w19, [x4,#3]
1c0: 3c5ff0b0 ldur b16, [x5,#-1]
1c4: 91001884 add x4, x4, #0x6
1c8: 7e61da10 ucvtf d16, d16
1cc: 1e7028f0 fadd d16, d7, d16
1d0: 1e780213 fcvtzs w19, d16
1d4: 7103fe7f cmp w19, #0xff
1d8: 1a81b273 csel w19, w19, w1, lt
1dc: 0ab37e73 bic w19, w19, w19, asr #31
1e0: 381fe073 sturb w19, [x3,#-2]
1e4: 3c5ff0b0 ldur b16, [x5,#-1]
1e8: 7e61da10 ucvtf d16, d16
1ec: 1e643a10 fsub d16, d16, d4
1f0: 1e653a10 fsub d16, d16, d5
1f4: 1e780213 fcvtzs w19, d16
1f8: 7103fe7f cmp w19, #0xff
1fc: 1a81b273 csel w19, w19, w1, lt
200: 0ab37e73 bic w19, w19, w19, asr #31
204: 381ff073 sturb w19, [x3,#-1]
208: 3c5ff0b0 ldur b16, [x5,#-1]
20c: 7e61da10 ucvtf d16, d16
210: 1e7028d0 fadd d16, d6, d16
214: 1e780213 fcvtzs w19, d16
218: 7103fe7f cmp w19, #0xff
21c: 1a81b273 csel w19, w19, w1, lt
220: 0ab37e73 bic w19, w19, w19, asr #31
224: 39000073 strb w19, [x3]
228: 3d4000b0 ldr b16, [x5]
22c: 7e61da10 ucvtf d16, d16
230: 1e7028e7 fadd d7, d7, d16
234: 1e7800f3 fcvtzs w19, d7
238: 7103fe7f cmp w19, #0xff
23c: 1a81b273 csel w19, w19, w1, lt
240: 0ab37e73 bic w19, w19, w19, asr #31
244: 39000473 strb w19, [x3,#1]
248: 3d4000a7 ldr b7, [x5]
24c: 7e61d8e7 ucvtf d7, d7
250: 1e6438e4 fsub d4, d7, d4
254: 1e653884 fsub d4, d4, d5
258: 1e780093 fcvtzs w19, d4
25c: 7103fe7f cmp w19, #0xff
260: 1a81b273 csel w19, w19, w1, lt
264: 0ab37e73 bic w19, w19, w19, asr #31
268: 39000873 strb w19, [x3,#2]
26c: 3d4000a4 ldr b4, [x5]
270: 910008a5 add x5, x5, #0x2
274: 7e61d884 ucvtf d4, d4
278: 1e6428c4 fadd d4, d6, d4
27c: 1e780093 fcvtzs w19, d4
280: 7103fe7f cmp w19, #0xff
284: 1a81b273 csel w19, w19, w1, lt
288: 0ab37e73 bic w19, w19, w19, asr #31
28c: 710000ff cmp w7, #0x0
290: 39000c73 strb w19, [x3,#3]
294: 91001863 add x3, x3, #0x6
298: 54fff18c b.gt c8 <nv21_to_rgb_worker+0xc8>
29c: 17ffff7b b 88 <nv21_to_rgb_worker+0x88>
2a0: aa1f03e0 mov x0, xzr
2a4: a8c14ff4 ldp x20, x19, [sp],#16
2a8: d65f03c0 ret
Disassembly of section .text.ncv_color_cvt:
0000000000000000 <ncv_color_cvt>:
0: d10143ff sub sp, sp, #0x50
4: f9001bf3 str x19, [sp,#48]
8: a9047bfd stp x29, x30, [sp,#64]
c: 910103fd add x29, sp, #0x40
10: d53bd053 mrs x19, tpidr_el0
14: f9401669 ldr x9, [x19,#40]
18: aa0003e8 mov x8, x0
1c: 52800020 mov w0, #0x1 // #1
20: f81e83a9 stur x9, [x29,#-24]
24: b4000668 cbz x8, f0 <ncv_color_cvt+0xf0>
28: b4000641 cbz x1, f0 <ncv_color_cvt+0xf0>
2c: b940010a ldr w10, [x8]
30: b9400029 ldr w9, [x1]
34: 7108055f cmp w10, #0x201
38: 54000321 b.ne 9c <ncv_color_cvt+0x9c>
3c: 711c053f cmp w9, #0x701
40: 540002e1 b.ne 9c <ncv_color_cvt+0x9c>
44: b9400509 ldr w9, [x8,#4]
48: b940042a ldr w10, [x1,#4]
4c: 6b0a013f cmp w9, w10
50: 540004e1 b.ne ec <ncv_color_cvt+0xec>
54: b9400909 ldr w9, [x8,#8]
58: b940082a ldr w10, [x1,#8]
5c: 6b0a013f cmp w9, w10
60: 54000461 b.ne ec <ncv_color_cvt+0xec>
64: f940090a ldr x10, [x8,#16]
68: b400054a cbz x10, 110 <ncv_color_cvt+0x110>
6c: f940082a ldr x10, [x1,#16]
70: b400050a cbz x10, 110 <ncv_color_cvt+0x110>
74: 90000000 adrp x0, 0 <ncv_color_cvt>
78: a90187e8 stp x8, x1, [sp,#24]
7c: 910063e8 add x8, sp, #0x18
80: 91000000 add x0, x0, #0x0
84: 910023e1 add x1, sp, #0x8
88: 290127ff stp wzr, w9, [sp,#8]
8c: f9000be8 str x8, [sp,#16]
90: 94000000 bl 0 <ncv_parallel_for>
94: 2a1f03e0 mov w0, wzr
98: 14000016 b f0 <ncv_color_cvt+0xf0>
9c: 7108055f cmp w10, #0x201
a0: 2a1f03e0 mov w0, wzr
a4: 54000261 b.ne f0 <ncv_color_cvt+0xf0>
a8: 7108053f cmp w9, #0x201
ac: 54000221 b.ne f0 <ncv_color_cvt+0xf0>
b0: b9400509 ldr w9, [x8,#4]
b4: b940042a ldr w10, [x1,#4]
b8: 6b0a013f cmp w9, w10
bc: 54000181 b.ne ec <ncv_color_cvt+0xec>
c0: b9400909 ldr w9, [x8,#8]
c4: b940082a ldr w10, [x1,#8]
c8: 6b0a013f cmp w9, w10
cc: 54000101 b.ne ec <ncv_color_cvt+0xec>
d0: f9400908 ldr x8, [x8,#16]
d4: 528000a0 mov w0, #0x5 // #5
d8: b40000c8 cbz x8, f0 <ncv_color_cvt+0xf0>
dc: f9400828 ldr x8, [x1,#16]
e0: f100011f cmp x8, #0x0
e4: 1a9f0000 csel w0, w0, wzr, eq
e8: 14000002 b f0 <ncv_color_cvt+0xf0>
ec: 52800080 mov w0, #0x4 // #4
f0: f9401668 ldr x8, [x19,#40]
f4: f85e83a9 ldur x9, [x29,#-24]
f8: eb09011f cmp x8, x9
fc: 540000e1 b.ne 118 <ncv_color_cvt+0x118>
100: a9447bfd ldp x29, x30, [sp,#64]
104: f9401bf3 ldr x19, [sp,#48]
108: 910143ff add sp, sp, #0x50
10c: d65f03c0 ret
110: 528000a0 mov w0, #0x5 // #5
114: 17fffff7 b f0 <ncv_color_cvt+0xf0>
118: 94000000 bl 0 <__stack_chk_fail>
4. 反汇编有什么用处?
当发生 crash 时, 当程序耗时超出预期时, 都可以从汇编代码进一步分析。
举例: 对于一个C/C++函数,返回值类型不是 void; 如果缺失返回值,可能会在 Debug 模式发生 TRAP(crash,信号为TRAP), 在 Release 模式能运行而不会 Trap。 这时候可以从反汇编分析。
再举例: 对于 rgb 到 gray 的颜色空间转换, 在 Release 模式下的程序运行的很快, 在 Debug 模式下运行的稍慢, 并且如果手写了 arm neon (intrinsics)优化, 仍然和 Release 模式差不多快。 那么检查 Release 下的反汇编代码, 大概率是生成了 neon intrinsics 指令, 并且可能还把流水排布做了优化。
5. 搞一个网站查看反汇编呗!
很可惜, godbolt.org 目前还不支持 android-ndk 作为编译器。
对于 infra 比较到位的公司, 应该可以自行搭建一个在线网站来查看反汇编, 以及根据 crash 堆栈自动做分析等 (比如腾讯)。
6. 总结
给出了 android-ndk 程序查看反汇编代码的方法, 包括单个可执行文件、 基于 cmake 构建时的每个源代码文件。 简要提及了反汇编典型使用场景: crash 分析、 性能分析.