首页
社区
课程
招聘
问一下,VC++里什么语句会被编译成下面的指令:(解决了,strlen函数)
发表于: 2006-10-11 17:22 7503

问一下,VC++里什么语句会被编译成下面的指令:(解决了,strlen函数)

2006-10-11 17:22
7503

问一下,VC++里什么语句会被编译成下面的指令序列:

xor         eax, eax
lea         edi, offset szString
xor         ecx, ecx
dec         ecx
repnz scas  byte ptr es:[edi]
not         ecx
dec         ecx

这是汇编里很常用的一个求字符串szString长度的片段,但为什么会出现在VC++编译过的程序里呢,它究竟是由C语言的什么语句转换过来的呢?(我的第一感是使用了strlen函数,但马上又否定了这个想法――如果真是用这个函数,那么反汇编里只会出现指向这个导入函数的一个call调用。)


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (12)
雪    币: 398
活跃值: (343)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
2
"如果真是用这个函数,那么反汇编里只会出现指向这个导入函数的一个call调用"

这句是谁说的?
2006-10-11 17:28
0
雪    币: 433
活跃值: (176)
能力值: ( LV13,RANK:1250 )
在线值:
发帖
回帖
粉丝
3
难道是内联函数,但库函数怎么内联?
2006-10-11 17:55
0
雪    币: 398
活跃值: (343)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
4
这种问题在VC下写个strlen, 编译一下就知道了
不能像gkend那样想当然
2006-10-11 17:57
0
雪    币: 390
活跃值: (707)
能力值: ( LV12,RANK:650 )
在线值:
发帖
回帖
粉丝
5
也许是strlen的内联汇编版?
2006-10-11 17:58
0
雪    币: 179
活跃值: (131)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
6
等待楼主编译的结果
2006-10-11 17:59
0
雪    币: 398
活跃值: (343)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
7
忘了说了,用release编译
2006-10-11 18:03
0
雪    币: 433
活跃值: (176)
能力值: ( LV13,RANK:1250 )
在线值:
发帖
回帖
粉丝
8
Thanks

我用的是公家的机器,权限不够,安装不了编译环境,不然我还可以解决更多事情
2006-10-11 18:11
0
雪    币: 325
活跃值: (97)
能力值: ( LV13,RANK:530 )
在线值:
发帖
回帖
粉丝
9
VC7以后函数 默认内联
2006-10-11 21:58
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
10
string  equ     [esp + 4]

        mov     ecx,string              
        test    ecx,3                  
        je      short main_loop

str_misaligned:
   
        mov     al,byte ptr [ecx]
        add     ecx,1
        test    al,al
        je      short byte_3
        test    ecx,3
        jne     short str_misaligned

        add     eax,dword ptr 0        

        align   16                     
redundant

main_loop:
        mov     eax,dword ptr [ecx]     
        mov     edx,7efefeffh
        add     edx,eax                     
        xor     eax,-1
        xor     eax,edx
        add     ecx,4
        test    eax,81010100h                           
        je      short main_loop
     
        mov     eax,[ecx - 4]
        test    al,al                 
        je      short byte_0
        test    ah,ah                  
        je      short byte_1
        test    eax,00ff0000h           
        je      short byte_2
        test    eax,0ff000000h         
        je      short byte_3
        jmp     short main_loop                                               

byte_3:
        lea     eax,[ecx - 1]
        mov     ecx,string
        sub     eax,ecx
        ret
byte_2:
        lea     eax,[ecx - 2]
        mov     ecx,string
        sub     eax,ecx
        ret
byte_1:
        lea     eax,[ecx - 3]
        mov     ecx,string
        sub     eax,ecx
        ret
byte_0:
        lea     eax,[ecx - 4]
        mov     ecx,string
        sub     eax,ecx
        ret

strlen  endp

        end

前阵子看熊力的《WINDOWS用户态程序高效排错》的时候,在他BLOG(blogs.msdn.com/lixiong)里的用户反馈里看到的,据说是系统STRLEN的汇编代码

下面是熊力对这段代码的分析:
“这里对一个DWORD (EAX)的判断方法是:

1. 对EAX+0x7efefeff
2. 对EAX取反
3. 把1和2的结果作XOR,然后跟0x81010100h作test运算

研究了好久,理解如下:

问题的关键点在于,当且仅当EAX四个byte都不为0的时候,运算结果会是下面的pattern:
0??? ???0 ???? ???0 ???? ???0 ???? ????

分别解释如下:

如果第一个byte为0, 考虑第二个byte的最后一个bit。不管这个bit是0还是1,计算公式是:
(x+0) XOR (!x) =x xor !x=0
如果第一个byte不为0,肯定产生进位,考虑第二个byte的最后一个bit。不管这个bit是0还是1,计算公式是:
(x+1)XOR(!x)=!x xor !x=1

这就是上面0??? ???0 ???? ???0 ???? ???0 ???? ????第二个byte的第一个bit是0的来历

同理,第二,三,四个byte中的的第一个bit的0也是在前面所有的byte都不为0的时候才会出现,否则就会出现至少一个1

换句话说,上面的代码无法区分最高一个byte最高bit为0,其他bit为1的情况。这是这种算法的一个死穴。当出现比如0x80112233这样的DWORD的时候,test    eax,81010100h 计算的结果跟0x00112233一样。当然最后的结果不会有问题,因为byte_3 -- byte_0里面会再次作判断。所以,如果用一连串的0x80112233作为字符串内容,strcpy的效率会大大下降

对于一个DWORD,导致这个因素的可能是
2^24/2^32=1/2^8=1/256

算是比较罕见了

从逻辑上说,最高byte是无法区分本身为1,或者是低byte进位的情况。所以单独的DWORD是无法判断出所有情况的,当前的做法已经算很有想法的了
2006-10-23 14:19
0
雪    币: 254
活跃值: (15)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
11
编译器优化的结果,如果你在VC项目设置的优化选项选为disable,那么就会出现你想看到的strlen.
2006-10-23 15:21
0
雪    币: 254
活跃值: (15)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
抱歉,我的理解有些偏差.

简单测试了一下
#include <stdio.h>
#include <string.h>

int main(int argc,char *argv[])
{
        int str_length;
        char str[100] = "pediypediypediypediypediypediypediy";

        str_length = strlen(str);
        printf("string length = %d\n", str_length);
        return 0;
}

VC6,Release模式编译,IDA的分析结果是:
        sub     esp, 64h
        mov     ecx, 9
        xor     eax, eax
        push    esi
        push    edi

;复制字符到栈

        mov     esi, offset aPediypediypedi ; "pediypediypediypediypediypediypediy"
        lea     edi, [esp+6Ch+var_64]
        rep movsd

;数组初始化
        mov     ecx, 10h
        lea     edi, [esp+6Ch+var_40]
        rep stosd

;以下就是楼主贴的代码
        lea     edi, [esp+6Ch+var_64]
        or      ecx, 0FFFFFFFFh
        repne scasb
        not     ecx
        dec     ecx
        push    ecx
        push    offset aStringLengthD   ; "string length = %d\n"
        call    ds:printf
        add     esp, 8
        xor     eax, eax
        pop     edi
        pop     esi
        add     esp, 64h
        retn

可以看到strlen影都没见着,所以可以肯定,strlen被优化了.
2006-10-24 09:08
0
雪    币: 254
活跃值: (15)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
楼主贴出的代码是strlen被VC6最大速度优化的结果.
2006-10-24 09:21
0
游客
登录 | 注册 方可回帖
返回
//