上星期接到一个CQ,问题是这样的:下载官方的爱奇艺或者搜狐视频apk在板子上安装之后,无法正常播放在线视频,点击视频播放之后总是弹出对话框“XXX has stopped”,或者整个视频APP闪退。
于是我首先下载了一个爱奇艺的apk开始复现这个问题,然后从log进行分析,在kernel log发现线索:
[ 392.674685] android: (01-02 01:17:32) process pid: 3352, tid: 4326 crash![ 392.682842] c0 4326 (com.qiyi.video) com.qiyi.video[4326]: undefined instruction: pc=000000007805022c
[ 392.692649] c0 4326 (com.qiyi.video) Code: 0a000073 e3130003 1afffff9 e59f22bc (f1010200)
可以看到这是一个undefined instruction问题,而且pc在user space,于是进一步查看logcat记录,user space的backtrace如下:
I/DEBUG ( 170): #00 pc 0008922c /data/app-lib/com.qiyi.video-1/libffmpeg_pps.so (ff_h264_find_start_code_candidate_armv6+52)
I/DEBUG ( 170): #01 pc 0014f52c /data/app-lib/com.qiyi.video-1/libffmpeg_pps.so
从中可以看出问题的根源在于动态库libffmpeg_pps.so,而且根据后面的函数名,猜测与指令集兼容有关(测试平台是ARMv8的),所以反汇编libffmpeg_pps.so查看。
命令:aarch64-linux-gnu-objdump -d libffmpeg_pps.so
然后查看地址偏移f1010200
89220: e3130003 tst r3, #3
89224: 1afffff9 bne 89210 <ff_h264_find_start_code_candidate_armv6+0x18>
89228: e59f22bc ldr r2, [pc, #700] ; 894ec <ff_h264_find_start_code_candidate_armv6+0x2f4>
8922c: f1010200 setend be
89230: e313000c tst r3, #12
89234: 0a000007 beq 89258 <ff_h264_find_start_code_candidate_armv6+0x60>
89238: e4934004 ldr r4, [r3], #4
可以看出是在执行指令setend时出错的,查看ARMv8的spec发现,这个指令已经被desperate了, libffmpeg已经有patch修复这个问题,如下:
commit 79fce1ec8abd017593c003917fc123f7119a78d6
Author: Martin Storsjö <[email protected]>
Date: Fri Jul 4 18:21:50 2014 +0300
arm: Avoid using the ‘setend‘ instruction on ARMv7 and newer
This instruction is deprecated on ARMv8, and it is serializing on
some ARMv7 cores as well [1].
[1] http://article.gmane.org/gmane.linux.ports.arm.kernel/339293
CC: [email protected]
Signed-off-by: Martin Storsjö <[email protected]>
diff --git a/libavcodec/arm/h264dsp_init_arm.c b/libavcodec/arm/h264dsp_init_arm.c
index 92658e7..f9712d8 100644
--- a/libavcodec/arm/h264dsp_init_arm.c
+++ b/libavcodec/arm/h264dsp_init_arm.c
@@ -104,8 +104,12 @@ av_cold void ff_h264dsp_init_arm(H264DSPContext *c, const int bit_depth,
{
int cpu_flags = av_get_cpu_flags();
- if (have_armv6(cpu_flags))
+ if (have_armv6(cpu_flags) && !(have_vfpv3(cpu_flags) || have_neon(cpu_flags))) {
+ // This function uses the ‘setend‘ instruction which is deprecated
+ // on ARMv8. This instruction is serializing on some ARMv7 cores as
+ // well. Therefore, only use the function on ARMv6.
c->h264_find_start_code_candidate = ff_h264_find_start_code_candidate_armv6;
+ }
if (have_neon(cpu_flags))
h264dsp_init_neon(c, bit_depth, chroma_format_idc);
}
但是现在市面上大多数的视频播放apk采用的还是老的libffmpeg,而且搜狐视频app使用的是libtea_codecs.so(这个动态库中也包括setend这个指令导致出错),所以我们最终还是决定在kernel层解决这个问题,查看ARMv8的spec,发现有方法可以enable 这个指令,相关寄存器描述如下:
SCTLR_EL1.{SED, ITD, THEE, CP15BEN}
These bits control AArch32 functionality that is deprecated, or OPTIONAL and deprecated:
SED Disables use of the SETEND instruction.
ITD Disables use of the IT instruction.
THEE Enables use of the Thumb-EE instruction set.
CP15BEN Enables use of the CP15 DMB, DSB, and ISB barrier operations.
fix的patch如下,作用也很简单,就是在系统初始化的是clear一下上面提到的SCTLR_EL1寄存器中的SED位
Date: Wed Nov 12 16:40:07 2014 +0800
arm64: mm: Clear bit SED in sctlr_el1 to support SETEND instruction.
SETEND instruction is deprecated on ARMv8, we need to clear bit SED in
sctlr_el1 to make SETEND instruction available explicitly.
This patch is used to solve the issue that some video app would fail to
playback video.
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 7736779..39a032e 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -238,5 +238,5 @@ ENDPROC(__cpu_setup)
*/
.type crval, #object
crval:
- .word 0x000802e2 // clear
- .word 0x0405d11d // set
+ .word 0x000803e2 // clear
+ .word 0x0405d01d // set
至于为什么改变这两个值就能到达修改sctlr_el1的目的,就牵涉到kernel的启动流程,以后再写文章分析吧。