asm.s:
.intel_syntax noprefix
.global Foo
Foo:
mov ax, 146
ret
字符串
main.c:
#include <stdio.h>
extern int Foo(void);
int main(int argc, char** args){
printf("Asm returned %d\n", Foo());
return 0;
}
型
现在我编译并链接:
(compiler name) -c asm.s -o asm.o
(compiler name) asm.o main.c -o main
./main
型
我使用Windows和LLVM的x64 windows二进制文件。
GCC打印146(正如预期的那样),但是clang生成的代码打印一个随机值,为什么?在Windows上用gdb调试clang二进制文件显然有一些问题,所以我不能提供任何gdb日志。GCC二进制文件正在做我期望它做的事情,但不是Clang。
1条答案
按热度按时间jq6vz3qz1#
您希望
mov eax, 146
匹配32位或更宽的返回类型。参见 * How do AX, AH, AL map onto EAX? * -将EAX零扩展写入RAX,但不幸的是,8位和16位部分寄存器保留了传统的386合并行为。
你告诉编译器它返回一个
int
(x86和x86-64调用约定中的4个字节),**但你只修改了EAX/RAX的低16位,在调用者在EAX中查找的32位int
返回值的高16位中留下了现有的垃圾。x86的所有32位和64位调用约定中的
sizeof(int) == 4
,因此它在EAX中返回。16-位AX是C中的
short
或unsigned short
。(或int16_t
/uint16_t
)如果将返回类型声明为
short
,则调用者将只在AX中查找,忽略窄返回值的调用约定所要求的高垃圾(与窄于32位的args不同,它至少是x86-64 System V的不成文扩展,也可能是Windows x64,clang依赖于此)。另请参阅How to remove "noise" from GCC/clang assembly output?,了解如何查看编译器生成的代码,以查看此类函数的正确asm。
返回值的低16位是
146
。如果你用十六进制来看,你可以看到这一点,例如xxxx0092
。可能GCC碰巧在EAX的高半部分已经为零的情况下调用它,而clang没有。在调用者中使用不同的代码,GCC可能也使用了RAX来获得一个非小值。对于C调用者和你碰巧测试的优化选项来说,这只是运气。
C的等价物是
int retval;
memcpy(&retval, &tmp, 2)
,除了使用寄存器,不替换retval
高半部分中未初始化的垃圾。