Как вызвать функции C из ARM Assembly? - PullRequest
10 голосов
/ 08 декабря 2011

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

Ответы [ 4 ]

6 голосов
/ 08 декабря 2011

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

asm:

bl cfun

c:
void cfun ( void )
{

}

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

Проблема с вышеперечисленным ограничена,

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.

так что если вы сделаете ваш ассм похожим на это:

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 pc, инструкции (если возможно, иначе выдаст ошибку), где он поместит полный 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, где ARM и thumb смешаны или могут быть (например, используйте bx lr вместо mov pc, lr), тогда вы захотите использовать bx

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

cfun_addr:
    .word cfun

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

1 голос

Пример минимального запуска 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

1 голос
/ 08 декабря 2011

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

0 голосов
/ 08 декабря 2011

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

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

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

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

Приходите к этому, зачем вам вообще нужен ассемблер .... C все равно в основном высокоуровневый ассемблер?

...