Вызов ARM сборки от C, GCC (голый металл) - PullRequest
7 голосов
/ 22 января 2012

Я пытаюсь сделать голое программирование на ARM с GCC и тестировать на QEMU. Всякий раз, когда я вызываю метку ARM из C, моя программа зависает. У меня есть простой пример кода, который показывает проблему на https://gist.github.com/1654392 - когда я вызываю activ () в этом коде, он зависает.

Я заметил, что с objdump, когда я делаю bl из ассемблера в код на C (как в _start), он генерирует небольшую оболочку, которая переключается на инструкции большого пальца. Кажется, что код C все генерируется в инструкциях большого пальца, но вся моя сборка генерируется в инструкциях ARM (32-bit). Я не могу понять, почему это или как это исправить.

Ответы [ 4 ]

5 голосов
/ 20 апреля 2013

Чтобы вызвать функцию режима ARM, определенную в сборке, из функции режима THUMB, определенной в C, вам необходимо определить символ в сборке как функцию, и инструменты (Linaro gcc) будут выдавать инструкцию blx вместо bl.

Пример:

@ Here, we suppose that this part of code is inside of .code 32

.type fn, %function

fn:
   mov  pc, lr
3 голосов
/ 22 января 2012

см. http://github.com/dwelch67/yagbat каталог qemu.

Вот несколько примеров вызова руки или большого пальца из руки

start_vector:
    mov sp,#0x20000
    ;@ call an arm function from arm
    bl notmain

    ;@ call a thumb function frm arm
    ldr r0,=0xAABBAABB
    bl hexstring_trampoline

    ;@ call a thumb function frm arm
    ldr r0,=0x12341234
    ldr r1,hexstring_addr
    mov lr,pc
    bx r1

    ;@ call a thumb function frm arm
    ldr r0,=0x12312344
    bl hexstring_trampoline

hang:
    b hang

hexstring_trampoline:
    ldr r1,hexstring_addr
    bx r1

hexstring_addr: .word hexstring

Если вы посмотрите на справочник по набору команд, вы увидите, что вам нужно использовать BX или BLX для переключения между состояниями руки и большого пальца. BLX не так широко поддерживается, как BX.

С точки зрения определения, программный счетчик pc на две команды впереди во время выполнения инструкции. для большого пальца 4 байта, для плеча 8 байтов. В любом случае две инструкции. Чтобы смоделировать bl, который нельзя использовать для изменения состояния, вам нужно загрузить регистр связи с обратным адресом и использовать bx для перехода к состоянию изменения функции в зависимости от lsbit адреса. так что

mov lr,pc
bx r1
here:

mov lr, pc выше загружает адрес здесь: это наш обратный адрес, bx r1 независимо от состояния вызывает функцию. lsbit lr-адреса указывает режим возврата, и вам всегда нужно использовать bx для возврата

pre_thumb:
ldr pc,lr

thumb_capable:
bx lr

Компилятор выделяет инструкцию bl для вызова функций, а компоновщик заполняет остальные позже, если он слишком далеко, то ему нужна функция батута, которую компоновщик добавляет сам. Аналогичным образом, если вам нужно изменить режимы, BL вызывает функцию батута, которая делает это. Я смоделировал, что в одном из вышеперечисленного имитировать это, вы можете видеть, что это немного расточительно, надеюсь, мое объяснение того, что компилятор только выделяет место для bl, делает более ясным, расточительным было бы всегда планировать изменение режима и необходимо вставить nops для большинства вызовов функций в коде.

Код также включает в себя вызов на руку с большого пальца в ассемблере:

.thumb

.thumb_func
.globl XPUT32
XPUT32:
    push {lr}
    ;@ call an arm function from thumb asm
    ldr r2,=PUT32
    mov lr,pc
    bx r2
    pop {r2}
    bx r2

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

    push {r2,lr}
    ;@ call an arm function from thumb asm
    ldr r2,=PUT32
    mov lr,pc
    bx r2
    pop {r2}
    mov lr,r2
    pop {r2}
    bx lr

Большой палец к большому или рука к руке, просто используйте bl, если можете достать. Ldr ПК, адрес, если вы не можете.

0 голосов
/ 31 января 2012

Для устранения путаницы:

Проблема заключалась в том, что кросс-компилятор GCC в Ubuntu для ARM по умолчанию генерирует большие (16-битные) инструкции. Как показывают другие ответы здесь, вызов между ними возможен, но в то время как ассемблер GNU обнаружил, что код C генерирует инструкции большого пальца, и поэтому с радостью сгенерировал прокладки, используя bx для правильной установки режима вызова в C, Я не имею никакого контроля над тем, что сам GCC генерирует для вызова функций, и он вызывал их просто с помощью bl, что прервалось, потому что мой ассемблерный код должен быть инструкциями ARM (32-битными).

Решение (которое плохо документировано) заключается в отправке gcc -marm, который, по крайней мере, сделает весь код одним и тем же типом.

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

0 голосов
/ 23 января 2012

Если вы собираете свой asm-код как Thumb, вам нужно пометить функцию как функцию Thumb, чтобы компоновщик использовал правильные инструкции при переходе к нему (например, BLX или BX по адресу с установленным младшим битом). Это делается с помощью директивы .thumb_func:

.global activate
.thumb_func
activate:
    b test

Другой вариант - явно попросить ассемблер сгенерировать код ARM:

.code 32
.global activate
activate:
    b test

Проверьте и эту статью , хотя помните, что текущим процессорам не нужно много обходных путей, которые были необходимы в ARMv4, поэтому вам, вероятно, не следует следовать им вслепую.

...