我最近在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)是一个变量寄存器,应该保留。但是,在ARM文档的答案中提供的link中,ARM文档本身通过首先将其结果与另一个保留的寄存器的值一起保存在堆栈中来利用v1寄存器:
STMDB sp!,{v1,lr}
LDR v1,[a2,#0]

然后检索它们的值(value)。当我编译此代码时,它的崩溃方式与原始代码相同,但是
STMDB sp!,{v1,lr}
LDR v2,[a2,#0]

否(注意v2而不是v1)。

最佳答案

我们试图解释的是,如果要在函数中使用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()函数返回的内容。例如,如果该值从未在调用fun时使用,则您对r4的修改将不会被注意到。

编译器会遵循调用约定规则,并期望所有被调用者也遵守该规则,如果您对此感到困惑,那么崩溃/失败的方式将从无效变为非常严重,因此您需要在asm中遵循这些调用约定。

09-25 22:20