返回寄存器中的结构 - GCC 中的 ARM ABI

发布于 2024-09-02 14:39:04 字数 332 浏览 3 评论 0原文

在 ARM ABI 文档中,我遇到了如下定义的函数:

__value_in_regs struct bar foo(int a, int b) {
    ...
}

但 GCC(4.3.3) 不允许这样做,我能找到的只是对某些 RealView 编译器的引用。 GCC 有什么办法可以做到这一点吗?

我尝试过 -freg-struct-return 但没有什么区别。由于它是 ABI,我无法更改原始程序,并且返回常规结构会破坏堆栈。

如果可以避免的话,我宁愿不使用汇编,因为否则没有必要。

谢谢!

In the ARM ABI documentation I come across functions defined like:

__value_in_regs struct bar foo(int a, int b) {
    ...
}

but GCC(4.3.3) doesn't allow it and all I could find are references to some RealView compiler.
Is there any way of doing this from GCC?

I have tried -freg-struct-return but it doesn't make a difference. As it is an ABI I can't change the original programs, and returning a regular struct mangles the stack.

I would rather not using assembly for this if avoidable as it isn't otherwise necessary.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

写给空气的情书 2024-09-09 14:39:04

根据请求作为答案发布:

如果您必须生成一个可以与您的编译器不支持的 ABI 一起使用的二进制文件,那么您就会遇到麻烦。在 C 中您无能为力。在这种情况下,您需要依靠汇编语言编程并执行必要的调用。有两种可能性:

  1. 从您的二进制文件调用另一个二进制文件的 ABI。
  2. 从其他二进制文件调用您的二进制文件的 ABI。

这两个问题的解决方法类似。要从代码中调用,您需要在程序集中创建填充函数,这些函数围绕调用约定进行调整以匹配外部 ABI,然后从那里调用外部函数。与 C 代码的区别在于,现在要进行外部调用,您需要调用内部汇编例程,它会执行外部调用所需的任何操作,然后将返回值以您的 C 代码可以理解的格式放回,然后返回。

为了支持从外部二进制文件调用您的代码,您可以做同样的事情,但方向相反。二进制文件的入口点将是一些小的汇编例程,它将外部 ABI 转换为 C 代码可以理解的格式,调用内部函数,然后将返回值放回到外部代码可以理解的格式,然后返回。

恐怕有时没有好的解决方案。

Posting as an answer by request:

If you have to generate a binary that will work with an ABI your compiler doesn't support, you're in for some trouble. There's nothing you can do in C. In this case, you'll need to fall back on assembly language programming and thunk the necessary calls. There are two possibilities:

  1. Calls from your binary into the other binary's ABI.
  2. Calls from the other binary into your binary's ABI.

Both of these problems are solved similarly. To call out from your code, you'll need to make shim functions in assembly that swizzle around the calling convention to match the external ABI, and then call the external functions from there. The difference to your C code is that now to make external calls, you call your internal assembly routine, and it does whatever it needs to to call out externally, then puts the return value back in a format your C code will understand, and returns.

To support calls from the external binary into your code, you do the same thing, but in reverse. The entry points to your binary will be little assembly routines that swizzle the external ABI into a format your C code can understand, call your internal function, then put the return values back into a format the external code understands, and return.

Sometimes there's just no good solution, I'm afraid.

潇烟暮雨 2024-09-09 14:39:04

您可以使用“long long”对两个寄存器执行此操作,如本页中给出的“ARM 体系结构的过程调用标准”链接中指定的那样。

long long test(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{
    long long ret;
    ret = a+b;
    ret <<= 32;
    ret |= c + d;
    return ret;
}

将被简单地编译为:

0002dbb8 <test>:
2dbb8:       1841            adds    r1, r0, r1
2dbba:       18d0            adds    r0, r2, r3
2dbbc:       4770            bx      lr

ret & 0xFFFFFFFFret>>调用函数中的 32 将被 r0 和 r1 无缝替换。

甚至可以通过使用“Containerized
矢量”:

typedef uint32_t uint32x4_t __attribute__ ((vector_size (16)));

uint32x4_t test2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{
    uint32x4_t ret = { a + 1, b + 2, c + 3, d + 4};
    // to access elements: ret[0], ret[1], ...
    return ret;
}

编译为:

0002dbb8 <test2>:
2dbb8:       3001            adds    r0, #1
2dbba:       3102            adds    r1, #2
2dbbc:       3203            adds    r2, #3
2dbbe:       3304            adds    r3, #4
2dbc0:       4770            bx      lr

请注意,它在上面的文档中被引用为 SIMD/NEON 功能,但我只是在 Thumb 模式下的 Cortex M0 上实现了它,没有 NEON 支持。

You can do it for two registers by using "long long", as specified in "Procedure Call Standard for the ARM Architecture" link given in this page.

long long test(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{
    long long ret;
    ret = a+b;
    ret <<= 32;
    ret |= c + d;
    return ret;
}

will be simply compiled as:

0002dbb8 <test>:
2dbb8:       1841            adds    r1, r0, r1
2dbba:       18d0            adds    r0, r2, r3
2dbbc:       4770            bx      lr

and the ret & 0xFFFFFFFF and ret >> 32 in your calling function will be replaced seamlessly by r0 and r1.

It is even possible to do it for registers r0 to r3 by using "Containerized
vectors":

typedef uint32_t uint32x4_t __attribute__ ((vector_size (16)));

uint32x4_t test2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{
    uint32x4_t ret = { a + 1, b + 2, c + 3, d + 4};
    // to access elements: ret[0], ret[1], ...
    return ret;
}

which is compiled as:

0002dbb8 <test2>:
2dbb8:       3001            adds    r0, #1
2dbba:       3102            adds    r1, #2
2dbbc:       3203            adds    r2, #3
2dbbe:       3304            adds    r3, #4
2dbc0:       4770            bx      lr

Note that it is referenced as a SIMD/NEON feature in the document above, but I just achieved it on a Cortex M0 in Thumb mode, without NEON support.

故事↓在人 2024-09-09 14:39:04

“ARM 架构的过程调用标准”
具体地说(第 5.4 节:结果返回):

“在 R0 中返回不大于 4 字节的复合类型。”

“大于 4 个字节的复合类型...存储在内存中,调用函数时作为额外参数传递的地址...。”

我知道某些 CPU 有几种不同的“标准”ABI。
但我的印象是,几乎所有 ARM 编译器都使用相同的 ABI。

您是否有任何证据表明 GCC 不使用此标准 ABI?

您介意发布一个链接,指向与此标准 ABI 不同的 ARM ABI 上的任何信息(调用方或被调用方或两者使用的 ABI)吗?

The "Procedure Call Standard for the ARM Architecture"
specifically says (section 5.4: Result Return):

"A Composite Type not larger than 4 bytes is returned in R0."

"A Composite Type larger than 4 bytes ... is stored in memory at an address passed as an extra argument when the function was called ... ."

I know that some CPUs have several different "standard" ABIs.
But I was under the impression that practically all compilers for the ARM used this same ABI.

Do you have any evidence that GCC doesn't use this standard ABI?

Would you mind posting a link to any information on a ABI for the ARM that is different from this standard ABI -- the ABI used by the caller, or the called, or both?

怪异←思 2024-09-09 14:39:04

我不确定这是否有效,但您可以尝试使用 pcs 功能属性

struct bar foo(int a, int b) __attribute__((pcs("aapcs")));
struct bar foo(int a, int b) {
    ...
}

I'm not sure if this will work, but you can try using the pcs function attribute:

struct bar foo(int a, int b) __attribute__((pcs("aapcs")));
struct bar foo(int a, int b) {
    ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文