Как вызвать функции C из сборки ARM?

Я пишу код, нацеленный на ARM Cortex-A на устройствах Android (используя ассемблер и компилятор GNU), и я пытаюсь взаимодействовать между сборкой и C. В частности, меня интересует вызов функций, написанных на C из сборки. Я пробовал много вещей, в том числе .extern директива, объявляющая функции C с asm и __asm__ и так далее, но никто из них не работал, поэтому я ищу минимальный пример этого. Ссылка на такой пример была бы столь же желательна.

4 ответов


вам нужно прочитать руку руку и / или знать, что набор инструкций все, обычно вы хотели бы сделать что-то вроде этого

asm:

bl cfun

c:
void cfun ( void )
{

}

вы можете попробовать это сами. для gnu as и gcc это работает отлично, это также должно работать отлично, если вы используете clang для получения кода c к объекту и gnu как для ассемблера. Не уверен, что вы используете.

проблема с вышеуказанным bl имеет ограниченный охват,

if ConditionPassed(cond) then
  if L == 1 then
    LR = address of the instruction after the branch instruction
    PC = PC + (SignExtend_30(signed_immed_24) << 2)

зная, что инструкция bl устанавливает ссылка зарегистрироваться на инструкцию после инструкции bl, то если вы читали о программе счетчик зарегистрироваться:

For an ARM instruction, the value read is the address of the instruction
plus 8 bytes. Bits [1:0] of this
value are always zero, because ARM instructions are always word-aligned.

Итак, если вы сделаете свой asm выглядеть так:

mov lr,pc
ldr pc,=cfun

вы получаете

d6008034:   e1a0e00f    mov lr, pc
d6008038:   e51ff000    ldr pc, [pc, #-0]   ; d6008040 
...
d6008040:   d60084c4    strle   r8, [r0], -r4, asr #9

ассемблер зарезервирует место памяти, в пределах досягаемости ПК ldr, инструкции (если возможно, в противном случае генерирует ошибку), где он разместит полный 32-битный адрес для инструкции. компоновщик позже заполнит этот адрес с помощью внешнего адрес. таким образом, вы можете получить любой адрес в адресном пространстве.

если вы не хотите играть в ассемблерные игры, как это, и хотите быть под контролем, то вы создаете местоположение, чтобы сохранить адрес функции и загрузить его в ПК самостоятельно:

    mov lr,pc
    ldr pc,cfun_addr

...

cfun_addr:
    .word cfun

составлен:

d6008034:   e1a0e00f    mov lr, pc
d6008038:   e51ff000    ldr pc, [pc, #-0]   ; d6008040 <cfun_addr>
...

d6008040 <cfun_addr>:
d6008040:   d60084c4    strle   r8, [r0], -r4, asr #9

наконец, если вы хотите перейти в современный мир ARM, где рука и большой палец смешаны или могут быть (например, используйте bx lr вместо mov pc, lr), то вы захотите использовать bx

    add lr,pc,#4
    ldr r1,cfun_addr
    bx r1
...

cfun_addr:
    .word cfun

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


вам нужны спецификации для armeabi-v7a, описывающий стек вызовов, регистры (вызываемый против вызывающего) и т. д. Затем посмотрите на выходные данные сборки из скомпилированного кода C для синтаксиса и т. д. Все сложнее при попытке вызова функций в общих библиотеках или перемещаемых объектах.


минимальный runnable armv7 пример

этот вопрос сводится к "Что такое соглашение о вызове ARM (AAPCS)". Пример a.S:

/* Make the glibc symbols visible. */
.extern exit, puts
.data
    msg: .asciz "hello world"
.text
.global main
main:
    /* r0 is the first argument. */
    ldr r0, =msg
    bl puts
    mov r0, #0
    bl exit

затем на Ubuntu 16.04:

sudo apt-get install gcc-arm-linux-gnueabihf qemu-user-static
# Using GCC here instead of as + ld without arguments is needed
# because GCC knows where the C standard library is.
arm-linux-gnueabihf-gcc -o a.out a.S
qemu-arm-static -L /usr/arm-linux-gnueabihf a.out

выход:

hello world

самая простая ошибка в более сложных примерах-забыть, что стек должен быть выровнен по 8 байтам. Например, вы хотите:

push {ip, lr}

вместо:

push {lr}

пример на GitHub с обобщенный шаблон:https://github.com/cirosantilli/arm-assembly-cheat/blob/82e915e1dfaebb80683a4fd7bba57b0aa99fda7f/c_from_arm.S


Как говорит Бретт, все, что вам действительно нужно сделать, это поместить правильные значения в правильные регистры и ветвь-с-ссылкой на адрес функции. Вам нужно знать, какой регистр будет перезаписан скомпилированной функцией, и какие регистры она восстановит до того, как вернется-все это написано в документации ABI по адресу infocentre.arm.com - ... Вам также нужно убедиться, что регистр стека установлен на то, что ожидает компилятор, и, возможно, другие регистры тоже (для PIC режим?)

но, вам действительно нужно написать код на ассемблере файлы?

Если вы используете функцию GCC "asm", вы можете вставлять фрагменты ассемблера (до тех пор, пока вам нравится) в регулярные функции C и возвращаться в C, когда это удобнее.

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

приходите к этому, почему вам вообще нужно использовать ассемблер .... C-это в основном ассемблер высокого уровня?