Взаимодействие ARM-C

Я пробую простую программу для взаимодействия ARM-C. Вот код:

 #include<stdio.h>
#include<stdlib.h>

int Double(int a);
extern int Start(void);
int main(){

int result=0;
printf("in C main\n");
result=Start();
printf("result=%d\n",result);
return 0;
}

int Double(int a)
{
  printf("inside double func_argument_value=%d\n",a);

  return (a*2);
}

Файл сборки выглядит так:

.syntax unified
    .cpu cortex-m3
    .thumb

    .align
    .global Start
    .global Double
    .thumb_func



Start:
      mov r10,lr
      mov r0,#42
      bl Double
      mov lr,r10
      mov r2,r0
      mov pc,lr

Во время отладки на LPC1769 (встроенная плата исполнителей) я получаю ошибку жесткого сбоя в инструкции «result = Start ()». Я пытаюсь сделать здесь межсетевое взаимодействие arm-C. значение lr во время выполнения приведенного выше оператора (результат = Start()) равно 0x0000029F, где указана неисправная инструкция, а значение pc равно 0x0000029E. Вот как я получил инструкцию по ошибке в r1

__asm("mrs r0,MSP\n"
"isb\n"
"ldr r1,[r0,#24]\n");

Может кто-нибудь объяснить, где я ошибаюсь? Любое решение приветствуется. Заранее спасибо.

Я новичок в cortex-m3 и использую NXP LPCXpresso IDE на базе Code_Red. Вот дизассемблирование моего кода.

      IntDefaultHandler:
00000269:   push {r7} 
0000026b:   add r7, sp, #0
0000026d:   b.n 0x26c <IntDefaultHandler+4>
0000026f:   nop 
00000271:   mov r3, lr
00000273:   mov.w r0, #42 ; 0x2a
00000277:   bl 0x2c0 <Double>
0000027b:   mov lr, r3
0000027d:   mov r2, r0
0000027f:   mov pc, lr



main:
00000281:   push {r7, lr}
00000283:   sub sp, #8
00000285:   add r7, sp, #0
00000287:   mov.w r3, #0
0000028b:   str r3, [r7, #4]
0000028d:   movw r3, #11212 ; 0x2bcc
00000291:   movt r3, #0
00000295:   mov r0, r3
00000297:   bl 0xd64 <printf>
0000029b:   bl 0x270 <Start>
0000029f:   mov r3, r0
000002a1:   str r3, [r7, #4]
000002a3:   movw r3, #11224 ; 0x2bd8
000002a7:   movt r3, #0
000002ab:   mov r0, r3
000002ad:   ldr r1, [r7, #4]
000002af:   bl 0xd64 <printf>
000002b3:   mov.w r3, #0
000002b7:   mov r0, r3
000002b9:   add.w r7, r7, #8
000002bd:   mov sp, r7
000002bf:   pop {r7, pc}

Double:
000002c0:   push {r7, lr}
000002c2:   sub sp, #8
000002c4:   add r7, sp, #0
000002c6:   str r0, [r7, #4]
000002c8:   movw r3, #11236 ; 0x2be4
000002cc:   movt r3, #0
000002d0:   mov r0, r3
000002d2:   ldr r1, [r7, #4]
000002d4:   bl 0xd64 <printf>
000002d8:   ldr r3, [r7, #4]
000002da:   mov.w r3, r3, lsl #1
000002de:   mov r0, r3
000002e0:   add.w r7, r7, #8
000002e4:   mov sp, r7
000002e6:   pop {r7, pc}

По твоему совету, Двелч, поменял r10 на r3.


person gst    schedule 21.04.2013    source источник
comment
Что вы ожидаете сделать при вызове неопределенной функции Start()?   -  person ott--    schedule 21.04.2013
comment
@ott-- управление должно быть передано в сборку, а оттуда оно должно разветвляться со ссылкой на двойную функцию.   -  person gst    schedule 21.04.2013
comment
использование r10 выглядит довольно опасно, вы хотели это сделать? это, безусловно, может вызвать сбой ... нажмите {lr}, затем нажмите {pc} или нажмите {r2}, затем bx r2 ... также почему mov r2, r0, это не имеет никакого смысла.   -  person old_timer    schedule 21.04.2013
comment
использование r10 с printf в функции Double, скорее всего, является проблемой.   -  person old_timer    schedule 21.04.2013
comment
Я думаю, что строка ---- 0000029b: bl 0x270 ‹Start›---- является ошибкой.   -  person gst    schedule 21.04.2013
comment
Большое спасибо! Я изменил имя функции Start на Start_func и r10 на r4. Теперь программа работает нормально. Но не могли бы вы сказать мне, почему Start_func работает, а не Start.?   -  person gst    schedule 21.04.2013
comment
возможно, звучит как компилятор, изучите разборку двоичного файла для Start и Start_func, ответ в разнице (машинный код и данные).   -  person old_timer    schedule 21.04.2013
comment
Перед вызовом функции должен быть известен ее прототип. Что не так с вызовом Double(7); в вашем main()?   -  person ott--    schedule 21.04.2013
comment
r3 намного хуже r10! это был не мой совет, я посоветовал нажать {lr} и нажать {lr}.   -  person old_timer    schedule 21.04.2013


Ответы (2)


Я предполагаю, что вы имеете в виду межсетевое взаимодействие, а не межсетевое взаимодействие? LPC1769 — это cortex-m3, работающий только с большим пальцем/большим пальцем2, поэтому он не поддерживает инструкции руки, поэтому для этой платформы нет возможности взаимодействия. Тем не менее, играя с компилятором, чтобы увидеть, что происходит:

Сначала заставьте компилятор сделать это за вас, а затем попробуйте сами на ассемблере...

старт.с

.thumb
.globl _start
_start:
    ldr r0,=hello
    mov lr,pc
    bx r0
hang : b hang

Привет

extern unsigned int two ( unsigned int );
unsigned int hello ( unsigned int h )
{
    return(two(h)+7);
}

два.с

unsigned int two ( unsigned int t )
{
    return(t+5);
}

Makefile

hello.list : start.s hello.c two.c
    arm-none-eabi-as -mthumb start.s -o start.o
    arm-none-eabi-gcc -c -O2 hello.c -o hello.o
    arm-none-eabi-gcc -c -O2 -mthumb two.c -o two.o
    arm-none-eabi-ld -Ttext=0x1000 start.o hello.o two.o -o hello.elf
    arm-none-eabi-objdump -D hello.elf > hello.list

clean :
    rm -f *.o
    rm -f *.elf
    rm -f *.list

производит hello.list

Disassembly of section .text:

00001000 <_start>:
    1000:   4801        ldr r0, [pc, #4]    ; (1008 <hang+0x2>)
    1002:   46fe        mov lr, pc
    1004:   4700        bx  r0

00001006 <hang>:
    1006:   e7fe        b.n 1006 <hang>
    1008:   0000100c    andeq   r1, r0, ip

0000100c <hello>:
    100c:   e92d4008    push    {r3, lr}
    1010:   eb000004    bl  1028 <__two_from_arm>
    1014:   e8bd4008    pop {r3, lr}
    1018:   e2800007    add r0, r0, #7
    101c:   e12fff1e    bx  lr

00001020 <two>:
    1020:   3005        adds    r0, #5
    1022:   4770        bx  lr
    1024:   0000        movs    r0, r0
    ...

00001028 <__two_from_arm>:
    1028:   e59fc000    ldr ip, [pc]    ; 1030 <__two_from_arm+0x8>
    102c:   e12fff1c    bx  ip
    1030:   00001021    andeq   r1, r0, r1, lsr #32
    1034:   00000000    andeq   r0, r0, r0

hello.o разобрал сам:

00000000 <hello>:
   0:   e92d4008    push    {r3, lr}
   4:   ebfffffe    bl  0 <two>
   8:   e8bd4008    pop {r3, lr}
   c:   e2800007    add r0, r0, #7
  10:   e12fff1e    bx  lr

компилятор использует bl, предполагая/надеясь, что он будет вызывать руку из руки. но этого не произошло, поэтому они поставили туда батут.

0000100c <hello>:
    100c:   e92d4008    push    {r3, lr}
    1010:   eb000004    bl  1028 <__two_from_arm>
    1014:   e8bd4008    pop {r3, lr}
    1018:   e2800007    add r0, r0, #7
    101c:   e12fff1e    bx  lr


00001028 <__two_from_arm>:
    1028:   e59fc000    ldr ip, [pc]    ; 1030 <__two_from_arm+0x8>
    102c:   e12fff1c    bx  ip
    1030:   00001021    andeq   r1, r0, r1, lsr #32
    1034:   00000000    andeq   r0, r0, r0

bl to __two_from_arm — это ответвленная ссылка между режимами постановки на охрану. адрес функции назначения (два) с установленным lsbit, который указывает bx переключиться в режим большого пальца, загружается в одноразовый регистр ip (r12?), затем происходит переключение режимов ip bx. ссылка ответвления установила обратный адрес в lr, который, без сомнения, был адресом режима охраны (нулевой lsbit).

00001020 <two>:
    1020:   3005        adds    r0, #5
    1022:   4770        bx  lr
    1024:   0000        movs    r0, r0

функция two() делает свое дело и возвращает результат, обратите внимание, что при взаимодействии вы должны использовать bx lr, а не mov pc,lr. По сути, если вы не используете ARMv4 без T или ARMv5 без T, mov pc,lr — это нормальная привычка. Но любой ARMv4T или новее (ARMv5T или новее) использует bx lr для возврата из функции, если у вас нет особых причин не делать этого. (избегайте использования pop {pc} по той же причине, если вам действительно не нужно сохранять эту инструкцию и не взаимодействуете). Теперь, будучи на cortex-m3, который поддерживает только thumb+thumb2, вы не можете взаимодействовать, поэтому вы можете использовать mov pc,lr и pop {pc}, но код не переносим, ​​и это не очень хорошая привычка, так как эта привычка укусит вас, когда вы вернетесь к программированию рук.

Итак, поскольку hello был в режиме arm, когда он использовал bl, который устанавливает регистр связи, bx в two_from_arm не касается регистра связи, поэтому, когда two() возвращается с bx lr, он возвращается в режим arm после bl __two_from_arm. строка в функции hello().

Также обратите внимание на дополнительный 0x0000 после функции большого пальца, это должно было выровнять программу по границе слова, чтобы следующий код руки был выровнен...

чтобы увидеть, как компилятор меняет два пальца на руку следующим образом

unsigned int three ( unsigned int );
unsigned int two ( unsigned int t )
{
    return(three(t)+5);
}

и поместите эту функцию в hello.c

extern unsigned int two ( unsigned int );
unsigned int hello ( unsigned int h )
{
    return(two(h)+7);
}

unsigned int three ( unsigned int t )
{
    return(t+3);
}

и теперь у нас есть еще один батут

00001028 <two>:
    1028:   b508        push    {r3, lr}
    102a:   f000 f80b   bl  1044 <__three_from_thumb>
    102e:   3005        adds    r0, #5
    1030:   bc08        pop {r3}
    1032:   bc02        pop {r1}
    1034:   4708        bx  r1
    1036:   46c0        nop         ; (mov r8, r8)
...
00001044 <__three_from_thumb>:
    1044:   4778        bx  pc
    1046:   46c0        nop         ; (mov r8, r8)
    1048:   eafffff4    b   1020 <three>
    104c:   00000000    andeq   r0, r0, r0

Теперь это очень крутой батут. bl to three_from_thumb находится в режиме большого пальца, а регистр связи настроен на возврат к функции two() с установленным lsbit, без сомнения, для указания возврата в режим большого пальца.

Батут начинается с bx pc, pc настроен на две инструкции вперед, а внутри pc всегда очищен lsbit, поэтому bx pc всегда переводит вас в режим постановки на охрану, если он еще не находится в режиме постановки на охрану, и в любом режиме на две инструкции вперед. Две инструкции перед bx pc — это инструкция руки, которая разветвляется (не разветвляется!) на три функции, завершая батут.

Обратите внимание, как я написал вызов hello() в первую очередь

_start:
        ldr r0,=hello
        mov lr,pc
        bx r0
    hang : b hang

это на самом деле не будет работать не так ли? Он перенесет вас из руки в большой палец, но не из большого пальца в руку. Я оставлю это в качестве упражнения для читателя.

Если вы измените start.s на это

.thumb

.globl _start
_start:
    bl hello
hang : b hang

компоновщик заботится о нас:

00001000 <_start>:
    1000:   f000 f820   bl  1044 <__hello_from_thumb>

00001004 <hang>:
    1004:   e7fe        b.n 1004 <hang>
    ...

00001044 <__hello_from_thumb>:
    1044:   4778        bx  pc
    1046:   46c0        nop         ; (mov r8, r8)
    1048:   eaffffee    b   1008 <hello>

Я бы всегда дизассемблировал подобные программы, чтобы убедиться, что компилятор и компоновщик решили эти проблемы. Также обратите внимание, что, например, __hello_from_thumb можно использовать из любой функции thumb, если я вызываю hello из нескольких мест, какая-то рука, какой-то большой палец, и hello был скомпилирован для руки, тогда вызовы руки будут вызывать hello напрямую (если они могут до него добраться) и все вызовы большого пальца будут использовать один и тот же hello_from_thumb (если они смогут его достичь).

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

Если вы действительно имели в виду межсетевое взаимодействие, а не межсетевое взаимодействие, то, пожалуйста, опишите, что это такое, и я удалю этот ответ.

РЕДАКТИРОВАТЬ:

Вы использовали регистр для сохранения lr во время вызова Double, это не сработает, никакой регистр не сработает, для этого нужно использовать память, а проще всего — стек. Посмотрите, как это делает компилятор:

00001008 <hello>:
    1008:   e92d4008    push    {r3, lr}
    100c:   eb000009    bl  1038 <__two_from_arm>
    1010:   e8bd4008    pop {r3, lr}
    1014:   e2800007    add r0, r0, #7
    1018:   e12fff1e    bx  lr

r3, скорее всего, выровняет стек по 64-битной границе (делает его быстрее). следует отметить, что регистр ссылки сохраняется в стеке, но всплывающее окно не извлекается на компьютер, потому что это не сборка ARMv4, поэтому для возврата из функции требуется bx. Поскольку это режим постановки на охрану, мы можем перейти к lr и просто bx lr.

Для большого пальца вы можете нажимать только r0-r7 и lr напрямую и выталкивать r0-r7 и pc напрямую, вы не хотите выталкивать на pc, потому что это работает, только если вы остаетесь в том же режиме (большой палец или рука). это хорошо для cortex-m, или хорошо, если вы знаете, что такое все ваши абоненты, но в целом плохо. Так

00001024 <two>:
    1024:   b508        push    {r3, lr}
    1026:   f000 f811   bl  104c <__three_from_thumb>
    102a:   3005        adds    r0, #5
    102c:   bc08        pop {r3}
    102e:   bc02        pop {r1}
    1030:   4708        bx  r1

та же сделка r3 используется в качестве фиктивного регистра, чтобы поддерживать стек в выравнивании для повышения производительности (я использовал сборку по умолчанию для gcc 4.8.0, которая, вероятно, является платформой с 64-битной аксиальной шиной, указав, что архитектура может удалить этот дополнительный регистр). Поскольку мы не можем вытолкнуть pc, я предполагаю, что из-за того, что r1 и r3 были бы не в порядке, и был выбран r3 (они могли выбрать r2 и сохранить инструкцию), есть два выталкивания, одно, чтобы избавиться от фиктивного значения в стеке, и другой, чтобы поместить возвращаемое значение в регистр, чтобы они могли вернуться к нему.

Ваша функция Start не соответствует ABI, и в результате, когда вы смешиваете ее с такими большими библиотеками, как вызов printf, вы, без сомнения, выйдете из строя. Если вы этого не сделали, это была глупая удача. Ваш ассемблерный список main показывает, что ни r4, ни r10 не использовались, и если предположить, что main() не вызывается, кроме начальной загрузки, то именно поэтому вам сошло с рук либо r4, либо r10.

Если это действительно LPC1769, то все это обсуждение не имеет значения, так как он не поддерживает ARM и не поддерживает взаимодействие (взаимодействие = смешение кода режима ARM и кода режима большого пальца). Ваша проблема не была связана с межсетевым взаимодействием, вы не взаимодействуете (обратите внимание на поп {pc} в конце функций). Ваша проблема, вероятно, была связана с вашим ассемблерным кодом.

РЕДАКТИРОВАТЬ2:

Изменение make-файла для указания cortex-m

00001008 <hello>:
    1008:   b508        push    {r3, lr}
    100a:   f000 f805   bl  1018 <two>
    100e:   3007        adds    r0, #7
    1010:   bd08        pop {r3, pc}
    1012:   46c0        nop         ; (mov r8, r8)

00001014 <three>:
    1014:   3003        adds    r0, #3
    1016:   4770        bx  lr

00001018 <two>:
    1018:   b508        push    {r3, lr}
    101a:   f7ff fffb   bl  1014 <three>
    101e:   3005        adds    r0, #5
    1020:   bd08        pop {r3, pc}
    1022:   46c0        nop         ; (mov r8, r8)

во-первых, это все большой палец, поскольку на cortex-m нет режима руки, во-вторых, bx не требуется для возврата функции (поскольку нет изменений режима руки / большого пальца). Так что pop {pc} будет работать.

Любопытно, что фиктивный регистр все еще используется при отправке, я пробовал сборку arm7tdmi/armv4t, и она все еще работала, поэтому есть какой-то другой флаг, который можно использовать, чтобы избавиться от этого поведения.

Если бы вы хотели узнать, как создать функцию на ассемблере, которую можно было бы вызывать из C, вы должны были это сделать. Создайте функцию C, которая чем-то напоминает структуру функции, которую вы хотите создать на ассемблере:

extern unsigned int Double ( unsigned int );
unsigned int Start ( void )
{
    return(Double(42));
}

собрать потом разобрать

00000000 <Start>:
   0:   b508        push    {r3, lr}
   2:   202a        movs    r0, #42 ; 0x2a
   4:   f7ff fffe   bl  0 <Double>
   8:   bd08        pop {r3, pc}
   a:   46c0        nop         ; (mov r8, r8)

и начните с того, что вы выполняете функцию сборки.

.globl Start
.thumb_func
Start:
   push {lr}
   mov  r0, #42 
   bl   Double
   pop  {pc}

То, или прочитать arm abi для gcc и понять какие регистры можно и нельзя использовать не сохраняя их в стеке, какие регистры используются для передачи и возврата параметров.

person old_timer    schedule 21.04.2013
comment
lr 0x29F является правильным для функции по адресу 0x29E, аналогично pc 0x29E (ну, вы не можете увидеть, что значение pc может вам, если это не разрешено аппаратным отладчиком) является правильным, потому что pc всегда имеет обнуленный lsbit. Bx использует младший бит для определения переключения на активацию (0) или переключения на большой палец (1), а затем младший бит удаляется, оставляя фактически ноль. Вам нужно будет опубликовать разборку вашего скомпилированного кода, чтобы лучше понять, что произошло в вашей системе. - person old_timer; 21.04.2013

Функция _start — это точка входа программы на языке C, которая вызывает функцию main(). Для отладки функция _start любой программы на C находится в файле сборки. На самом деле реальной точкой входа в программу в Linux является не функция main(), а функция с именем _start(). Стандартные библиотеки обычно предоставляют такую ​​версию, которая запускает некоторый код инициализации, а затем вызывает main().

Попробуйте скомпилировать это с помощью gcc -nostdlib:

person santy    schedule 21.04.2013
comment
управление должно перейти к сборке. Определение start находится в файле сборки. - person gst; 21.04.2013