GCC 忽略 cdecl?
GCC ignores cdecl?
我在 Linux x86 上使用 gcc。
我的程序将指向 C 函数的指针导出到 LLVM JIT 函数。调用约定是 cdecl。它在 Windows 上的 MingW 上运行良好。但是在 linux x86 平台上发生了奇怪的事情。导出的C函数的反汇编是这样的:
push ebp
mov ebp,esp
push ebx
sub esp,0x34
mov eax,0xfffffffc
mov eax,DWORD PTR gs:[eax]
mov eax,DWORD PTR [eax+0x1c]
mov eax,DWORD PTR [eax]
mov eax,DWORD PTR [eax+0x28]
mov edx,DWORD PTR [ebp+0xc]
shl edx,0x4
add eax,edx
mov DWORD PTR [ebp-0xc],eax
mov edx,DWORD PTR ds:0x8e49940
mov ebx,DWORD PTR [ebp+0x8]
lea eax,[ebp-0x20]
mov ecx,DWORD PTR [ebp-0xc]
mov DWORD PTR [esp+0xc],ecx
mov ecx,DWORD PTR [ebp+0x10]
mov DWORD PTR [esp+0x8],ecx
mov DWORD PTR [esp+0x4],edx
mov DWORD PTR [esp],eax
call 0x8090f6f <SoCreateArray(DVM_VirtualMachine_tag*, int, DVM_TypeSpecifier_tag*)>
sub esp,0x4
mov eax,DWORD PTR [ebp-0x20]
mov edx,DWORD PTR [ebp-0x1c]
mov DWORD PTR [ebx],eax
mov DWORD PTR [ebx+0x4],edx
mov eax,DWORD PTR [ebp+0x8]
mov ebx,DWORD PTR [ebp-0x4]
leave
ret 0x4
C 源代码在这里:
DVM_ObjectRef SoNewArray(BINT ty,BINT dim)
{
DVM_TypeSpecifier *type
= &curthread->current_executable->executable->type_specifier[ty];
DVM_ObjectRef barray;
barray = SoCreateArray(curdvm, dim, type);
return barray;
}
注意反汇编代码的最后一条指令是"ret 0x4",意思是它自己清理栈的函数,不是cdecl函数!更重要的是,即使我这样声明 C 函数:
DVM_ObjectRef SoNewArray(BINT ty,BINT dim) 属性((cdecl));
结果是一样的。也许 GCC 优化了我的代码,并自动使用 stdcall,忽略了调用约定?
我的 GCC 命令是
gcc -Wall -fexceptions -Wfatal-errors -g
我的 64 位 gcc 5.4.0 linux 需要 __attribute__((__cdecl__))
:
#ifdef __GNUC__
#define _cdecl __attribute__((__cdecl__))
#endif
//sometime later
int _cdecl AddItemVar(void *AContext,void *AFun,long ACfg1,...);
代码最初来自 __BORLANDC__,但也使用 __WATCOMC__ 和 __MSVC__ 编译。编译产生以下警告:
../../LIB/DevOS.C/DevOS.h:242:64: warning: ‘cdecl’ attribute
ignored [-Wattributes]
https://www.zhihu.com/question/38726507
这里是中国人"Whosebug""Zhihu"的回答。感谢
RednaxelaFX@知乎。我发现实际上这个问题的原因是函数 returns 是一个结构而不是基本类型。
看函数声明:
DVM_ObjectRef SoNewArray(BINT ty,BINT dim)
DVM_ObjectRef是一个结构。 x86 gcc在处理上面这样的一个函数时,实际上会生成:
DVM_ObjectRef SoNewArray(DVM_ObjectRef * ret,BINT ty,BINT dim)
查看有关 x86 调用约定的更多详细信息:
我在 Linux x86 上使用 gcc。 我的程序将指向 C 函数的指针导出到 LLVM JIT 函数。调用约定是 cdecl。它在 Windows 上的 MingW 上运行良好。但是在 linux x86 平台上发生了奇怪的事情。导出的C函数的反汇编是这样的:
push ebp
mov ebp,esp
push ebx
sub esp,0x34
mov eax,0xfffffffc
mov eax,DWORD PTR gs:[eax]
mov eax,DWORD PTR [eax+0x1c]
mov eax,DWORD PTR [eax]
mov eax,DWORD PTR [eax+0x28]
mov edx,DWORD PTR [ebp+0xc]
shl edx,0x4
add eax,edx
mov DWORD PTR [ebp-0xc],eax
mov edx,DWORD PTR ds:0x8e49940
mov ebx,DWORD PTR [ebp+0x8]
lea eax,[ebp-0x20]
mov ecx,DWORD PTR [ebp-0xc]
mov DWORD PTR [esp+0xc],ecx
mov ecx,DWORD PTR [ebp+0x10]
mov DWORD PTR [esp+0x8],ecx
mov DWORD PTR [esp+0x4],edx
mov DWORD PTR [esp],eax
call 0x8090f6f <SoCreateArray(DVM_VirtualMachine_tag*, int, DVM_TypeSpecifier_tag*)>
sub esp,0x4
mov eax,DWORD PTR [ebp-0x20]
mov edx,DWORD PTR [ebp-0x1c]
mov DWORD PTR [ebx],eax
mov DWORD PTR [ebx+0x4],edx
mov eax,DWORD PTR [ebp+0x8]
mov ebx,DWORD PTR [ebp-0x4]
leave
ret 0x4
C 源代码在这里:
DVM_ObjectRef SoNewArray(BINT ty,BINT dim)
{
DVM_TypeSpecifier *type
= &curthread->current_executable->executable->type_specifier[ty];
DVM_ObjectRef barray;
barray = SoCreateArray(curdvm, dim, type);
return barray;
}
注意反汇编代码的最后一条指令是"ret 0x4",意思是它自己清理栈的函数,不是cdecl函数!更重要的是,即使我这样声明 C 函数:
DVM_ObjectRef SoNewArray(BINT ty,BINT dim) 属性((cdecl));
结果是一样的。也许 GCC 优化了我的代码,并自动使用 stdcall,忽略了调用约定?
我的 GCC 命令是
gcc -Wall -fexceptions -Wfatal-errors -g
我的 64 位 gcc 5.4.0 linux 需要 __attribute__((__cdecl__))
:
#ifdef __GNUC__
#define _cdecl __attribute__((__cdecl__))
#endif
//sometime later
int _cdecl AddItemVar(void *AContext,void *AFun,long ACfg1,...);
代码最初来自 __BORLANDC__,但也使用 __WATCOMC__ 和 __MSVC__ 编译。编译产生以下警告:
../../LIB/DevOS.C/DevOS.h:242:64: warning: ‘cdecl’ attribute ignored [-Wattributes]
https://www.zhihu.com/question/38726507
这里是中国人"Whosebug""Zhihu"的回答。感谢 RednaxelaFX@知乎。我发现实际上这个问题的原因是函数 returns 是一个结构而不是基本类型。 看函数声明:
DVM_ObjectRef SoNewArray(BINT ty,BINT dim)
DVM_ObjectRef是一个结构。 x86 gcc在处理上面这样的一个函数时,实际上会生成:
DVM_ObjectRef SoNewArray(DVM_ObjectRef * ret,BINT ty,BINT dim)
查看有关 x86 调用约定的更多详细信息: