0 c assembly x86-64 calling-convention
汇编:
.intel_syntax noprefix
.global Foo
Foo:
mov ax, 146
ret
Run Code Online (Sandbox Code Playgroud)
主要.c:
#include <stdio.h>
extern int Foo(void);
int main(int argc, char** args){
printf("Asm returned %d\n", Foo());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在我编译并链接:
(compiler name) -c asm.s -o asm.o
(compiler name) asm.o main.c -o main
./main
Run Code Online (Sandbox Code Playgroud)
我正在使用 Windows 和 LLVM 的 x64 windows 二进制文件。
GCC 打印 146(如预期),但 clang 生成的代码打印随机值,为什么?在 Windows 上使用 gdb 调试 clang 二进制文件显然存在一些问题,因此我无法提供任何 gdb 日志。GCC 二进制文件正在执行我期望的操作,但不是 Clang。
您想要mov eax, 146匹配 32 位或更宽的返回类型。
请参阅AX、AH、AL 如何映射到 EAX?- 不幸的是,将 EAX 零扩展写入 RAX,但 8 位和 16 位部分寄存器保留了仅合并的传统 386 行为。
您告诉编译器它返回一个int(x86 和 x86-64 调用约定中的 4 个字节),但您只修改了 EAX/RAX 的低 16 位,将现有垃圾留在调用者的32 位返回值的高 16 中int在 EAX 中查找。
sizeof(int) == 4在 x86 的所有 32 位和 64 位调用约定中,因此它以 EAX 形式返回。
16 位 AX 是 C 语言中的“short或” unsigned short。(或int16_t/ uint16_t)
如果将返回类型声明为short,则调用者只会在 AX 中查找,忽略窄返回值的调用约定所要求的高垃圾(与窄于 32 位的参数不同,作为至少 x86-64 系统的不成文扩展) V,也许还有 Windows x64,clang 依赖于此)。
另请参阅如何从 GCC/clang 程序集输出中删除“噪音”?了解如何查看编译器生成的代码以查看此类函数的正确 asm。
返回值的低 16 位是146。如果您以十六进制查看,您可以看到这一点,例如xxxx0092。
也许 GCC 碰巧在 EAX 的高半部分已经为零的情况下调用它,但 clang 却没有。由于调用者中的代码不同,GCC 也可能使用 RAX 来获取非小值。对于 C 调用者和您碰巧测试的优化选项来说,这只是运气。
C 等效项int retval; memcpy(&retval, &tmp, 2)除了使用寄存器之外,不替换 的高半部分中未初始化的垃圾retval。
| 归档时间: |
|
| 查看次数: |
86 次 |
| 最近记录: |