assembly 为什么这段代码打印clang和gcc编译的不同值?

v1l68za4  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(111)

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。

jq6vz3qz

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中的shortunsigned 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高半部分中未初始化的垃圾。

相关问题