1 c assembly gcc x86-64 undefined-behavior
我有以下一段代码:
typedef struct {
int x;
int y;
int z;
int w;
} s32x4;
s32x4
f() {
s32x4 v;
v.x = 0
return v;
}
Run Code Online (Sandbox Code Playgroud)
生成(gcc -O2):
f:
xor eax, eax
xor edx, edx ; this line is questionable
ret
Run Code Online (Sandbox Code Playgroud)
其中 clang 输出(clang -O2):
f: # @f
xor eax, eax
ret
Run Code Online (Sandbox Code Playgroud)
GCC插入XOR那里?您读取了一个部分未初始化的 struct 对象以返回它,即使调用者不使用返回值,它也(可以说是)当场未定义行为。
16 字节结构在 x86-64 System V ABI 中的 RDX:RAX 中返回(任何更大的结构,它将通过让调用者传递指向返回值对象的指针来返回)。 GCC 将未初始化的部分归零,clang 将留下任何垃圾。
GCC 喜欢在任何可能存在将错误依赖耦合到某些东西中的风险时打破依赖关系。(例如,pxor xmm0,xmm0在使用设计不当的 之前cvtsi2sd xmm0, eax)。Clang 更“激进”地将其排除在外,有时即使这样做只有很小的代码大小好处,例如使用mov al, 1代替mov eax,1,或mov al, [rdi]代替movzx eax, byte ptr [rdi])
您所看到的最简单的形式是返回一个未初始化的 plainint,
GCC 和 clang 代码生成之间的区别相同:
int foo(){
int x;
return x;
}
Run Code Online (Sandbox Code Playgroud)
(神马)
# clang 11.0.1 -O2
foo:
# leaving EAX unwritten
ret
# GCC 10.2 -O2
foo:
xor eax, eax # return 0
ret
Run Code Online (Sandbox Code Playgroud)
这里 clang "gets to" 省略了整个指令。当然,这是未定义的行为(读取未初始化的对象),因此该标准实际上允许任何事情,包括ud2(保证引发非法指令异常),或者甚至忽略ret此代码路径无法访问的假设,即函数永远不会叫做。或者返回0xdeadbeef,或者调用任何其他函数,如果你有一个恶意的 DeathStation 9000 C 实现。
| 归档时间: |
|
| 查看次数: |
178 次 |
| 最近记录: |