【问题标题】:Passing arguments from asm to C in on ARM在 ARM 上将参数从 asm 传递给 C
【发布时间】:2013-02-12 16:16:25
【问题描述】:

我在这个论坛上阅读了很多主题,并找到了很多关于这个主题的答案。我实现了从我的汇编代码中将 5 个参数传递给 C 函数。为此,我使用了以下说明:

mov r0, #0
mov r1, #1
mov r2, #2
mov r3, #3
mov r4, #4
STR r4, [sp, #-4]!
BL displayRegistersValue

但今天我试图将整个寄存器传递给 C 函数以将它们保存在 C 结构中。我试过这个指令:

STMDB sp!, {registers that i want to save}

我的 C 函数:

displayRegistersValue(int registers[number_of_registers])
char printable = registers[0] + (int)'0'; // Convert in a printable character
print_uart0(&printable);

但是我的显示器不好。那么,如何访问 C 代码中的寄存器呢?

【问题讨论】:

  • STMDB 之前/之后,您需要mov r0, sp。这会将指向您的“C”例程的指针作为第一个参数传递。那么你的代码应该可以工作了。
  • 我不会指望它。您永远不知道编译器想要保存到堆栈中的内容。这个,其他垃圾等等。更安全的是给它自己的地址。不过,我认为 OP 不理解指针,这会妨碍您。
  • 编译器会在栈上保留它需要的空间;它不应该覆盖以前的堆栈帧。 sp! 表示保存后更新堆栈指针。他似乎为 EABI 使用了正确的堆栈方向。

标签: c assembly arm


【解决方案1】:

很确定 ARM 标准只允许 R0-R3 按值传递,因此最多 4 个。如果您需要更多值,则将它们压入堆栈并以这种方式访问​​它们 - 就像编译器一样。或者创建一个结构并传递它的地址。

好的,仔细检查一下,我是对的 here 是指向 ARM 调用约定的链接 - 在页面下方。

要做你想做的事,将某个内存位置(数组)的地址传递到你的汇编例程中。一旦你有了那个地址,可能在 r0 之内,你就可以 stmdb!到那个位置你所有的寄存器值和那个内存将在 C 级别上是可见的。

请注意,这可能不会像您想象的那样做。根据上面的调用约定链接,允许这些值进行相当大的更改。如果这是为了调试,最好使用调试器并以这种方式观察寄存器。

好吧,你还是不明白:


{
    int registerValues[14];

    myAsmRoutine(registerValues);

    print_uart0(& registerValues);
}

myAsmRoutine:
    stmia  r0!, {r1-r14}
    blx   lr

我跳过了 R0 和 PC,但你明白了。此外,您需要做一些复杂的事情来将值更改为可打印的格式 - sprintf 或 itoa os 之类的。

【讨论】:

  • 好的,所以我必须将函数参数的数量设置为要保存的寄存器数量?
  • 没有。传递了 1 个参数 - 内存地址。该地址存储在寄存器中并用于保存您的值。
【解决方案2】:
displayRegistersValue(int registers[number_of_registers])

这是一个数组而不是结构,它作为指针传递,而不是长列表。顺便说一句,结构也是如此。

通常最简单的方法是在 asm 中构造一个执行您想要的操作的 C 函数,然后查看编译器生成的内容,然后从那里开始(使用 ABI 文档进行确认等)。

#define NUMREGS 13
void displayRegistersValue(unsigned int registers[NUMREGS]);
void outer ( void )
{
    unsigned int regs[NUMREGS];
    displayRegistersValue(regs);
}

> arm-none-linux-gnueabi-gcc -O2 -c fun.c -o fun.o
> arm-none-linux-gnueabi-objdump -D fun.o

fun.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <outer>:
   0:   e52de004    push    {lr}        ; (str lr, [sp, #-4]!)
   4:   e24dd03c    sub sp, sp, #60 ; 0x3c
   8:   e28d0004    add r0, sp, #4
   c:   ebfffffe    bl  0 <displayRegistersValue>
  10:   e28dd03c    add sp, sp, #60 ; 0x3c
  14:   e49df004    pop {pc}        ; (ldr pc, [sp], #4)

你需要做类似的事情,通过添加堆栈指针在堆栈上腾出空间,保存 lr 这样你就不会使用分支链接将其丢弃,将寄存器复制到该内存(堆栈)指向 r0 到您要传递的内存/数组的开头,然后调用函数(r0 是您传递给函数的第一个也是唯一的参数)。

push {lr} 
mov lr,sp 
stmdb sp!,{r0-r12} 
mov r0,lr 
bl displayRegistersValue
add sp,sp,#52
pop {lr}

【讨论】:

  • +1 用于提及objdumpobjdump 对于了解汇编程序的人来说是诊断许多问题的绝佳工具。
  • 非常感谢大家,今晚我会尝试您的所有解决方案。
【解决方案3】:

数组作为指针传递到单个寄存器中。如果你想要 5 个寄存器,那么你需要有 5 个参数(int i1、int i2 等)。

【讨论】:

  • 好的,如果我理解的话,如果我想传递 13 个寄存器,我的 C 函数中需要 13 个参数?还有其他方法吗?您说数组作为指针传递,因此在单个寄存器中。所以它假设指针存储在 r0 中。但是存储的是什么?指针的地址或值?
【解决方案4】:

引用 ARM APCS 文档:

"前四个寄存器 r0-r3 (a1-a4) 用于将参数值传递给子程序并从函数返回结果值。它们也可用于保存程序中的中间值(但是,一般情况下,仅在子程序调用之间)。”

因此,如果您想将超过 4 个值传递给 C 函数,则需要传递堆栈中的其余值。更好的办法是将寄存器值放在已静态分配的内存区域中,并将内存地址(指针)传递给 C 函数。函数可以取消引用指针以获取寄存器值。

【讨论】:

    猜你喜欢
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    • 2021-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-29
    • 2015-06-07
    相关资源
    最近更新 更多