【问题标题】:(ARM Assembly/C) Converting C functions to ARM assembly?(ARM 汇编/C) 将 C 函数转换为 ARM 汇编?
【发布时间】:2021-04-23 08:40:55
【问题描述】:

本季度我正在学习 ARM 汇编课程,但我无法完全理解它。 明天有一个实验室,我们可以在实验室中抢占先机。本实验的任务是将以下 C 函数转换为汇编:

int32_t Less1(int32_t a)
    {
    return a - 1 ;
    }

int32_t Add(int32_t a, int32_t b)
    {
    return a + b ;
    }

int32_t Square2x(int32_t x)
    {
    return Square(x + x) ;
    }

int32_t Last(int32_t x)
    {
    return x + SquareRoot(x) ;
    }

以下是确切的说明:
您的任务是为这些 C 函数中的每一个创建汇编语言替换... 主程序中提供了 Square 和 SquareRoot 函数。

我们获得了一个主程序,如有必要,我可以提供全部或部分内容。我不理解其中的 90%,并且我不希望它有很多专门用于使功能显示在带有触摸屏的测试板上。它还具有 square 和 squareroot 函数,因为看起来汇编没有这些操作。我不确定我是否正确调用它们,但它们被命名为“Square”和“Squareroot”。

这是我在 ARM 汇编中写的:

Add: PUSH {LR}
     MOV r0, a
     MOV r1, b
     ADD r2, r1, r0
     BX r2

Less1: PUSH {LR}
       MOV r0, a
       MOV r1, 1
       SUB r2, r0, r1
       BX r2

Square2x: PUSH {LR}
          MOV r0, x
          MOV r1, r0
          ADD r2, r0, r1
          Square r3, r2
          BX r3

Last: PUSH {LR}
      MOV r0, x
      Squareroot r1, r0
      ADD r2, r0, r1
      BX r2

我不知道这是否正确,因为我知道 C++ 并且刚刚开始组装,所以我很感激任何意见。我还没有完全理解寄存器和说明,但这是我根据我的教科书和我可以在网上找到的内容。

谢谢!

【问题讨论】:

  • 检查此类事情的一个不错的选择是查看编译器生成的输出。这是一个讨论这个问题的线程:stackoverflow.com/questions/137038/…
  • 在 asm 中,a 是一个全局符号名。您的函数 args 到达寄存器 - 检查调用约定文档或查看编译器输出以查看它在哪里找到它的 args。
  • 使用bx lr 将流控制返回给调用者。在机器代码中,返回 address 是另一个用于返回控制的参数。它与返回的 value 是分开的。
  • 如果你需要推送{lr}(即当一个函数调用另一个函数并且lr被重新利用)然后将返回地址直接弹出到pc中(而不是弹出到@987654329 @ 和下一个使用 bx lr)。
  • 我们不通过列出寄存器来调用函数。相反,我们在函数调用之前在寄存器中提供值,然后调用函数以使用先前建立的正确寄存器中的正确值来转移控制。

标签: c assembly arm


【解决方案1】:

您使用的是哪个 ARM 指令集,根据您的代码 sn-ps,它是 ARM(armv7 或更早版本)。 ARM 或拇指指令

int32_t Add(int32_t a, int32_t b)
    {
    return a + b ;
    }

我会让你尝试godbolt或命令行编译器等。老师必须知道你可以访问互联网和工具。但是你最好能够理解和保护编译器的代码,否则它只是抄袭,它会变得很明显。

所以 int32_t 表示 32 位,即寄存器的大小。编译语言具有“调用约定”或有时是 abi(应用程序二进制接口)。基本上每个功能都有一套规则。第一个参数以这种方式传递,特定类型有一些例外。第二个参数以这种方式传递,部分基于第一个及其类型,依此类推。返回值就是这样处理的。

有指令集的东西,比如对函数的调用(查找bl)将返回地址放在链接寄存器r14中,所以你通常但不限于使用bx lr返回,它会是bx的东西,那里是不是 lr 而是 lr 中的内容的情况。

如果函数a调用函数b,函数a的返回值将在链接寄存器中(假设只有一个链接寄存器)。如果 b 调用函数 c,尽管这改变了链接寄存器以返回函数 b,我们如何返回函数 a?您将链接寄存器保存在堆栈中并将其弹出。您添加到堆栈中的所有内容都需要在返回之前删除。

Add 函数是否调用另一个函数? (no) 那是需要使用堆栈来保存链接寄存器的第一个测试。 Add 函数是否在任何时候都有多个中间变量在运行?这可能需要其中一些保留在堆栈上。 (没有)

您可能不需要将堆栈用于 Add 功能,并不意味着您不能,非常欢迎您使用。

我会告诉你,我为这样的函数使用的 arm 编译器,这两个参数是使用寄存器传递的,你可以找出哪些参数,因为这是你分配的一部分,我将它们称为 ra 和 rb。

所以你需要一个依赖于汇编语言的标签(汇编器、工具,而不是目标),所以它可能有也可能没有冒号作为示例

Add:
   ...
   bx lr 

我给你的bx lr

中间需要两个数字相加

rc = ra + rb

或许

ra = ra + rb

您需要弄清楚输入寄存器名称和输出寄存器名称(它是一个寄存器,而不是我见过的用于 ARM 的 C 编译器的堆栈)。

所以会是这样的

Add:
   rc = ra + rb (a + b) 
   rd = rc      (put the result in the return register) 
   bx lr

你也许可以优化它,你必须弄清楚那些 指令和寄存器是。以及这些说明的规则。

执行加法后,减法应该会更容易。

int32_t Square2x(int32_t x)
    {
    return Square(x + x) ;
    }

它是在调用一个函数吗,是的,那么这对于保留返回地址意味着什么?这应该定义您的功能的骨架或框架。

在调用函数之前需要处理 x + x 。如你所料 调用约定有一个特定的寄存器,每次都会出现 x 对于这样的 32 位参数。这与用于调用 Square2X 以及 Square 以及 Less1 和 Last 以及要添加的两个操作数之一的寄存器相同。因此,如果我调用该寄存器 rx,那么在调用 Square 之前,您需要 计算 rx = rx + rx 是吗?或者还有另一种方法可以给出相同的结果。然后你打电话给Square。请注意,您可以在此处优化 并节省一些东西。

int32_t Last(int32_t x)
    {
    return x + SquareRoot(x) ;
    }

您正在调用一个函数,该函数会触发一些代码。你正在传递来的 x 在调用的函数中,可能不需要移动或修改。但我会告诉你,那个特定的寄存器基于我所知道的约定 可以被调用函数修改。因此,当 SquareRoot 返回时,该 x 寄存器可能不再包含 x。那么在调用 SquareRoot 之前如何保存它以供以后使用,以便将其添加到从 SquareRoot 返回的结果中?

从 Add 和 Less1 你应该已经知道像这样的单个 32 位参数的传入寄存器以及这样的结果的返回寄存器,让我们假设 SquareRoot 被传递并返回一个 int32_t 否则这会得到一个更难。所有函数都使用相同的规则,因此您知道将哪个寄存器 x 传递给 SquareRoot,并且知道它返回到哪个寄存器。因此您需要弄清楚如何保留传递给 Last 的 x,以便以后添加它。您已经在保留另一个被弄乱的寄存器,您需要稍后保存。但是你会看到编译器可能会改变他们如何做到这一点以避免额外的一两条指令。根据 ARM 指令集,这个应该是 5。

添加您正在寻找的两条指令。

少1,两条指令

Square2X 先传四条指令。我想你可以做这两个。

最后,五个指令

根据指令集和架构版本,其中一些可能会更多。

【讨论】:

    【解决方案2】:

    您不需要将参数一一加载到寄存器中。

    因为它在传递参数时自动设置在寄存器(使用r0-r3)。

    例如:

    int32_t Add(int32_t a, int32_t b)
    {
      return a + b ;
    }
    

    a 自动设置为r0,b 自动设置为r1

    这是一个添加函数的例子:

    Add: stmfd sp!,{lr}
         ADD r0, r0, r1 // return value store into r0
         ldmfd sp!,{lr}
         mov pc,lr
    

    【讨论】:

    • 有时在跑步之前先走是好的。在将其全部包装成stmldm 之前,可能需要对函数序言和结语进行解释?
    猜你喜欢
    • 1970-01-01
    • 2021-02-26
    • 1970-01-01
    • 2011-03-12
    • 1970-01-01
    • 2017-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多