关于 Android16 MOPS 函数指令非法问题

背景

我们最近收到多个应用在 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 的适配,转达不易,感谢相互告知!

相关推荐
猿小蔡-Cool1 小时前
Android ADB命令之内存统计与分析
android·adb
Monkey-旭1 小时前
Android Handler 完全指南
android·java·handler
從南走到北2 小时前
JAVA东郊到家按摩服务同款同城家政服务按摩私教茶艺师服务系统小程序+公众号+APP+H5
android·java·开发语言·微信小程序·小程序
alexhilton3 小时前
学会用最优雅的姿式在Compose中显示富文本
android·kotlin·android jetpack
阿华的代码王国5 小时前
【Android】卡片式布局 && 滚动容器ScrollView
android·xml·java·前端·后端·卡片布局·滚动容器
风起云涌~5 小时前
【Android】桌面小组件开发
android·gitee
双鱼大猫7 小时前
从传统播放器到AI智能体:Xplayer 2.0的技术革新之路
android
CYRUS_STUDIO7 小时前
动态篡改 so 函数返回值:一篇带你玩转 Android Hook 技术!
android·c++·逆向
xzkyd outpaper8 小时前
Android中主线程、ActivityThread、ApplicationThread的区别
android·面试
就叫飞六吧9 小时前
mysql全量备份、全量恢复demo
android·mysql·adb