我试图得到关于COM在x64机器上的行为方式的明确答案.在调度x64机器上的COM接口时,Windows是否使用普通的x64调用约定(假设COM实现是64位)?具体来说,我动态生成我的vtbl条目以指向在运行时动态加载的一大块程序集.这个程序集需要知道如何从调用它的人那里正确地获取参数.因此,我想知道COM是否使用标准x64调用约定(非常快速调用)设置对程序集的调用.
这些是什么?我作为开发人员如何影响这些?
我正在开发一个库,允许其用户(驻留在同一进程中的其他库)交换数据缓冲区和流.该库必须可以从MSVC和mingw代码中使用(更多兼容性不会受到影响,但并非严格必要).为了实现这一点,核心功能应该从一个小的,与编译器兼容的接口提供,以后可以通过使用客户端代码编译的便利层来隐藏.
该库的一个具有挑战性的方面是它必须是可扩展的,以便客户端可以提供自己的缓冲区和流实现,但核心库接口必须在发布后保持稳定.如果您对进一步的背景感兴趣,可以在论坛帖子讨论中阅读.
我试图了解编译器之间二进制兼容性的问题,但由于我是这个主题的新手,我会对我的结果发表评论.我在这里的标准定义的行为(结构可能会失败那个测试),只能在MinGW和MSVC之间的相容性不感兴趣,也许其他的编译器,如果方便可行的.
特别是结构是否兼容?它们统一由函数指针组成,因此我认为填充不会成为问题.另外,这里需要stdcall约定,还是cdecl也能正常工作?我可以不指定它,因为两个编译器都默认为cdecl吗?我是不是该?这就是我现在所拥有的:
#include <stdint.h>
typedef struct {
uint32_t (__stdcall *read)(void*, uint8_t*, uint32_t);
void (__stdcall *write)(void*, const uint8_t*, uint32_t);
uint32_t (__stdcall *getBytesLeft)(void*);
uint8_t (__stdcall *destroy)(void*);
} SharedStreamInterface;
typedef struct {
uint32_t (__stdcall *read)(void*, uint8_t*, uint32_t);
void (__stdcall *write)(void*, const uint8_t*, uint32_t);
uint32_t (__stdcall *getBytesLeft)(void*);
uint8_t (__stdcall *destroy)(void*);
uint32_t (__stdcall *getreadpos)(void*);
uint32_t (__stdcall *getwritepos)(void*);
uint32_t (__stdcall *getlength)(void*);
void (__stdcall *setreadpos)(void*, uint32_t);
void (__stdcall *setwritepos)(void*, uint32_t);
void (__stdcall *setlength)(void*, uint32_t);
} SharedBufferInterface;
extern "C" {
// Functions …Run Code Online (Sandbox Code Playgroud) compiler-construction mingw calling-convention binary-compatibility visual-c++
我正在为第三方应用程序编写DLL.主要的软件工程师提到应用程序使用__cdecl(/ Gd)调用约定.我需要确保使用它.
另外,第三方提供了一个C++ DLL骨架,它导出如下函数:
#ifdef _EXPORTING
#define DECLSPEC __declspec(dllexport)
#else
#define DECLSPEC __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C" {
#endif
DECLSPEC int ICD_Create(char* id);
....
....
Run Code Online (Sandbox Code Playgroud)
我有点困惑.为什么使用__declspec约定而不是__cdedl导出函数?__declspec支持_cdecl吗?
谢谢.
据我所知,调用约定依赖于编译器和体系结构.但C和C++调用约定之间是否存在明显差异?
当我阅读Linux内核源码时,我遇到了这段代码:
__visible void __irq_entry smp_apic_timer_interrupt(struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
entering_ack_irq();
local_apic_timer_interrupt();
exiting_irq();
set_irq_regs(old_regs);
}
Run Code Online (Sandbox Code Playgroud)
该函数smp_apic_timer_interrupt()有一个参数.调用此函数是通过一段汇编语言代码:
ENTRY(apic_timer_interrupt)
RING0_INT_FRAME;
ASM_CLAC;
pushl_cfi $~(0xef);
SAVE_ALL;
TRACE_IRQS_OFF
movl %esp,%eax;
call smp_apic_timer_interrupt; // <------call high level C function
jmp ret_from_intr;
CFI_ENDPROC;
ENDPROC(apic_timer_interrupt)
Run Code Online (Sandbox Code Playgroud)
我无法弄清楚高级C函数如何smp_apic_timer_interrupt()获取其参数(通过哪个寄存器)?
x86 assembly calling-convention linux-kernel interrupt-handling
众所周知,一些没有非平凡复制的小结构,没有非平凡的dtor在寄存器中传递.
引用ARM程序调用标准:
大于32位的基本类型可以作为参数传递给函数调用,或者作为函数调用的结果返回.当这些类型在核心寄存器中时,以下规则适用:双字大小类型在两个连续寄存器中传递(例如,r0和r1,或r2和r3).寄存器的内容就好像该值是通过单个LDM指令从存储器表示加载的.
事实上,我可以轻松地用铿锵声证实这一点.然而,gcc为这样一个简单的代码片段发出了大量的内存加载和存储:
struct Trivial {
int i1;
int i2;
};
int foo(Trivial t)
{
return t.i1 + t.i2;
}
Run Code Online (Sandbox Code Playgroud)
$ clang++ arm.cpp -O2 -mabi=aapcs -c -S && cat arm.s
add r0, r0, r1
bx lr
Run Code Online (Sandbox Code Playgroud)
$ g++ arm.cpp -O2 -mabi=aapcs -c -S && cat arm.s
sub sp, sp, #8
add r3, sp, #8
stmdb r3, {r0, r1}
ldmia sp, {r0, r3}
add r0, r0, r3
add sp, sp, #8
bx lr
Run Code Online (Sandbox Code Playgroud)
我正在使用由ArchlinuxARM发行版提供的gcc和clang,在raspberry pi 2(gcc 5.2)上运行,但我也用基于gcc的交叉编译器再现它.
当通过P/Invoke通过显式64位.NET应用程序与64位本机库交互时,属性中的CallingConvention属性是否DllImport被有效忽略?
我问这个是因为在"传统"x86上你必须指定调用者或被调用者清理堆栈变量的方式(以及函数本身如何使用某些CPU寄存器等); 但据我了解,x64只有一个约定__fastcall(__vectorcall尽管最近添加了).
那么__fastcall,无论你为CallingConvention属性设置什么,CLR都会继续使用x64约定来编组函数调用?
在x64上,每个PUSH指令是否会推送8个字节的倍数?如果没有,它会推动多少?
此外,每个函数参数消耗多少堆栈空间?
我不明白不在 RAX 中传递参数有什么好处,因为返回值在 RAX 中,无论如何它都会被被调用者破坏。
有人可以解释一下吗?