【发布时间】:2016-06-03 00:20:47
【问题描述】:
假设我需要写入零地址(例如,我在那里有 mmapped 的东西并想访问它,无论出于何种原因,包括好奇),并且该地址在编译时是已知的。以下是我可以想到的一些变体来获取指针,其中一个有效,另外三个无效:
#include <stdint.h>
void testNullPointer()
{
// Obviously UB
unsigned* p=0;
*p=0;
}
void testAddressZero()
{
// doesn't work for zero, GCC detects it as NULL
uintptr_t x=0;
unsigned* p=(unsigned*)x;
*p=0;
}
void testTrickyAddressZero()
{
// works, but the resulting assembly is not as terse as it could be
unsigned* p;
asm("xor %0,%0\n":"=r"(p));
*p=0;
}
void testVolatileAddressZero()
{
// p is updated, but the code doesn't actually work
unsigned*volatile p=0;
*p=0; // because this doesn't dereference p! // EDIT: pointee should also be volatile, then this will work
}
我用这个编译
gcc test.c -masm=intel -O3 -c -o test.o
然后objdump -d test.o -M intel --no-show-raw-insn 给我(此处跳过对齐字节):
00000000 <testNullPointer>:
0: mov DWORD PTR ds:0x0,0x0
a: ud2a
00000010 <testAddressZero>:
10: mov DWORD PTR ds:0x0,0x0
1a: ud2a
00000020 <testTrickyAddressZero>:
20: xor eax,eax
22: mov DWORD PTR [eax],0x0
28: ret
00000030 <testVolatileAddressZero>:
30: sub esp,0x10
33: mov DWORD PTR [esp+0xc],0x0
3b: mov eax,DWORD PTR [esp+0xc]
3f: add esp,0x10
42: ret
这里 testNullPointer 显然有 UB,因为它根据定义取消引用空指针。
testAddressZero 的原则将给出除0 以外的任何地址的预期代码,例如1,但对于零,GCC 似乎检测到地址零对应于空指针,因此也会生成 UD2。
asm 获取零地址的方式肯定会抑制编译器的检查,但其代价是即使testAddressZero 的原理可能成功(即如果不是 UD2 和类似的陷阱,每个拱上的相同平面内存模型。此外,代码看起来不像上述两个变体那样简洁。
volatile 指针的方式似乎是最好的,但是这里生成的代码由于某种原因似乎没有取消引用地址,所以它也被破坏了。
现在的问题:如果我以 GCC 为目标,我如何才能无缝访问零地址而不会出现任何陷阱或 UB 的其他后果,并且无需编写汇编代码?
【问题讨论】:
-
如果你有
mmap-ed,那么大概你已经有一个“有效”的指针,这意味着你不需要使用这些技巧中的任何。 -
@alk - 似乎是 same 问题,在 2 小时前问了所有问题。这是怎么回事?
-
您不能取消引用空指针。您的运行时系统必须禁止此类 mmap-ing,或者您的编译器/库必须将空指针实现为非全零位模式(是的,此类系统确实存在)。如果两者都不是,那么你就在 UB 领域,并帮助你
deity[choice]。 -
您应该编辑您的问题以解释为什么您需要取消引用 0 地址。真正有用的情况很少。