我正在编写一些代码,这些代码已经在我的Rust项目中被组装.我和一位同事正在讨论呼叫约定,我一直很难找到明确的资源.

据我所知,Rust调用约定是未定义的.所以,当调用到汇编中时,我应该使用C调用约定来获得已知的内容.

我在三个职能部门之间工作:

unsafe extern "C" fn f(a) -> b是一个只从Rust调用的裸函数.它完全是汇编的,假设参数和返回值是像C一样完成的(在堆栈上并放入% eax). 因为它被标记为C并从Rust调用,编译器将确保无论在哪里调用它,都将使用C调用约定.

unsafe extern "C" fn g()也是一个赤裸裸的功能.它从来没有被称为Rust.只有有时f返回这个函数(即fret将弹出g的地址). 虽然这不是一个真正的C函数(我知道它是从f调用的,并立即使用寄存器而不是f个左值),它总是会回调到rust函数.本质上,所有这些都是将参数推到堆栈上,然后将call推到h(通过使用sym h).

unsafe fn h是这里的最后一个功能.它只在内联程序集中从g经由sym调用. 因此,h应该期望用C约定调用,但可以用Rust约定返回(实际上h永远不会返回).

我的问题是:

使用我的C函数g中的callsym h是否提醒Rust编译器在h中始终使用C调用约定?

我之前已经将h标记为extern "C",尽管它是纯铁 rust 代码.在我看来,这将确保编译器尊重h‘S参数是C风格的.但是,代码似乎是双向工作的.我不希望在future Rust决定以不同的方式(并且不正确地)将调用优化为h时,这一点会改变.

推荐答案

在内联汇编中使用call并不能保证函数将使用C调用约定(事实上,编译器根本不了解内联汇编).你确实需要使用extern "C".使用Rust调用约定可以工作(有时调用约定是等价的),但不能保证.

如果你传递一个指向h()的函数指针,它们也必须是extern "C",即使它们不是从程序集调用的.据我所知,没有关于Rust调用约定函数的ABI的保证,所以你甚至不能传递它们,这就是为什么编译器会警告你.

如果h()没有返回,您可以将其标记为具有返回类型!,这将使编译器验证它确实没有返回(如果从Rust调用,也将允许它利用此信息).

C++相关问答推荐

从C函数调用asm函数时生成错误的BLX指令(STM32H753上的gcc)

C:gcc返回多个错误定义,但msvc—不""'

如果我释放其他内容,返回值就会出错

如何在C中使printf不刷新标准输出?

调用mProtection将堆栈上的内存设置为只读,直接导致程序SIGSEGV

对于C中给定数组中的每个查询,如何正确编码以输出给定索引范围(1到N)中所有数字的总和?

CC2538裸机项目编译但不起作用

用gcc-msse 2编译的C程序包含AVX 1指令

getline()从c中的外部函数传递指针时输出null

在编写代码时,Clion比vscode有更多的问题指示器

Cairo STM32MP1 cairo_Surface_WRITE_TO_PNG始终返回CAROLIO_STATUS_WRITE_ERROR

Zlib:解压缩大文件导致";无效代码长度设置";错误

Valgrind用net_pton()抱怨

Makefile无法将代码刷新到ATmega328p

如何解释数组中的*(ptr)和*(ptr+2)?

在NASM中链接Linux共享库时出错-';将R_ X86_64_;foo';

为什么INT_MIN是在c语言的头文件limits.h中定义的(-INT_MAX-1)而不是直接使用-2147483648

使用fread()函数读取txt文件

UEFI 应用程序中的计时器回调仅在 AMI BIOS 中挂起

如何让 unlinkat(dir_fd, ".", AT_REMOVEDIR) 工作?