分享一则编译器的趣事,内容取自【日】川合秀实的《30天自制操作系统》
unsigned int memtest_sub(unsigned int start, unsigned int end)
{
unsigned int i, *p, old, pat0 = 0xaa55aa55, pat1 = 0x55aa55aa;
for (i = start; i <= end; i += 4) {
p = (unsigned int *) i;
old = *p; /* 先记住修改前的值 */
*p = pat0; /* 试写 */
*p ^= 0xffffffff; /* 反转 */
if (*p != pat1) { /* 检查反转结果 */
not_memory:
*p = old;
break;
}
*p ^= 0xffffffff; /* 再次反转 */
if (*p != pat0) { /* 检查值是否恢复 */
goto not_memory;
}
*p = old; /* 恢复为修改前的值 */
}
return i;
}
这个程序所做的是:调查从start地址到end地址的范围内,能够使用的内存的末尾地址。要做 的事情很简单。首先如果p不是指针,就不能指定地址去读取内存,所以先执行“p=i;”。紧接着 使用这个p,将原值保存下来(变量old)。接着试写0xaa55aa55,在内存里反转该值,检查结果是 否正确①。如果正确,就再次反转它,检查一下是否能回复到初始值。最后,使用old变量,将内 存的值恢复回去。……如果在某个环节没能恢复成预想的值,那么就在那个环节终止调查,并报 告终止时的地址。
以上程序是笔者对0x00400000~0xbfffffff范围的内存进行检查,关于反转,笔者用XOR运算来实现,其运算符是“”。“*p = 0xffffffff;”是“*p = *p^0xffffffff;” 的省略形式。
然后就出问题了,笔者描述很清晰,就截图吧:
哈哈哈哈!!用户肯定会说:“这编译器真好,速度特别快!”
这个故事告诉我们汇编真的很重要,笔者用汇编语言改写的程序如下:
_memtest_sub: ; unsigned int memtest_sub(unsigned int start, unsigned int end)
PUSH EDI ; (由于还要使用EBX, ESI, EDI)
PUSH ESI
PUSH EBX
MOV ESI,0xaa55aa55 ; pat0 = 0xaa55aa55;
MOV EDI,0x55aa55aa ; pat1 = 0x55aa55aa;
MOV EAX,[ESP+12+4] ; i = start;
mts_loop:
MOV EBX,EAX
ADD EBX,0xffc ; p = i + 0xffc;
MOV EDX,[EBX] ; old = *p;
MOV [EBX],ESI ; *p = pat0;
XOR DWORD [EBX],0xffffffff ; *p ^= 0xffffffff;
CMP EDI,[EBX] ; if (*p != pat1) goto fin;
JNE mts_fin
XOR DWORD [EBX],0xffffffff ; *p ^= 0xffffffff;
CMP ESI,[EBX] ; if (*p != pat0) goto fin;
JNE mts_fin
MOV [EBX],EDX ; *p = old;
ADD EAX,0x1000 ; i += 0x1000;
CMP EAX,[ESP+12+8] ; if (i <= end) goto mts_loop;
JBE mts_loop
POP EBX
POP ESI
POP EDI
RET
mts_fin:
MOV [EBX],EDX ; *p = old;
POP EBX
POP ESI
POP EDI
RET