Wie rufe ich C-Funktionen von ARM Assembly auf?

Lesezeit: 6 Minuten

Benutzeravatar von Phonon
Phonon

Ich schreibe Code, der auf ARM Cortex-A auf Android-Geräten abzielt (unter Verwendung von GNU-Assembler und -Compiler), und ich versuche, eine Schnittstelle zwischen Assembly und C herzustellen. Insbesondere bin ich daran interessiert, in C geschriebene Funktionen von Assembly aufzurufen. Ich habe viele Dinge ausprobiert, einschließlich der .extern Direktive, die C-Funktionen mit deklariert asm und __asm__ und so weiter, aber keiner von ihnen hat funktioniert, also suche ich nach einem Minimalbeispiel dafür. Ein Hinweis auf ein solches Beispiel wäre ebenso willkommen.

  • @ user606723 Daran habe ich gedacht, aber C verwendet Header-Dateien mit Funktionsdeklarationen für die Kommunikation zwischen verschiedenen Dateien. Wäre das nicht ein ganz anderes Bild?

    – Phonon

    7. Dezember 2011 um 21:13 Uhr


  • Der Compiler sieht sich die Deklaration der Funktion in der Header-Datei an, um den Aufruf zu kompilieren. Sie müssen dasselbe manuell tun.

    – sternenblau

    8. Dezember 2011 um 8:32 Uhr

Sie müssen den ARM ARM lesen und / oder wissen, dass der Befehlssatz vollständig ist. Normalerweise möchten Sie so etwas tun

asm:

bl cfun

c:
void cfun ( void )
{

}

Sie können dies selbst versuchen. für gnu as und gcc funktioniert das ganz gut es sollte auch gut funktionieren, wenn Sie clang verwenden, um den C-Code zu einem Objekt zu bringen, und gnu wie für Assembler. Nicht sicher, was Sie verwenden.

Das Problem mit dem oben genannten ist, dass bl eine begrenzte Reichweite hat,

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)

Wenn Sie wissen, dass der bl-Befehl das Verknüpfungsregister auf den Befehl nach dem bl-Befehl setzt, lesen Sie dann über das Programmzählerregister:

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.

Wenn Sie also Ihren Asm so aussehen lassen:

mov lr,pc
ldr pc,=cfun

du erhältst

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

Der Assembler reserviert einen Speicherplatz innerhalb der Reichweite des ldr-PC-Befehls (falls möglich, erzeugt andernfalls einen Fehler), wo er die vollständige 32-Bit-Adresse für den Befehl platziert. der Linker wird diese Adresse später mit der externen Adresse füllen. Auf diese Weise können Sie jede Adresse im Adressraum erreichen.

Wenn Sie solche Assembler-Spiele nicht spielen und die Kontrolle behalten möchten, erstellen Sie den Speicherort, um die Adresse der Funktion zu speichern, und laden Sie sie selbst in den PC:

    mov lr,pc
    ldr pc,cfun_addr

...

cfun_addr:
    .word cfun

zusammengestellt:

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

Wenn Sie schließlich in die moderne ARM-Welt einsteigen möchten, in der ARM und Daumen gemischt sind oder sein können (verwenden Sie beispielsweise bx lr anstelle von mov pc,lr), dann sollten Sie bx verwenden

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

cfun_addr:
    .word cfun

Natürlich benötigen Sie dazu ein weiteres Register, und denken Sie daran, Ihr Link-Register und das andere Register vor und nach Ihrem Aufruf von C zu pushen und zu öffnen, wenn Sie sie beibehalten möchten.

  • Wenn Sie die Aufrufkonventionen noch nicht kennen, um beispielsweise zu wissen, welche Register zum Übergeben von Parametern verwendet werden, tun Sie eines von zwei Dingen, schreiben Sie C-Code, um Ihre Funktion aufzurufen, und disassemblieren Sie, um zu sehen, was der Compiler getan hat. Die richtige Antwort ist, die Anrufkonvention nachzuschlagen, auf die andere Sie hingewiesen haben.

    – Oldtimer

    7. Dezember 2011 um 22:45 Uhr

  • und Sie haben Recht. Ich habe nur gezeigt, wie ARM-Anweisungen eine Funktion aufrufen, damit keine Daumenanweisungen eine C-Funktion aufrufen. Ich kann die Antwort bearbeiten und Ihnen zeigen, wie das geht. Sie haben einen Kortex A erwähnt, also nehme ich an Sie laufen Armanweisungen, nicht zum größten Teil mit dem Daumen, nutzen die PS, die Sie haben, und nicht irgendein Bus-beschränktes Ding.

    – Oldtimer

    7. Dezember 2011 um 22:48 Uhr

  • Und wie Brett erwähnte, dass verschiebbare Objekte es komplizierter machen können, müssten Sie cfun_addr global machen, und wer auch immer das verschiebbare Objekt lädt, muss cfun_addr ändern, bevor Sie es verwenden.

    – Oldtimer

    7. Dezember 2011 um 22:51 Uhr

  • Respektiere Oldtimer!

    – Sam Hammamy

    24. November 2017 um 20:47 Uhr

Minimales lauffähiges armv7-Beispiel

Diese Frage kommt herunter: “Was ist die ARM-Aufrufkonvention (AAPCS)”. Ein Beispiel 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

Dann auf 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

Ausgabe:

hello world

Der einfachste Fehler, den man in komplexeren Beispielen machen kann, ist zu vergessen, dass der Stapel auf 8 Byte ausgerichtet sein muss. Sie möchten z. B.:

push {ip, lr}

Anstatt von:

push {lr}

Beispiel auf GitHub mit verallgemeinerter Boilerplate: https://github.com/cirosantilli/arm-assembly-cheat/blob/82e915e1dfaebb80683a4fd7bba57b0aa99fda7f/c_from_arm.S

  • Das OP hat nicht gefragt, wie man hallo Welt ausgibt. Er wollte einen anrufen C Funktion.

    – Sam Hammamy

    24. November 2017 um 20:49 Uhr

  • @SamHammamy ist es nicht puts eine C-Funktion? 🙂

    – Ciro Santilli OurBigBook.com

    24. November 2017 um 20:58 Uhr

Sie benötigen die Spezifikationen für die armeabi-v7adie den Call-Stack, Register (Callée vs. Caller) usw. beschreibt. Sehen Sie sich dann die Assembly-Ausgabe aus kompiliertem C-Code für Syntax usw. an. Die Dinge sind komplizierter, wenn Sie versuchen, Funktionen in gemeinsam genutzten Bibliotheken oder verschiebbaren Objekten aufzurufen.

Wie Brett sagt, alles, was Sie wirklich tun müssen, ist, die richtigen Werte in die richtigen Register zu schreiben und mit Link zur Adresse der Funktion zu verzweigen. Sie müssen wissen, welches Register die kompilierte Funktion überschreibt und welche Register sie wiederherstellt, bevor sie zurückkehrt – das steht alles in der ABI-Dokumentation unter infocentre.arm.com. Sie müssen auch sicherstellen, dass das Stapelregister auf das eingestellt ist, was der Compiler erwartet, und möglicherweise auch andere Register (für den PIC-Modus?)

Aber müssen Sie den Code wirklich in Assembler-Dateien schreiben?

Wenn Sie die “asm”-Funktion von GCC verwenden, können Sie Assembler-Fragmente (so lange Sie möchten) in reguläre C-Funktionen einbetten und jederzeit wieder in C zurückfallen, wenn es bequemer ist.

Es gibt Fälle, in denen C-Gubbins nicht ausreichen, aber wenn Sie C-Funktionen aufrufen können, schätze ich, dass Sie nicht dazu gehören.

Kommen Sie dazu, warum müssen Sie überhaupt Assembler verwenden …. C ist im Grunde sowieso High-Level-Assembler?

1432240cookie-checkWie rufe ich C-Funktionen von ARM Assembly auf?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy