背景
我们最近收到多个应用在 Android16 的设备上闪退问题,均是错误堆栈在 __memset_aarch64_mops、__memmove_aarch64_mops 这个两个函数身上。包括某宝应用在 Android16 的适配上也发生了此类错误,想必仍有许多开发者会犯的错,这里简单的介绍下来龙去脉。
MOPS 特性
Android16 开始引进了 MOPS 的函数优化,具体优化了 C 库的 memset、memcpy、memmove 函数。
标题 | 地址 |
---|---|
arm64: support Armv8.8 memcpy instructions in userspace | lwn.net/Articles/92... |
Add MOPS memcpy/memset/memmove from arm-optimized-routines. | android-review.googlesource.com/c/platform/... |
Enable MOPS routines. | android-review.googlesource.com/c/platform/... |
特别注意
: ARM MOPS 的支持需要 ARMv8.8 /ARMv9.3 之后,目前只有新硬件
支持,在函数加载链接阶段在 resolver 函数中通过判断 HWCAP2_MOPS 来选择,然而众多开发者发生问题的地方也是这里。因此老设备升级到 Android16 若不甚选择了 MOPS 函数则会发生 ILL_ILLOPC
指令异常。
developer.arm.com/documentati...

示例
这里以一个较有代表性的程序来解答下该问题如何发生的。国内某应用在 Android16 上打开必现闪退,其错误堆栈如下:
less
signal 4 (SIGILL), code 1 (ILL_ILLOPC), fault addr 0x0000007d3ff3a9c8
x0 0000007fedec2658 x1 0000000000000000 x2 0000000000000080 x3 0000007fedec2658
x4 00000079d2874bd4 x5 00000079d2874c10 x6 00000079d2873dbc x7 00000079d2873df8
x8 0000000000000001 x9 522c04132487c7f6 x10 0000000000000007 x11 0000007a054a68e8
x12 0000007fedec1290 x13 0000007fedec1370 x14 0000007fedec2838 x15 00000000ebad6a89
x16 0000007a054a0540 x17 0000007d3ff3a9c0 x18 0000000000002840 x19 b400007b34410330
x20 0000007fedec2838 x21 0000000000000001 x22 0000007fedec2880 x23 0000000000000001
x24 0000000000000005 x25 0000007d557d5880 x26 0000000000000002 x27 0000007d346219d8
x28 0000007fedec2750 x29 0000007fedec2730
lr 0000007a053cc45c sp 0000007fedec25b0 pc 0000007d3ff3a9c8 pst 0000000060001000
2 total frames
backtrace:
#00 pc 000000000006c9c8 /apex/com.android.runtime/lib64/bionic/libc.so (__memset_aarch64_mops+8) (BuildId: 5fe6683fc17a8d811149b5b1d5f850b9)
#01 pc 000000000004f458 <anonymous:7a0537d000>
如何调试
我们可 zygote64 进程的 memset_resolver 处断点,通过父子进程分离其共享内存的特性,若程序进入了 MOPS 分支则让其暂停,,即可捕获现场。
shell
# ./data/core-parser -p `pidof zygote64`
bash
core-parser> sysroot /apex
...
Mmap segment [7601e3a000, 7601e91000) /apex/com.android.runtime/lib64/bionic/libc.so [0]
Mmap segment [7601e92000, 7601f2c000) /apex/com.android.runtime/lib64/bionic/libc.so [58000]
Read symbols[2711] (/apex/com.android.runtime/lib64/bionic/libc.so)
...
yaml
core-parser> disas memset_resolver
LIB: /apex/com.android.runtime/lib64/bionic/libc.so
memset_resolver: [7601f22d70, 7601f22db4]
0x7601f22d70: 39405428 | ldrb w8, [x1, #0x15]
0x7601f22d74: 371801a8 | tbnz w8, #3, 0x7601f22da8
0x7601f22d78: 39402428 | ldrb w8, [x1, #9]
0x7601f22d7c: d503201f | nop
0x7601f22d80: 10c1d800 | adr x0, 0x7601ea6880
0x7601f22d84: 36180108 | tbz w8, #3, 0x7601f22da4
0x7601f22d88: d5380008 | mrs x8, MIDR_EL1
0x7601f22d8c: 52b5de09 | mov w9, #-0x51100000
0x7601f22d90: 0b090108 | add w8, w8, w9
0x7601f22d94: d503201f | nop
0x7601f22d98: 10d8b949 | adr x9, 0x7601ed44c0
0x7601f22d9c: 7154011f | cmp w8, #0x500, lsl #12
0x7601f22da0: 9a803120 | csel x0, x9, x0, lo
0x7601f22da4: d65f03c0 | ret
0x7601f22da8: d503201f | nop
0x7601f22dac: 10c1e0a0 | adr x0, 0x7601ea69c0
0x7601f22db0: d65f03c0 | ret
yaml
core-parser> disas 0x7601ea69c0
LIB: /apex/com.android.runtime/lib64/bionic/libc.so
__memset_aarch64_mops: [7601ea69c0, 7601ea69d8]
0x7601ea69c0: d503245f | bti c
0x7601ea69c4: aa0003e3 | mov x3, x0
0x7601ea69c8: 19c10443 | setp [x3]!, x2!, x1
0x7601ea69cc: 19c14443 | setm [x3]!, x2!, x1
0x7601ea69d0: 19c18443 | sete [x3]!, x2!, x1
0x7601ea69d4: d65f03c0 | ret
可以看到地址 0x7601ea69c0 即是 __memset_aarch64_mops 函数,那我们可在 0x7601f22dac 拦截。
shell
core-parser> remote wd 0x7601f22dac -v d65f03c014000000
当启动其它应用,存在某应用在 0x7601f22dac 处暂停,那么该应用存在链接异常。
离线分析
截获到存在异常的进程号,抓取的该进程的现场 DUMP 来分析具体原因,包名已打码 a.b.c.d。
bash
./data/core-parser -p <PID>
yaml
core-parser> bt
"main" sysTid=32523 Native
| group="main" daemon=0 prio=5 target=0x0 uncaught_exception=0x0
| tid=1 sCount=0 flags=0 obj=0x72aa15d8 self=0xb4000073b08e5380 env=0xb4000074208d9510
| stack=0x7fcfa7b000-0x7fcfa7d000 stackSize=0x7ff000 handle=0x76462c6098
| mutexes=0xb4000073b08e5b20 held=
x0 0x0000000000000000 x1 0x00000072dfc4e418 x2 0x0000000000000000 x3 0x005f5a0000000000
x4 0x0000000000800000 x5 0x3f2d000000000000 x6 0x0000000000802d3f x7 0x534b4e5a5f007465
x8 0x000000000000006f x9 0x0000007601e3a000 x10 0x00000000000e8d70 x11 0x0000000000096c9f
x12 0x000000764500e0f0 x13 0x0000000040000541 x14 0x000000764611d290 x15 0x0000000000000000
x16 0x00000072daca0b48 x17 0x0000007601ea6ec0 x18 0xb4000073b09262b0 x19 0x000000000000050c
x20 0x00000072dfc5fdd0 x21 0x0000007fd026d620 x22 0x00000072dfc4e414 x23 0x00000000000000f6
x24 0x00000072dfc28000 x25 0x0000007fd026d640 x26 0x00000072dacb81f0 x27 0x0000050c00000402
x28 0x0000000000123540 fp 0x0000007fd026d610
lr 0x00000072dac2134c sp 0x0000007fd026d5a0 pc 0x0000007601f22db0 pst 0x0000000060001000
Native: #00 0000007601f22db0 memset_resolver+0x40
Native: #01 00000072dac21348 /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijm_linker.so+0x13348
Native: #02 00000072dac21074 /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijm_linker.so+0x13074
Native: #03 00000072dac72994 /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijm_linker.so+0x64994
Native: #04 00000072dac2093c /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijm_linker.so+0x1293c
Native: #05 00000072dac726b0 /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijm_linker.so+0x646b0
Native: #06 00000072dac202cc /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijm_linker.so+0x122cc
Native: #07 00000072dac720b4 /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijm_linker.so+0x640b4
Native: #08 00000072dac20220 /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijm_linker.so+0x12220
Native: #09 00000072dac7232c /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijm_linker.so+0x6432c
Native: #10 00000072dac20368 ijm_linker_init(unsigned char*, t_raep_info_def**, unsigned long*, int*)+0x80
Native: #11 00000072e7249400 /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijmdetect-drisk.so+0x5400
Native: #12 000000764617be00 __dl__ZN6soinfo17call_constructorsEv+0x280
Native: #13 000000764617afb0 __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv+0x550
Native: #14 00000072efa609ec /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libexec.so+0x5f9ec
Native: #15 000000764617a9b8 __dl__ZL10dlopen_extPKciPK17android_dlextinfoPKv.__uniq.234527301065430621646263515731762262959.llvm.6398802490342409884+0x48
Native: #16 00000072ecb098bc /data/data/a.b.c.d/files/libijmDataEncryption.so+0x4e8bc
Native: #17 00000076381f1110 android_dlopen_ext+0x10
Native: #18 00000076441da29c android::NativeLoaderNamespace::Load(char const*) const+0x8c
Native: #19 00000076441c88b4 OpenNativeLibrary+0x394
Native: #20 000000736a5d7d64 art::JavaVMExt::LoadNativeLibrary(_JNIEnv*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, _jobject*, _jclass*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)+0x2d4
Native: #21 00000073538dc480 JVM_NativeLoad+0x170
Native: #22 00000000717ddec8 /system/framework/arm64/boot.oat+0x361ec8
Native: #23 0000007369e9e1d6
JavaKt: #00 0000000000000000 java.lang.Runtime.nativeLoad
JavaKt: #01 0000007369b0c500 java.lang.Runtime.loadLibrary0
JavaKt: #02 0000007369b0c48c java.lang.Runtime.loadLibrary0
JavaKt: #03 0000007369b17b24 java.lang.System.loadLibrary
JavaKt: #04 00000072e61bde14 com.ijm.detect.drisk.DRiskNativeTool.setLicenseKey
JavaKt: #05 00000072e607412c a.b.c.d.base.BaseApplication.initIjmSdk
JavaKt: #06 00000072e6074052 a.b.c.d.base.BaseApplication.initComponent
JavaKt: #07 00000072e60742b0 a.b.c.d.base.BaseApplication.initSdk
JavaKt: #08 00000072e6074512 a.b.c.d.base.BaseApplication.onCreate
JavaKt: #09 0000007369237ea8 android.app.Instrumentation.callApplicationOnCreate
JavaKt: #10 000000736919eb80 android.app.ActivityThread.handleBindApplication
JavaKt: #11 000000736919af0c android.app.ActivityThread.-$$Nest$mhandleBindApplication
JavaKt: #12 0000007369195d40 android.app.ActivityThread$H.handleMessage
JavaKt: #13 000000736805fb2e android.os.Handler.dispatchMessage
JavaKt: #14 0000007368088d5e android.os.Looper.loopOnce
JavaKt: #15 0000007368089680 android.os.Looper.loop
JavaKt: #16 00000073691a35fc android.app.ActivityThread.main
JavaKt: #17 0000000000000000 java.lang.reflect.Method.invoke
JavaKt: #18 0000007366e7a2e2 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run
JavaKt: #19 0000007366e7f0dc com.android.internal.os.ZygoteInit.main
yaml
core-parser> f 0 -n
Native: #00 0000007601f22db0 memset_resolver+0x40
{
library: /apex/com.android.runtime/lib64/bionic/libc.so
symbol: memset_resolver
frame_fp: 0x7fd026d610
frame_pc: 0x7601f22db0
ASM CODE:
0x7601f22d8c: 52b5de09 | mov w9, #-0x51100000
0x7601f22d90: 0b090108 | add w8, w8, w9
0x7601f22d94: d503201f | nop
0x7601f22d98: 10d8b949 | adr x9, 0x7601ed44c0
0x7601f22d9c: 7154011f | cmp w8, #0x500, lsl #12
0x7601f22da0: 9a803120 | csel x0, x9, x0, lo
0x7601f22da4: d65f03c0 | ret
0x7601f22da8: d503201f | nop
0x7601f22dac: 10c1e0a0 | adr x0, 0x7601ea69c0
0x7601f22db0: d65f03c0 | ret
}
可以看到当前已经在 0x7601f22dac 暂停了。




我们需要看 memset_resolver 函数的参数存在两个,分别是 hwcap 与 __ifunc_arg_t*。
bash
core-parser> auxv
21 AT_SYSINFO_EHDR 0x7646168000
33 AT_MINSIGSTKSZ 0x1270
10 AT_HWCAP 0xefffffff
6 AT_PAGESZ 0x1000
11 AT_CLKTCK 0x64
3 AT_PHDR 0x5b68148040
4 AT_PHENT 0x38
5 AT_PHNUM 0xc
7 AT_BASE 0x764611d000
8 AT_FLAGS 0x0
9 AT_ENTRY 0x5b6814c000
b AT_UID 0x0
c AT_EUID 0x0
d AT_GID 0x0
e AT_EGID 0x0
17 AT_SECURE 0x1
19 AT_RANDOM 0x7fd0278438
1a AT_HWCAP2 0x801af3ff
1f AT_EXECFN 0x7fd0279fe0 /system/bin/app_process
f AT_PLATFORM 0x7fd0278448 aarch64
0 AT_NULL 0x0
当前设备的 HWCAP: 0xefffffff,HWCAP2:0x801af3ff,是不支持 MOPS 特性的,而发生错误的程序传入的参数寄存器 x0、x1 均是错误的。
makefile
core-parser> rd 0x00000072dfc4e418 -e 0x00000072dfc4e518
72dfc4e418: 534b4e5a5f007465 316b646e5f5f3674 et._ZNKSt6__ndk1
72dfc4e428: 5f726f7272653031 73656d3765646f63 10error_code7mes
72dfc4e438: 5f00764565676173 5f5f3674534b4e5a sageEv._ZNKSt6__
72dfc4e448: 72653531316b646e 646e6f635f726f72 ndk115error_cond
72dfc4e458: 656d376e6f697469 0076456567617373 ition7messageEv.
72dfc4e468: 72333174534e5a5f 655f656d69746e75 _ZNSt13runtime_e
72dfc4e478: 76453244726f7272 5f3674534e5a5f00 rrorD2Ev._ZNSt6_
72dfc4e488: 733231316b646e5f 72655f6d65747379 _ndk112system_er
72dfc4e498: 6e695f5f36726f72 5f534e4b52457469 ror6__initERKNS_
72dfc4e4a8: 5f726f7272653031 5f534e4565646f63 10error_codeENS_
72dfc4e4b8: 5f63697361623231 6349676e69727473 12basic_stringIc
72dfc4e4c8: 61686331315f534e 7374696172745f72 NS_11char_traits
72dfc4e4d8: 395f534e45456349 6f7461636f6c6c61 IcEENS_9allocato
72dfc4e4e8: 0045454545634972 5f5f3674534e5a5f rIcEEEE._ZNSt6__
72dfc4e4f8: 79733231316b646e 7272655f6d657473 ndk112system_err
72dfc4e508: 5f534e453143726f 5f726f7272653031 orC1ENS_10error_
core-parser>
于是从错误地址 x1 处读同样的 OFFSET 得到的 BIT 位刚好为 1,因此错误的选择了 MOPS 函数。
如何适配
发生该类问题的应用,基本都是自实现加载链接过程的黑科技。
yaml
core-parser> f 1 -n
Native: #01 00000072dac21348 /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijm_linker.so+0x13348
{
library: /data/app/~~U52GnH6ZG5EzXPS_ZD0ZGQ==/a.b.c.d-CA0NfNRECbGv1DUkeH9ecg==/lib/arm64/libijm_linker.so
symbol:
frame_fp: 0x7fd026d610
frame_pc: 0x72dac21348
ASM CODE:
0x72dac21328: 7100191f | cmp w8, #6
0x72dac2132c: 54000440 | b.eq 0x72dac213b4
0x72dac21330: f85f8329 | ldur x9, [x25, #-8]
0x72dac21334: f940040a | ldr x10, [x0, #8]
0x72dac21338: 7100291f | cmp w8, #0xa
0x72dac2133c: f940d929 | ldr x9, [x9, #0x1b0]
0x72dac21340: 8b0a0128 | add x8, x9, x10
0x72dac21344: 54000061 | b.ne 0x72dac21350
0x72dac21348: d63f0100 | blr x8
}
core-parser>

从汇编以及伪代码可以看到,调用 memset_resolver 函数并为其传递参数,因此使用的之前程序运行遗留下来的 x1 寄存器,只能说运气真好,带参的 resolver 从 API30 开始已经加入了,之前自实现老的无参数版本,使用了错误参数 x1 来选择。
标题 | 地址 |
---|---|
Adopt GNU calling convention for ifunc resolvers. | android-review.googlesource.com/c/platform/... |

最后
期望更多开发者能做好 Android16 的适配,转达不易,感谢相互告知!