CRT源码解析(二)字符串函数(1)
发表于 2006-05-07 03:55 AM 作者: tomato
CRT源码解析(二)
字符串函数(1)
0、代码:
c++ 代码:
1、memset
标准中声明:void *memset(void *s, int c, size_t n);
crt\src\intel\mamset.asm中的定义:
asm 代码:
调用约定是__cdecl
判断count == 0,直接把dst放在eax中,然后结束
判断value是0,且count>=100h(256),且__sse2_available不为0,则jmp _VEC_memzero(此时有al: value,ecx: dst, edx: count)
判断count若小于4,则循环edx次mov [edi], al直到count为0,把dst放在eax中,然后结束
计算dst到下一个dword boundary的距离,放在ecx中,如果不为0,sub edx, ecx,并且先将这ecx个用mov [edi], al放好
把eax的4个byte全设成al
把edx(调整dword boundary后的count)/4放在ecx中,把edx&3放在edx中
然后rep stosd,把ecx个int的数据放好
如果edx为0,结束,不然循环edx次mov [edi], al
tips:
1) 把eax的4个byte全设成al:
asm 代码:
2) _VEC_memzero
用了4个局部变量和3个参数:
形参[ebp+8],实参为dst
形参[ebp+C],实参为0,无用
形参[ebp+10],实参为count
调用约定是__cdecl
asm 代码:
3) fastzero_I
2个参数:
形参[dst]为[ebp+8],实参是dst
形参[len]为[ebp+C],实参是count的高25位(低7位为0)
1个局部变量:[ebp-4]
调用约定是__cdecl
asm 代码:
2、memchr
标准的声明:void *memchr(const void *s, int c, size_t n);
crt\src\intel\memchr.asm中的定义:
asm 代码:
调用约定是__cdecl
检查cnt是否为0
判断buf是不是4的倍数(aligned on 32bits),如果不是,先找前面几个byte直到4的倍数,
判断剩余需要比较的bytes是否大于4,如果小于4,判断最后的几个bytes
如果大于等于4,类似于memset的方法把ebx用chr填满,跳至main_loop_entry
主循环:
asm 代码:
byte_0~byte_3分别是找到后退出
3、memcpy & memmove
标准中声明:
void *memcpy(void * restrict s1,
const void * restrict s2,
size_t n);
void *memmove(void *s1, const void *s2, size_t n);
crt\src\intel\mamcpy.asm中的定义:
asm 代码:
调用约定是__cdecl
若dst <= src或dst >= src + count,则CopyUp,否则,CopyDown
1)CopyUp
先判断若count >= 256且__sse2_available != 0且dst - src整除16,则调用_VEC_memcpy
否则做Dword_align:
判断edi是否align到dword了
a. 如果已经完成
ecx记录count / 4,edx记录count % 4
判断若ecx < 8,手写从esi到edi的复制过程(至于吗,难道rep movsd的效率不如手写?或是为了便于cpu乱序执行?)
否则rep movsd,然后调用TrailUpVec复制剩余的bytes
b.如果没有完成
若count小于4直接调用TrailUpVec复制剩余的bytes
否则调用LeadUpVec
UnwindUpVec长度为8,其实就是手写index个dword的复制,然后把剩下的调用TrailUpVec
LeadUpVec长度为3(注意没有LeadUp0),作用是先将dst的align所差的几个bytes(就是4 - dst % 4)复制过去,然后再rep movsd,最后将count中不足4的剩余bytes调用TrailUpVec复制剩余的bytes
TrailUpVec长度为4,作用是拷贝最后的index个字符,然后退出
2) CopyDown,情况完全一样,也是LeadDownVec有3个元素、UnwindDownVec有8个元素、TrailDownVec有4个元素,但注意在rep movsd前后分别加上std/cld修改方向
tips:这个函数是跳转表优化的绝佳例子
3、memcmp
标准中声明:
int memcmp(const void *s1, const void *s2, size_t n);
3个参数:
形参const void *s1为[ebp+8]
形参const void *s2为[ebp+C]
形参size_t n为[ebp+10]
调用约定是__cdecl
好长的程序,从MSVCR80D.dll:10232280~MSVCR80D.dll:10232CEC
实在没法看完……
入口程序
asm 代码:
简单评价一下
memset中规中矩,对sse2做了优化,但竟然没有对mmx做优化(sigh,我用的电脑还不支持sse2),memchr里面的一个算法很诡异,memcpy写得很漂亮,memcmp就压根看不懂了……
字符串函数(1)
0、代码:
c++ 代码:
代码:
#include <string.h>
int main()
{
int* src = new int[10];
int* dest = new int[10];
memset(src, 0, sizeof(int) * 10);
*(char*)(&src[5]) = ‘a’;
memchr(src, ‘a’, sizeof(int) * 10);
memcpy(dest, src, sizeof(int) * 10);
memmove(dest, src, sizeof(int) * 10);
memcmp(src, dest, sizeof(int) * 10);
return 0;
} 标准中声明:void *memset(void *s, int c, size_t n);
crt\src\intel\mamset.asm中的定义:
asm 代码:
代码:
memset proc \
dst:ptr byte, \
value:byte, \
count:dword 判断count == 0,直接把dst放在eax中,然后结束
判断value是0,且count>=100h(256),且__sse2_available不为0,则jmp _VEC_memzero(此时有al: value,ecx: dst, edx: count)
判断count若小于4,则循环edx次mov [edi], al直到count为0,把dst放在eax中,然后结束
计算dst到下一个dword boundary的距离,放在ecx中,如果不为0,sub edx, ecx,并且先将这ecx个用mov [edi], al放好
把eax的4个byte全设成al
把edx(调整dword boundary后的count)/4放在ecx中,把edx&3放在edx中
然后rep stosd,把ecx个int的数据放好
如果edx为0,结束,不然循环edx次mov [edi], al
tips:
1) 把eax的4个byte全设成al:
asm 代码:
代码:
; set all 4 bytes of eax to [value] mov ecx,eax ; ecx=0/0/0/value shl eax,8 ; eax=0/0/value/0 add eax,ecx ; eax=0/0val/val mov ecx,eax ; ecx=0/0/val/val shl eax,10h ; eax=val/val/0/0 add eax,ecx ; eax = all 4 bytes = [value]
用了4个局部变量和3个参数:
形参[ebp+8],实参为dst
形参[ebp+C],实参为0,无用
形参[ebp+10],实参为count
调用约定是__cdecl
asm 代码:
代码:
MSVCR80D.dll:102335F0 sub_102335F0 proc near ; CODE XREF: MSVCR80D.dll:10230897j
MSVCR80D.dll:102335F0 ; sub_102335F0+7Dp
MSVCR80D.dll:102335F0
MSVCR80D.dll:102335F0 var_10= dword ptr -10h ;
MSVCR80D.dll:102335F0 var_C= dword ptr -0Ch ; 留着放count的低7位
MSVCR80D.dll:102335F0 var_8= dword ptr -8 ; 留着放修改后的dst
MSVCR80D.dll:102335F0 var_4= dword ptr -4 ; 留着放edi
MSVCR80D.dll:102335F0 arg_0= dword ptr 8 ; dst
MSVCR80D.dll:102335F0 arg_8= dword ptr 10h ; count
MSVCR80D.dll:102335F0
MSVCR80D.dll:102335F0 push ebp
MSVCR80D.dll:102335F1 mov ebp, esp
MSVCR80D.dll:102335F3 sub esp, 10h
MSVCR80D.dll:102335F6 mov [ebp+var_4], edi ; var_4 <- edi,等同于push/pop的保存寄存器方法
MSVCR80D.dll:102335F9 mov eax, [ebp+arg_0] ; eax <- dst
MSVCR80D.dll:102335FC cdq ; 把eax按符号扩展为edx : eax
MSVCR80D.dll:102335FD mov edi, eax ; edi <- dst
MSVCR80D.dll:102335FF xor edi, edx
MSVCR80D.dll:10233601 sub edi, edx
MSVCR80D.dll:10233603 and edi, 0Fh
MSVCR80D.dll:10233606 xor edi, edx
MSVCR80D.dll:10233608 sub edi, edx ; edi是dst的低4位保留,
; edi的高28位全为0(若dst为正)或全为1(若dst为负)
MSVCR80D.dll:1023360A test edi, edi ; edi == 0 iff dst>0且dst整除16(xmm是16byte)
MSVCR80D.dll:1023360C jnz short loc_1023364A
MSVCR80D.dll:1023360E mov ecx, [ebp+arg_8] ; ecx <- count
MSVCR80D.dll:10233611 mov edx, ecx
MSVCR80D.dll:10233613 and edx, 7Fh ; edx <- count的低7位
MSVCR80D.dll:10233616 mov [ebp+var_C], edx ; var_C <- count的低7位
MSVCR80D.dll:10233619 cmp ecx, edx ; ecx - edx == 0 iff count < 128
MSVCR80D.dll:1023361B jz short loc_1023362F
MSVCR80D.dll:1023361D sub ecx, edx ; ecx <- count的高25位(低7位为0)
MSVCR80D.dll:1023361F push ecx ; 第二个参数:count的高25位(也就是128的倍数)
MSVCR80D.dll:10233620 push eax ; 第一个参数:dst
MSVCR80D.dll:10233621 call near ptr unk_10233680 ; 名字为fastzero_I,IDA竟然默认不会反汇编这一段,ft
MSVCR80D.dll:10233626 add esp, 8
MSVCR80D.dll:10233629 mov eax, [ebp+arg_0] ; eax <- dst
MSVCR80D.dll:1023362C mov edx, [ebp+var_C] ; edx <- count的低7位
MSVCR80D.dll:1023362F
MSVCR80D.dll:1023362F loc_1023362F: ; CODE XREF: sub_102335F0+2Bj
MSVCR80D.dll:1023362F test edx, edx
MSVCR80D.dll:10233631 jz short loc_10233678
MSVCR80D.dll:10233633 add eax, [ebp+arg_8]
MSVCR80D.dll:10233636 sub eax, edx ; eax <- dst + count的高25位(低7位为0)
MSVCR80D.dll:10233638 mov [ebp+var_8], eax ; var_8 <- 修改后的dst
MSVCR80D.dll:1023363B xor eax, eax
MSVCR80D.dll:1023363D mov edi, [ebp+var_8]
MSVCR80D.dll:10233640 mov ecx, [ebp+var_C]
MSVCR80D.dll:10233643 rep stosb ; 将剩余的清0
MSVCR80D.dll:10233645 mov eax, [ebp+arg_0] ; eax <- dst
MSVCR80D.dll:10233648 jmp short loc_10233678
MSVCR80D.dll:1023364A ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
MSVCR80D.dll:1023364A
MSVCR80D.dll:1023364A loc_1023364A: ; CODE XREF: sub_102335F0+1Cj
; edi是dst的低4位保留,
; edi的高28位全为0(若dst为正)或全为1(若dst为负)
MSVCR80D.dll:1023364A neg edi
MSVCR80D.dll:1023364C add edi, 10h
MSVCR80D.dll:1023364F mov [ebp+var_10], edi ; var_10 <- dst到下一个可以整除16byte的距离
MSVCR80D.dll:10233652 xor eax, eax
MSVCR80D.dll:10233654 mov edi, [ebp+arg_0]
MSVCR80D.dll:10233657 mov ecx, [ebp+var_10]
MSVCR80D.dll:1023365A rep stosb ; 从dst开始清零var_10长度,之后edi可以整除16
MSVCR80D.dll:1023365C mov eax, [ebp+var_10]
MSVCR80D.dll:1023365F mov ecx, [ebp+arg_0]
MSVCR80D.dll:10233662 mov edx, [ebp+arg_8]
MSVCR80D.dll:10233665 add ecx, eax ; ecx <- 剩余的需要清零的dst地址
MSVCR80D.dll:10233667 sub edx, eax ; edx <- 剩余的需要清零的长度
MSVCR80D.dll:10233669 push edx
MSVCR80D.dll:1023366A push 0
MSVCR80D.dll:1023366C push ecx
MSVCR80D.dll:1023366D call sub_102335F0 ; 递归调用_VEC_memzero
MSVCR80D.dll:10233672 add esp, 0Ch
MSVCR80D.dll:10233675 mov eax, [ebp+arg_0] ; eax <- dst
MSVCR80D.dll:10233678
MSVCR80D.dll:10233678 loc_10233678: ; CODE XREF: sub_102335F0+41j
MSVCR80D.dll:10233678 ; sub_102335F0+58j
MSVCR80D.dll:10233678 mov edi, [ebp+var_4]
MSVCR80D.dll:1023367B mov esp, ebp
MSVCR80D.dll:1023367D pop ebp
MSVCR80D.dll:1023367E retn
MSVCR80D.dll:1023367E sub_102335F0 endp 2个参数:
形参[dst]为[ebp+8],实参是dst
形参[len]为[ebp+C],实参是count的高25位(低7位为0)
1个局部变量:[ebp-4]
调用约定是__cdecl
asm 代码:
代码:
fastzero_I: 10233680 push ebp 10233681 mov ebp,esp 10233683 sub esp,4 10233686 mov dword ptr [ebp-4],edi ; 保存edi 10233689 mov edi,dword ptr [dst] 1023368C mov ecx,dword ptr [len] 1023368F shr ecx,7 ; ecx的低7位都是0 10233692 pxor xmm0,xmm0 10233696 jmp L_1 (102336A0h) 10233698 lea esp,[esp] 1023369F nop L_1: 102336A0 movdqa xmmword ptr [edi],xmm0 ; mm是64位,xmm是128位合计16bytes 102336A4 movdqa xmmword ptr [edi+10h],xmm0 102336A9 movdqa xmmword ptr [edi+20h],xmm0 102336AE movdqa xmmword ptr [edi+30h],xmm0 102336B3 movdqa xmmword ptr [edi+40h],xmm0 102336B8 movdqa xmmword ptr [edi+50h],xmm0 102336BD movdqa xmmword ptr [edi+60h],xmm0 102336C2 movdqa xmmword ptr [edi+70h],xmm0 ; 把128bytes清零 102336C7 lea edi,[edi+80h] ; 就是add edi, 80h 102336CD dec ecx 102336CE jne L_1 (102336A0h) 102336D0 mov edi,dword ptr [ebp-4] 102336D3 mov esp,ebp 102336D5 pop ebp 102336D6 ret
标准的声明:void *memchr(const void *s, int c, size_t n);
crt\src\intel\memchr.asm中的定义:
asm 代码:
代码:
memchr proc \
buf:ptr byte, \
chr:byte, \
cnt:dword 检查cnt是否为0
判断buf是不是4的倍数(aligned on 32bits),如果不是,先找前面几个byte直到4的倍数,
判断剩余需要比较的bytes是否大于4,如果小于4,判断最后的几个bytes
如果大于等于4,类似于memset的方法把ebx用chr填满,跳至main_loop_entry
主循环:
asm 代码:
代码:
main_loop:
sub eax,4
jb short return_from_main
main_loop_entry:
mov ecx,dword ptr [edx] ; read 4 bytes
xor ecx,ebx ; ebx is byte\byte\byte\byte
mov edi,7efefeffh ; 0111 1110 1111 1110 1111 1110 1111 1111
add edi,ecx
xor ecx,-1
xor ecx,edi
add edx,4
and ecx,81010100h ; 1000 0001 0000 0001 0000 0001 0000 0000
je short main_loop ; 这里为什么要用7efefeffh和81010100h?
; found zero byte in the loop?
char_is_found:
mov ecx,[edx - 4]
xor cl,bl ; is it byte 0
je short byte_0
xor ch,bl ; is it byte 1
je short byte_1
shr ecx,10h ; is it byte 2
xor cl,bl
je short byte_2
xor ch,bl ; is it byte 3
je short byte_3
jmp short main_loop ; taken if bits 24-30 are clear and bit
; 31 is set 3、memcpy & memmove
标准中声明:
void *memcpy(void * restrict s1,
const void * restrict s2,
size_t n);
void *memmove(void *s1, const void *s2, size_t n);
crt\src\intel\mamcpy.asm中的定义:
asm 代码:
代码:
ifdef MEM_MOVE
_MEM_ equ <memmove>
else ; MEM_MOVE
_MEM_ equ <memcpy>
endif ; MEM_MOVE
_MEM_ proc \
dst:ptr byte, \
src:ptr byte, \
count:IWORD 若dst <= src或dst >= src + count,则CopyUp,否则,CopyDown
1)CopyUp
先判断若count >= 256且__sse2_available != 0且dst - src整除16,则调用_VEC_memcpy
否则做Dword_align:
判断edi是否align到dword了
a. 如果已经完成
ecx记录count / 4,edx记录count % 4
判断若ecx < 8,手写从esi到edi的复制过程(至于吗,难道rep movsd的效率不如手写?或是为了便于cpu乱序执行?)
否则rep movsd,然后调用TrailUpVec复制剩余的bytes
b.如果没有完成
若count小于4直接调用TrailUpVec复制剩余的bytes
否则调用LeadUpVec
UnwindUpVec长度为8,其实就是手写index个dword的复制,然后把剩下的调用TrailUpVec
LeadUpVec长度为3(注意没有LeadUp0),作用是先将dst的align所差的几个bytes(就是4 - dst % 4)复制过去,然后再rep movsd,最后将count中不足4的剩余bytes调用TrailUpVec复制剩余的bytes
TrailUpVec长度为4,作用是拷贝最后的index个字符,然后退出
2) CopyDown,情况完全一样,也是LeadDownVec有3个元素、UnwindDownVec有8个元素、TrailDownVec有4个元素,但注意在rep movsd前后分别加上std/cld修改方向
tips:这个函数是跳转表优化的绝佳例子
3、memcmp
标准中声明:
int memcmp(const void *s1, const void *s2, size_t n);
3个参数:
形参const void *s1为[ebp+8]
形参const void *s2为[ebp+C]
形参size_t n为[ebp+10]
调用约定是__cdecl
好长的程序,从MSVCR80D.dll:10232280~MSVCR80D.dll:10232CEC
实在没法看完……
入口程序
asm 代码:
代码:
MSVCR80D.dll:10232280 ; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹? MSVCR80D.dll:10232280 MSVCR80D.dll:10232280 ; Attributes: bp-based frame MSVCR80D.dll:10232280 MSVCR80D.dll:10232280 MSVCR80D_memcmp proc near MSVCR80D.dll:10232280 MSVCR80D.dll:10232280 var_24= dword ptr -24h MSVCR80D.dll:10232280 var_20= dword ptr -20h MSVCR80D.dll:10232280 var_1C= dword ptr -1Ch MSVCR80D.dll:10232280 var_18= dword ptr -18h MSVCR80D.dll:10232280 var_14= dword ptr -14h MSVCR80D.dll:10232280 var_10= dword ptr -10h ; 存放size_t n MSVCR80D.dll:10232280 var_C= dword ptr -0Ch MSVCR80D.dll:10232280 var_8= dword ptr -8 ; 存放const void *s2 MSVCR80D.dll:10232280 var_4= dword ptr -4 ; 存放const void *s1 MSVCR80D.dll:10232280 arg_0= dword ptr 8 ; const void *s1 MSVCR80D.dll:10232280 arg_4= dword ptr 0Ch ; const void *s2 MSVCR80D.dll:10232280 arg_8= dword ptr 10h ; size_t n MSVCR80D.dll:10232280 MSVCR80D.dll:10232280 push ebp MSVCR80D.dll:10232281 mov ebp, esp MSVCR80D.dll:10232283 sub esp, 28h MSVCR80D.dll:10232286 mov eax, [ebp+arg_4] MSVCR80D.dll:10232289 mov [ebp+var_8], eax MSVCR80D.dll:1023228C mov ecx, [ebp+arg_0] MSVCR80D.dll:1023228F mov [ebp+var_4], ecx MSVCR80D.dll:10232292 mov edx, [ebp+arg_8] MSVCR80D.dll:10232295 mov [ebp+var_10], edx MSVCR80D.dll:10232298 cmp [ebp+var_10], 4 MSVCR80D.dll:1023229C ja loc_10232409 ; 当n > 4时调用另外一个函数,在10232490 MSVCR80D.dll:102322A2 mov eax, [ebp+var_10] MSVCR80D.dll:102322A5 jmp ds:dword_10232424[eax*4] ; 一个vector,当0 <= n <= 4时走这里 MSVCR80D.dll:10232421 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪? MSVCR80D.dll:10232424 dword_10232424 dd 10232405h ; DATA XREF: MSVCR80D_memcmp+25r MSVCR80D.dll:10232428 dd 102323F3h ; vector的5个值在这里 MSVCR80D.dll:1023242C dd 102323B4h MSVCR80D.dll:10232430 dd 10232348h MSVCR80D.dll:10232434 dd 102322ACh
memset中规中矩,对sse2做了优化,但竟然没有对mmx做优化(sigh,我用的电脑还不支持sse2),memchr里面的一个算法很诡异,memcpy写得很漂亮,memcmp就压根看不懂了……
评论总数 2
评论
| | 你好! 我阅读了你的c++的一些分析笔记,感觉c++的根是汇编,是这样么?学习c++是不是一定要翻阅汇编这座大山? 另外图书市场的变化很快,我觉得看图书积累不是学习的办法,太被动了,深入学习c++/编程有什么好建议? 谢谢! |
| 发表于 2008-01-22 07:48 PM 作者: 我来了(sxfcity@gmail.com) |
| | 不是这样子的,crt与c++没有太多关系,c++也和汇编没有太多关系。对于c++的书来说,我比较推崇Bjarne的c++设计与演化那本书。 |
发表于 2008-01-22 07:48 PM 作者: tomato |
发表评论 |
作者为 tomato 的最新文章
- C++对象模型(二) (2006-09-23)
- C++对象模型(一) (2006-09-20)
- CRT源码解析(三)字符串函数(2) (2006-05-22)
- CRT源码解析(二)字符串函数(1) (2006-05-07)
- CRT源码解析(一) (2006-05-05)




