无法写入ARM寄存器R4:功能还是bug?

时间:2011-10-14 14:05:00

标签: android android-ndk arm assembly cortex-a8

我最近在使用Assembly编程时遇到了ARM Cortex-A8的奇怪行为。每当我MOV进入R4时,我的程序崩溃(下面的堆栈转储)

10-14 09:48:43.117: INFO/DEBUG(3048): Build fingerprint: 'google/soju/crespo:2.3.6/GRK39F/189904:user/release-keys'
10-14 09:48:43.121: INFO/DEBUG(3048): pid: 7082, tid: 7082  >>> neontests <<<
10-14 09:48:43.121: INFO/DEBUG(3048): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000001
10-14 09:48:43.125: INFO/DEBUG(3048):  r0 00000001  r1 afa025b6  r2 00000000  r3 bec77051
10-14 09:48:43.128: INFO/DEBUG(3048):  r4 00000001  r5 bec7704c  r6 00000001  r7 00000004
10-14 09:48:43.128: INFO/DEBUG(3048):  r8 00000005  r9 00000000  10 4214cca4  fp 800a5368
10-14 09:48:43.128: INFO/DEBUG(3048):  ip afa03110  sp bec77010  lr afa0133b  pc afd37b42  cpsr 60000030
10-14 09:48:43.132: INFO/DEBUG(3048):  d0  0000000200000053  d1  0000000400000074
10-14 09:48:43.132: INFO/DEBUG(3048):  d2  000000060000006f  d3  0000000800000070
10-14 09:48:43.132: INFO/DEBUG(3048):  d4  006f0065006e002e  d5  007300650074006e
10-14 09:48:43.136: INFO/DEBUG(3048):  d6  0000000c00000005  d7  0000002000000015
10-14 09:48:43.136: INFO/DEBUG(3048):  d8  0000000c00000005  d9  0000002000000015
10-14 09:48:43.140: INFO/DEBUG(3048):  d10 0000000000000000  d11 0000000000000000
10-14 09:48:43.140: INFO/DEBUG(3048):  d12 0000000000000000  d13 0000000000000000
10-14 09:48:43.140: INFO/DEBUG(3048):  d14 0000000000000000  d15 0000000000000000
10-14 09:48:43.144: INFO/DEBUG(3048):  d16 800220e8401644a8  d17 bff0000000000000
10-14 09:48:43.144: INFO/DEBUG(3048):  d18 3ff0000000000000  d19 0000000000000000
10-14 09:48:43.148: INFO/DEBUG(3048):  d20 0000000000000000  d21 0000000000000000
10-14 09:48:43.148: INFO/DEBUG(3048):  d22 3ff0000000000000  d23 0000000000000000
10-14 09:48:43.148: INFO/DEBUG(3048):  d24 3ff0000000000000  d25 0000000000000000
10-14 09:48:43.148: INFO/DEBUG(3048):  d26 0000000000000000  d27 0000000000000000
10-14 09:48:43.148: INFO/DEBUG(3048):  d28 0000000000000000  d29 0000000000000000
10-14 09:48:43.148: INFO/DEBUG(3048):  d30 0000000000000000  d31 0000000000000000
10-14 09:48:43.148: INFO/DEBUG(3048):  scr 20000012
10-14 09:48:43.195: INFO/DEBUG(3048):          #00  pc 00037b42  /system/lib/libc.so
10-14 09:48:43.195: INFO/DEBUG(3048):          #01  pc 00001338  /system/lib/liblog.so
10-14 09:48:43.199: INFO/DEBUG(3048):          #02  pc 00001482  /system/lib/liblog.so
10-14 09:48:43.199: INFO/DEBUG(3048):          #03  pc 00000c54  /data/data/neontests/lib/libneon_tests.so
10-14 09:48:43.199: INFO/DEBUG(3048):          #04  pc 00017e34  /system/lib/libdvm.so
10-14 09:48:43.199: INFO/DEBUG(3048):          #05  pc 0004968c  /system/lib/libdvm.so
10-14 09:48:43.199: INFO/DEBUG(3048):          #06  pc 0004ee62  /system/lib/libdvm.so
10-14 09:48:43.199: INFO/DEBUG(3048):          #07  pc 0001d034  /system/lib/libdvm.so
10-14 09:48:43.199: INFO/DEBUG(3048):          #08  pc 000220e4  /system/lib/libdvm.so
10-14 09:48:43.199: INFO/DEBUG(3048):          #09  pc 00020fdc  /system/lib/libdvm.so
10-14 09:48:43.199: INFO/DEBUG(3048):          #10  pc 0005fdde  /system/lib/libdvm.so
10-14 09:48:43.203: INFO/DEBUG(3048):          #11  pc 00067b52  /system/lib/libdvm.so
10-14 09:48:43.203: INFO/DEBUG(3048):          #12  pc 0001d034  /system/lib/libdvm.so
10-14 09:48:43.203: INFO/DEBUG(3048):          #13  pc 000220e4  /system/lib/libdvm.so
10-14 09:48:43.203: INFO/DEBUG(3048):          #14  pc 00020fdc  /system/lib/libdvm.so
10-14 09:48:43.203: INFO/DEBUG(3048):          #15  pc 0005fc40  /system/lib/libdvm.so
10-14 09:48:43.203: INFO/DEBUG(3048):          #16  pc 0004c126  /system/lib/libdvm.so
10-14 09:48:43.203: INFO/DEBUG(3048):          #17  pc 00032572  /system/lib/libandroid_runtime.so
10-14 09:48:43.203: INFO/DEBUG(3048):          #18  pc 0003341e  /system/lib/libandroid_runtime.so
10-14 09:48:43.203: INFO/DEBUG(3048):          #19  pc 00008cca  /system/bin/app_process
10-14 09:48:43.207: INFO/DEBUG(3048):          #20  pc 00014b52  /system/lib/libc.so
10-14 09:48:43.207: INFO/DEBUG(3048): code around pc:
10-14 09:48:43.207: INFO/DEBUG(3048): afd37b20 18801889 c003f810 c003f801 d2f93b01 
10-14 09:48:43.207: INFO/DEBUG(3048): afd37b30 bf00bdf0 2200b510 3201e003 4618b90b 
10-14 09:48:43.207: INFO/DEBUG(3048): afd37b40 5c83e004 42a35c8c 1b18d0f7 bf00bd10 
10-14 09:48:43.207: INFO/DEBUG(3048): afd37b50 b152b530 5cc42300 42ac5ccd 1b60d001 
10-14 09:48:43.207: INFO/DEBUG(3048): afd37b60 b114e004 429a3301 2000d1f5 bf00bd30 
10-14 09:48:43.207: INFO/DEBUG(3048): code around lr:
10-14 09:48:43.207: INFO/DEBUG(3048): afa01318 fffffff4 00001e20 b088b570 4615460c 
10-14 09:48:43.207: INFO/DEBUG(3048): afa01328 b9099001 447c4c28 46204928 f7ff4479 
10-14 09:48:43.207: INFO/DEBUG(3048): afa01338 2800edc4 4926d02e 22034620 f7ff4479 
10-14 09:48:43.207: INFO/DEBUG(3048): afa01348 b338edc2 46204923 f7ff4479 b308edb6 
10-14 09:48:43.207: INFO/DEBUG(3048): afa01358 46204921 f7ff4479 b1d8edb0 4620491f 
10-14 09:48:43.207: INFO/DEBUG(3048): stack:
10-14 09:48:43.207: INFO/DEBUG(3048):     bec76fd0  800a5368  
10-14 09:48:43.207: INFO/DEBUG(3048):     bec76fd4  afd1c701  /system/lib/libc.so
10-14 09:48:43.207: INFO/DEBUG(3048):     bec76fd8  bec771f0  
10-14 09:48:43.207: INFO/DEBUG(3048):     bec76fdc  bec77051  
10-14 09:48:43.207: INFO/DEBUG(3048):     bec76fe0  0000ce60  
10-14 09:48:43.207: INFO/DEBUG(3048):     bec76fe4  000003fa  
10-14 09:48:43.207: INFO/DEBUG(3048):     bec76fe8  ffff0208  
10-14 09:48:43.207: INFO/DEBUG(3048):     bec76fec  bec7704c  
10-14 09:48:43.207: INFO/DEBUG(3048):     bec76ff0  000003ff  
10-14 09:48:43.207: INFO/DEBUG(3048):     bec76ff4  00000000  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec76ff8  00000003  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec76ffc  00000004  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77000  80400d90  /data/data/neontests/lib/libneon_tests.so
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77004  bec7704c  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77008  df002777  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec7700c  e3a070ad  
10-14 09:48:43.210: INFO/DEBUG(3048): #00 bec77010  00000001  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77014  afa0133b  /system/lib/liblog.so
10-14 09:48:43.210: INFO/DEBUG(3048): #01 bec77018  80400420  /data/data/neontests/lib/libneon_tests.so
10-14 09:48:43.210: INFO/DEBUG(3048):     bec7701c  00000004  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77020  bec7701c  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77024  00000001  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77028  80400d90  /data/data/neontests/lib/libneon_tests.so
10-14 09:48:43.210: INFO/DEBUG(3048):     bec7702c  00000014  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77030  00000000  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77034  00000000  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77038  bec7704c  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec7703c  afd4d5c8  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77040  00000001  
10-14 09:48:43.210: INFO/DEBUG(3048):     bec77044  afa01487  /system/lib/liblog.so

编辑:上面的堆栈转储是以下代码的结果(道歉,GNU Assembly突出显示在这里似乎有点奇怪):

.arm
.global asm_test

asm_test:

    mov r0, #4 @make sure r0 is not the same as r4   
    mov r4, #1 @move to r4 something different from r0

    mov pc, lr @return from function

我从(本机)C调用它如下:

#include <jni.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <arm_neon.h>
#include <android/log.h>
#include "com_something_neontests_NativeLib.h"

extern volatile int asm_test(void);

JNIEXPORT jint JNICALL Java_com_something_neontests_NativeLib_asmTry
  (JNIEnv * env, jobject obj)
{

    __android_log_print(ANDROID_LOG_INFO, "com.something.neontests", "Start!");

    asm_test();

    __android_log_print(ANDROID_LOG_INFO, "com.something.neontests", "Done!");


    return 0;
}

以下是我注意到的一些事情。首先,每当我向R4分配任何内容时,无论是MOV R4, #2还是ADD R4, R0, R1,结果 会在程序崩溃之前在R4中结束,但同样的结果也总是会结束在R0。我还发现我可以POP从堆栈中填充到R4中。没有其他寄存器表现出同样的行为。汇编代码使用Android NDK编译,我相信使用GCC 4.4.3。我在几部Android手机上测试过它,一切似乎都是一致的。

我知道所有寄存器都被分段,使得R0-R3接收参数,R4-R12是可变寄存器,然后有特殊寄存器等等。也许这种行为是由某种我从未听说过的C调用约定引起的?是否有预期的解释?

干杯! =)

更新

正如@Graham所指出的那样,r4(或者v1)是一个应该保留的变量寄存器。但是,在他的回答中提供的link中,ARM文档本身使用v1寄存器,首先将其结果与另一个保留寄存器的值一起保存在堆栈中:

STMDB sp!,{v1,lr}
LDR v1,[a2,#0]

以后检索他们的值。当我编译这段代码时,它崩溃的方式与我原来的相同,但是

STMDB sp!,{v1,lr}
LDR v2,[a2,#0]

没有(注意v2而不是v1)。

2 个答案:

答案 0 :(得分:8)

我们要解释的是,如果要在函数中使用r4,则需要执行此操作:

.globl asm_test
asm_test:
    stmdb r13!,{r4}
    mov r0, #4 @make sure r0 is not the same as r4
    mov r4, #1 @move to r4 something different from r0
    ldmia r13!,{r4}
    mov pc, lr @return from function

否则你会留下一颗定时炸弹在路上的某个地方熄灭。编译器已经为更高级别的函数分配了r4,并且根据规则,没有人可以更改该寄存器,以便更高级别的调用不必保护r4,通过在正确的时间搞乱它并将您创建问题,如何问题的行为是依赖于代码的。并且将解释为什么其他寄存器在这种情况下不敏感。有时,当你这样做时,你实际上不会崩溃,有时可能是字符串打印错误或循环重复或提前退出。

要查看发生了什么,请反汇编有问题的功能(不是源代码,而是反汇编)。加上调用它的函数和调用它的函数,直到r4出现在其中一个周围的函数中。检查r4的用途。

如果你的asm_test()调用函数要在asm_test()调用之前和之后使用局部变量,以便优化器将它们保存在寄存器中,那么你也可以更改行为,还可以改变优化器不会一起删除所有代码:

void fun ( void )
{
  int r;
  r=10;
  asm_test();
  r++;
}

优化器会在上面的代码中完全删除r,但是:

int fun ( int a, int b, int c, int d )
{
   int e;
   e=a+b+c+d;
   b=asm_test(a+d);
   e+=b; 
   return(e);
}

创建的内容足以强制编译器构建堆栈帧。

00000000 <fun>:
   0:   e0811000    add r1, r1, r0
   4:   e92d4010    push    {r4, lr}
   8:   e0830000    add r0, r3, r0
   c:   e0814002    add r4, r1, r2
  10:   e0844003    add r4, r4, r3
  14:   ebfffffe    bl  0 <asm_test>
  18:   e0840000    add r0, r4, r0
  1c:   e8bd8010    pop {r4, pc}

r4是这种情况下的变量e(围绕asm_test调用),通过搞乱r4,你将改变函数fun()返回的内容。如果这个值从未用于调用乐趣,例如你的r4修改就会被忽视。

编译器遵循调用约定规则并期望所有被调用者也是如此,如果你搞砸了它可以崩溃/失败的方式从没有效果到非常严重,所以你需要符合你的那些调用约定ASM。

答案 1 :(得分:7)

根据APCS, R4 是您必须保留的寄存器之一。如果您需要使用它,请在进入时将其存储在堆栈中,然后在退出时再将其弹出。有一些寄存器,例如 R0-R3 ,它们是临时寄存器;你可以在不保留它们的情况下腐蚀你的日常生活。

See the docs描述了在从例程返回之前必须保留和恢复的寄存器。

  

v1-v8,[f4-f7]

     

这些用作寄存器变量。它们必须由被调用的函数保存。

v1 R4 的APCS替代名称。