2011年12月16日

Android 2.3と4.0で0除算のふるまいの違い

Android 2.3までは0で割り算しても無視されていました。4.0では普通のARM Linuxと同様にSIGFPEのシグナルを発行するようになりました。どこでこの違いがあらわれるかやっと突き止めたのでここにメモしておきます。



android2.3/hardware/libhardware/modules/gralloc/framebuffer.cpp

    if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
        return -errno;

    int refreshRate = 1000000000000000LLU /
    (
            uint64_t( info.upper_margin + info.lower_margin + info.yres )
            * ( info.left_margin  + info.right_margin + info.xres )
            * info.pixclock
    );

    if (refreshRate == 0) {
        // bleagh, bad info from the driver
        refreshRate = 60*1000;  // 60 Hz
    }

info.pixclockは0だったので、refreshRateを計算する時に0で除算していました。しかし正常に動作していました。これを元にカスタマイズしたものをAndroid 4.0で動かすと、SIGFPEのシグナルが発生してこのプロセスは強制終了させられてしましました。(KZM-A9-DualボードでAndroid 4.0(Ice Cream Sandwich)を動かす)

同じところがAndroid4.0では以下のように修正されて0で除算しないようになっていました。

android4.0/hardware/libhardware/modules/gralloc/framebuffer.cpp

    if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
        return -errno;

    uint64_t  refreshQuotient =
    (
            uint64_t( info.upper_margin + info.lower_margin + info.yres )
            * ( info.left_margin  + info.right_margin + info.xres )
            * info.pixclock
    );

    /* Beware, info.pixclock might be 0 under emulation, so avoid a
     * division-by-0 here (SIGFPE on ARM) */
    int refreshRate = refreshQuotient > 0 ? (int)(1000000000000000LLU / refreshQuotient) : 0;

    if (refreshRate == 0) {
        // bleagh, bad info from the driver
        refreshRate = 60*1000;  // 60 Hz
    }

intの除算はgccのランタイムライブラリ libgccの中の__aeabi_uidivで行われます。(Androidで使われるARM926, ARM1136, Cortex-A8, Cortex-A9などは整数の割り算命令がありません。)

Androidではlibgccに含まれる関数群は/system/bin/linkerに含まれていることを発見しました。ただしシンボルに__dl_のprefixがつけられていました。(bionic/linker/Android.mk参照。) もちろん__aeabi_uidivもあります。そこでの0除算のハンドラは

$ arm-eabi-objdump -d android-2.3/out/target/product/kzm9d/symbols/system/bin/linker |less

b00016d4 <__dl___aeabi_idiv0>:
b00016d4:       e12fff1e        bx      lr

$ arm-eabi-objdump -d android-4.0/out/target/product/generic/symbols/system/bin/linker |less

b0001b30 <__dl___div0>:
b0001b30:       e92d4002        push    {r1, lr}
b0001b34:       e3a00008        mov     r0, #8  ; 0x8
b0001b38:       eb0013dd        bl      b0006ab4 <__dl_raise+0x18>
b0001b3c:       e8bd8002        pop     {r1, pc}

__aeabi_idiv0と__div0はaliasになっているので同じところを指しています。

2.3のほうは何もせずにすぐリターンしていて、4.0ではraiseを呼んでシグナルを発行しています。

その元になっているlibgccを見つけました。

$arm-eabi-objdump -d android-4.0/prebuilt/linux-x86/toolchain/arm-linux-androideabi-4.4.x/lib/gcc/arm-linux-androideabi/4.4.3/armv7/libgcc.a |less

Disassembly of section .text:

00000000 <__div0>:
   0:   e92d4002        push    {r1, lr}
   4:   e3a00008        mov     r0, #8  ; 0x8
   8:   ebfffffe        bl      0 <raise>
   c:   e8bd8002        pop     {r1, pc}

わかりました。Android 2.3と4.0では使用しているtoolchainが異なります。そのためにlibgcc.aの内容が違っていました。

Android 2.3でのtoolchainの選択

ANDROID_EABI_TOOLCHAIN=$prebuiltdir/toolchain/arm-eabi-4.4.3/bin

Android 4.0でのtoolchainの選択

ANDROID_EABI_TOOLCHAIN=$prebuiltdir/toolchain/arm-linux-androideabi-4.4.x/bin

さらに深堀りして、gccのソースでこの部分を見つけました。

gcc-4.4.3/gcc/config/arm/lib1funcs.asm

#ifdef L_dvmd_tls

        FUNC_START div0
        FUNC_ALIAS aeabi_idiv0 div0
        FUNC_ALIAS aeabi_ldiv0 div0

        RET

        FUNC_END aeabi_ldiv0
        FUNC_END aeabi_idiv0
        FUNC_END div0
        
#endif /* L_divmodsi_tools */
/* ------------------------------------------------------------------------ */

#ifdef L_dvmd_lnx
@ GNU/Linux division-by zero handler.  Used in place of L_dvmd_tls

/* Constant taken from <asm/signal.h>.  */
#define SIGFPE  8

        ARM_FUNC_START div0

        do_push {r1, lr}
        mov     r0, #SIGFPE
        bl      SYM(raise) __PLT__
        RETLDM  r1

        FUNC_END div0
        
#endif /* L_dvmd_lnx */

2012.4.14追記

ここで書かれていことはAndroidのシステムでの話です。NDKを使って作成したダイナミックリンクライブラリではgccのランタイムライブラリ(libgcc.a)はスタティックリンクされるようになっていました。つまり動作させる環境がAndroid2.3でも4.0でも0除算のふるまいは同じです。



トラックバックURL

コメントする

名前
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード