Как определить функцию сборки ARM, которая может быть использована в C - PullRequest
0 голосов
/ 04 апреля 2019

Я выучил C уже несколько месяцев, и теперь я начинаю более глубоко изучать язык нижнего уровня - сборку ARM, поэтому я решил начать с очень простого проекта - создания файла сборки .Sкоторый определяет факториальную функцию, а затем вызывает и использует ее в C. Итак, я хочу спросить, может ли сборка использоваться для определения такой сложной функции и может ли она использоваться как другая функция в C?Если вы не возражаете, приведите простой пример файла .S, в котором функция загружает 2 регистра из стандартных входных данных num1 и num2 в регистры, вычисляет num1 * num2 и возвращает определенный результат, который можно использовать в исходном коде Cфайл?

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

Ответы [ 2 ]

0 голосов
/ 04 апреля 2019

Большинство здравомыслящих компиляторов компилируются в сборку, а затем вызывают ассемблер, чтобы превратить это в объект.

unsigned int fun ( unsigned int a, unsigned int b)
{
    return((a<<1)+(b^0xFF));
}

arm-none-eabi-gcc -O2 -c so.c -o so.o
arm-none-eabi-objdump -D so.o

00000000 <fun>:
   0:   e22110ff    eor r1, r1, #255    ; 0xff
   4:   e0810080    add r0, r1, r0, lsl #1
   8:   e12fff1e    bx  lr

Я считаю, что это самый простой способ прочитать выходные данные, хотя я позволяю собирать их.

Но вы можете сделать это

arm-none-eabi-gcc -O2 -S so.c -o so.s

или

arm-none-eabi-gcc -O2 -c -save-temps so.c -o so.o

и посмотрите на так.

     cat so.s
    .cpu arm7tdmi
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 1
    .eabi_attribute 30, 2
    .eabi_attribute 34, 0
    .eabi_attribute 18, 4
    .file   "so.c"
    .text
    .align  2
    .global fun
    .arch armv4t
    .syntax unified
    .arm
    .fpu softvfp
    .type   fun, %function
fun:
    @ Function supports interworking.
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    eor r1, r1, #255
    add r0, r1, r0, lsl #1
    bx  lr
    .size   fun, .-fun
    .ident  "GCC: (GNU) 8.2.0"

который вы могли бы собрать самостоятельно

arm-none-eabi-as so.s -o so.o
arm-none-eabi-objdump -D so.o

so.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <fun>:
   0:   e22110ff    eor r1, r1, #255    ; 0xff
   4:   e0810080    add r0, r1, r0, lsl #1
   8:   e12fff1e    bx  lr

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

    .global fun
fun:
    eor r1, r1, #255
    add r0, r1, r0, lsl #1
    bx  lr

arm-none-eabi-as so.s -o so.o
arm-none-eabi-objdump -D so.o

Disassembly of section .text:

00000000 <fun>:
   0:   e22110ff    eor r1, r1, #255    ; 0xff
   4:   e0810080    add r0, r1, r0, lsl #1
   8:   e12fff1e    bx  lr
0 голосов
/ 04 апреля 2019

Чтобы использовать функцию, написанную на ассемблере из C, вам, в основном, нужно:

  1. Объявить, но не определять функцию в C.
  2. Придерживаться соглашений о вызовах ARM.

Декларация

Объявление легко, например:

int add(int a, int b);

Это объявление, потому что оно не включает в себя телофункция.И, конечно, имя ( добавить в этом случае) должно совпадать.

Если вы используете его в C ++, вы должны добавить extern "C":

extern "C" int add(int a, int b);

Соглашение о вызовах

Соглашение о вызовах определяет, как аргументы передаются и возвращаются из функций и какие регистры должны быть сохранены.Вам нужно будет ознакомиться с деталями.Упрощенный обзор можно найти в ARM (A32) соглашение о вызовах .

Супер короткая и очень упрощенная версия:

  • Переданы первые четыре аргументав R0-R3 остаток передается в стек.
  • Возвращаемые значения возвращаются в R0.
  • Регистры, отличные от R0-R3, должны быть сохранены и восстановлены.

Простая реализация для вышеуказанной функции:

add:
        add     r0, r0, r1
        bx      lr

a передается в R0, b передается в R1.Результат возвращается в R0.Регистры выше R3 не сохраняются и не восстанавливаются, поскольку к ним не прикасаются.

Более расширенная версия выглядит следующим образом:

add:
        str     fp, [sp, #-4]!
        add     fp, sp, #0
        sub     sp, sp, #12
        str     r0, [fp, #-8]
        str     r1, [fp, #-12]
        ldr     r2, [fp, #-8]
        ldr     r3, [fp, #-12]
        add     r3, r2, r3
        mov     r0, r3
        add     sp, fp, #0
        ldr     fp, [sp], #4
        bx      lr

Это в основном версия debug add function: в стеке выделяется место для хранения a и b в качестве локальных переменных, а регистр указателя кадра (fp) указывает на локальные переменные.В конце все восстанавливается.

...