【问题标题】:How to define an ARM assembly function that can be used in C如何定义可在 C 中使用的 ARM 汇编函数
【发布时间】:2019-08-26 08:39:13
【问题描述】:

我已经学习 C 几个月了,现在我开始更深入地研究底层语言 - ARM 汇编,所以,我决定从一个非常基本的项目开始,制作一个汇编文件 .S它定义了一个阶乘函数,然后在C中调用和使用它。所以我想问一下,汇编是否可以用来定义这样一个复杂的函数,并且可以用作C中的另一个函数?如果您不介意,请给我一个简单的 .S 文件示例,其中一个函数从标准输入 num1 和 num2 加载 2 个数字到寄存器,计算 num1*num2 并返回定义的结果,可以在 C 源代码中使用文件?

感谢您的阅读。如果这个问题很愚蠢,至少,请给我一个方向,以便我自己跟随并找出它。

【问题讨论】:

  • 关于从 C 中调用 asm 函数的问题并不愚蠢(但是,如果您尝试过,您可以找到大量关于此的信息)。给你几乎所有东西的请求在这里通常不受欢迎——首先展示你的努力,到目前为止你做了什么,你在哪里卡住了。在汇编中进行输入是一个非常不切实际的想法 - 在 C 中执行此操作要容易得多,那么重点是什么,您将获得什么?在 C 中进行输入,将其作为参数传递给 asm 函数以进行处理并将结果返回到您的 C 程序中。这更有意义。
  • 你可能想检查编译器生成的汇编代码。如果您使用“-S”标志,gcc 将产生 .s 输出。

标签: assembly arm gnu-toolchain


【解决方案1】:

要从 C 中使用用 Assembler 编写的函数,您主要需要做的事情:

  1. 在 C 中声明但不定义函数。
  2. 坚持 ARM 调用约定。

声明

声明很简单,例如:

int add(int a, int b);

这是一个声明,因为它不包含函数体。当然,名称(在本例中为 add)必须匹配。

如果在C++中使用,必须加上extern "C":

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

调用约定

调用约定定义了如何将参数传递给函数并从函数返回,以及必须保存哪些寄存器。你需要熟悉细节。可以在ARM (A32) calling convention 找到简化的概述。

一个超短且非常简化的版本是:

  • 前四个参数在 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

它基本上是add函数的debug版本:在堆栈上分配空间来存储ab作为局部变量和帧指针寄存器(fp) 指向局部变量。最后,一切都恢复了。

【讨论】:

    【解决方案2】:

    大多数理智的编译器编译成汇编,然后调用汇编器将其转换为对象。

    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

    看看so.s

         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
    

    【讨论】:

    • 汇编语言是由汇编器、工具而不是目标定义的。但是,除非声明为全局,否则标签是本地的并不少见,当然对于气体来说,这与 C 中的标签(变量/函数名称)是全局的,除非声明为本地(静态)不同。有些编译器会破坏标签,有些则不会,所以主要练习是针对您的工具链/汇编器,您是否需要声明全局标签以及是否需要对其进行破坏。
    • 还要注意,如果你想做 thumb,那么在函数标签之前使用 .thumb_func 是个好主意(将自己应用到它看到的下一个标签不必是下一行代码)。
    猜你喜欢
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    • 2011-01-27
    • 1970-01-01
    • 1970-01-01
    • 2021-07-27
    • 2020-08-11
    相关资源
    最近更新 更多