Не удается записать в регистр ARM R4: функция или ошибка? - PullRequest
5 голосов
/ 14 октября 2011

Недавно я столкнулся со странным поведением 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) - это регистр переменной, который должен быть сохранен. Однако в ссылке , приведенной в его ответе, сама документация ARM использует регистр v1, сначала сохраняя свой результат в стеке вместе со значением другого сохраненного регистра:

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

и позже, возвращая их значения. Когда я компилирую этот код, он падает так же, как мой оригинал, но

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

нет (обратите внимание, v2 вместо v1).

Ответы [ 2 ]

8 голосов
/ 14 октября 2011

Мы пытаемся объяснить, что вам нужно сделать это, если вы хотите использовать 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.

7 голосов
/ 14 октября 2011

Согласно APCS, R4 является одним из регистров, которые вы должны сохранить.Если вам нужно его использовать, сохраните его в стеке при входе и снова вытолкните при выходе.Есть несколько регистров, таких как R0-R3 , которые являются чистыми регистрами;вам разрешено испортить их в вашей повседневной жизни, не сохраняя их.

См. Документы для описания регистров, которые вы должны сохранить и восстановить, прежде чем вернуться из своей рутины.

v1-v8, [f4-f7]

Они используются в качестве переменных регистра.Они должны сохраняться вызываемыми функциями.

v1 - альтернативное имя APCS для R4 .

...