【问题标题】:Cannot write to ARM register R4: feature or bug?无法写入 ARM 寄存器 R4:功能还是错误?
【发布时间】:2011-12-07 19:15:49
【问题描述】:

我最近在使用 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 汇编突出显示有点奇怪):

.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)。

【问题讨论】:

  • 您能否展示可能崩溃的最小代码? fault addr 00000001 似乎建议您从 r4 中包含的地址加载。 r0 中出现的相同结果听起来很奇怪。
  • @user786653 好的,正在编辑我的问题。
  • 我仍然觉得我们没有显示所有相关代码。您是否有展示这种行为的自包含示例?
  • @user786653 没有其他代码。我使用 Android Native 接口从 C 调用它。如果这是相关的,我也可以发布我的 C 函数。
  • 是的,请执行此操作以及您用于存储和检索v1 的所有代码(原因可能是一个简单的拼写错误)。

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


【解决方案1】:

我们试图解释的是,如果你想在函数中使用 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 的修改将不会被注意到。

编译器遵循调用约定规则并期望所有被调用者也这样做,如果你弄乱了它可能崩溃/失败的方式从无效到非常严重,所以你需要遵守你的那些调用约定汇编。

【讨论】:

  • 您的意思是它崩溃了,因为我将 r4 的值更改为更高级别的函数不期望的值,并且当我的函数返回时程序崩溃。对吗?
【解决方案2】:

根据 APCS,R4 是您必须保留的寄存器之一。如果您需要使用它,则在进入时将其存储在堆栈中,并在退出时再次将其弹出。有一些寄存器,如R0-R3,是暂存寄存器;你可以在你的例程中破坏这些而不保留它们。

See the docs 了解在从例程返回之前必须保留和恢复哪些寄存器。

v1-v8,[f4-f7]

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

v1R4 的 APCS 替代名称。

【讨论】:

  • 这一切都是真的,但必须保留它的事实并不能解释崩溃。我更新了我的答案,请看一下。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-06
相关资源
最近更新 更多