【问题标题】:ARM assembly - Are registers guaranteed to be large enough to hold a pointer?ARM 汇编 - 是否保证寄存器足够大以容纳指针?
【发布时间】:2014-06-04 23:16:20
【问题描述】:

我正在编写一个小程序来学习一些 ARM 汇编以及它如何与 C 交互。我现在要做的是从 C 调用汇编函数并将指针作为参数传递给它。

现在,我知道单个参数通常会存储在 r0 中,但我想知道这是否也适用于指针。是否存在 CPU 的寄存器不足以容纳指针的情况?如果我试图通过值传递它,比如说,一个结构体怎么办?

虽然我在基于 Cortex-A8 的主板上对此进行测试,但我想知道(至少理论上)这是否可能在其他架构中发生。如果是这种情况,是否有一种与平台无关的方法来知道它有多大(除了在 C 中使用 sizeof)?

【问题讨论】:

  • 这完全属于 ABI 的领域。我不知道哪个 ABI 适用于 ARM 上的 C,但肯定有一个,希望只有一个。

标签: c pointers assembly arm size


【解决方案1】:

Cortex A8 上的 CPU 寄存器为 32 位宽,因此指针可以很好地放入其中。就是这样的想法。不过,您有一个更大的问题 - 不能保证任何参数都会在任何寄存器中传递,即使它适合。 c 编译器可以随意传递参数,即使是信鸽。

也许能够回答“{您正在使用的编译器}如何传递指针?”这个问题,但您可能需要知道函数的完整签名,无论是没有任何特定于编译器的标志生效,也许是返回值? ETC。 。 .

“如果我试图通过值传递它会怎样?” 就更难了。答案是“可能会在堆栈上传递”(堆栈是另一个实现细节!)但还有其他阴险的细节,比如 - 在函数结束时,谁递减堆栈指针?调用者还是被调用者?是否有任何特殊的对齐要求(可能是!)等等。

如果您真的想要编写程序集,最好使用编译器特定的扩展来编写程序集 - 但一个 c 函数。这样一来,一些真正困难的事情就会为您处理。

【讨论】:

  • 确实,我会看看我能得到什么文档。我正在使用 gcc-arm-linux-gnueabihf 工具链,所以我想一定有一些文档指定它将如何处理参数传递。
  • “内存总线”,以及它与指针的关系,真的有点模糊 - 考虑 Cortex-A15,其中指针是 32 位,物理地址是 64 位,地址总线是 40 位并且数据总线是 128 位 ;)
  • 此外,在这种情况下,“不能保证任何参数都会在任何寄存器中传递,即使它适合”。编译器必须保证在调用任何外部函数时遵循定义的ABI,否则什么都不起作用。当然它可以在inside它自己的对象中快速而松散地播放,但它甚至不知道外部代码存在直到链接。
  • 在 ARM 中,寄存器可以存储进程的地址,但不能像 LPAE 那样存储整个地址范围
  • @Notlikethat:编译器可能会对定义为静态的函数执行此操作。在这种情况下,它知道该函数不会在当前文件之外使用。但在任何其他情况下,编译器都应该(并且将)遵循 ABI。
【解决方案2】:

如果呢? ...好吧,试试吧:

typedef struct
{
    unsigned int a[30];
    unsigned int b;
    unsigned int c;
    unsigned int d;
} HELLO;

void more_fun ( unsigned int, unsigned int, HELLO );

HELLO b;

void fun ( void )
{
    HELLO a;

    a.a[7]=5;
    a.b=6;
    a.c=7;
    a.d=8;
    more_fun(1,2,a);
    a.c=12;
    more_fun(66,77,a);
    b.c=12;
    more_fun(44,55,b);
}

然后编译反汇编

arm-none-eabi-gcc -O2 -c fun.c -o fun.o
arm-none-eabi-objdump -D fun.o

看看你得到了什么......很明显我一直在修改/添加东西以查看实际发生的情况。我没想到会这样,但它实际上是按价值传递的。我期待它制作一个可销毁的副本,并传递一个指向它的指针。 arm abi 首先使用 r0-r3 (如果使用所有这些,则有规则),然后在这些用完时进入堆栈。在上述情况下,前两个单词在 r2 和 r3 中,另外 31 个在堆栈中,a 结构也在堆栈中用于 fun 函数,我得到的结构足够大,可以强制它使用 memcpy 而不是 a ldm/stms 系列。

但你真正的问题可能是什么。如果手臂寄存器不能保存“指针”,那么架构将如何工作?如果/当地址空间超过单个通用寄存器(迄今为止,该架构有基于寄存器的寻址指令),则将一些方案添加到架构中。分页非常流行,一些新的寄存器/地址保存高地址位或基地址,寄存器现在是偏移量而不是整个地址。到目前为止,我还没有在手臂上看到过这样的东西。

正如已经回答的那样,由于这是一个 C 指针问题而不是架构问题,因此答案严格在于 C 是如何为该架构实现的。并且使用像保证这样的词肯定会最终失败。除了架构是否会随着时间而改变之外,您没有理由假设任何两个编译器或同一编译器的任何两个版本或编译都会产生与另一个相同的结果,所以今天它可能会做一件事,但它可以/明天会做点什么。

至于你传递一个指针问题...试试看 您正在使用输出的编译器。然后,如果您只是想知道或认为编译器有问题,请检查已发布的调用约定,如果有(在本例中存在),并查看编译器生成的代码与调用约定的比较。如果您的测试 C 代码是真实的或真实的,那么这就是编译器传递该指针的方式,并且您的 asm 不仅可以而且必须从传递该指针的位置检索该指针,以便 c/asm 接口工作。

当然,如果你在你的 asm/c 接口中做任何太复杂的事情,它会增加你下次 abi 更改你的 c/asm 接口时失败的几率。 asm/c 接口总是存在一定程度的失败机会。对于任何平台上的任何编译器(除非您是编译器/汇编器的作者并确保您永远不会更改它)。

你几乎总是可以做的就是“试一试”,看看编译器如何生成调用,数据在哪里,如果编译器没有错误,那么它遵循约定,你可以依赖它(对于那个编译器),如果它有问题,那么由于有问题的编译器,无论有没有 asm,你的程序都可能会失败。

【讨论】:

    猜你喜欢
    • 2019-04-10
    • 1970-01-01
    • 2014-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多