在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
自己用上了D2010,然后心中一直想着一句话就是Borland的编译器比其他的都要好很多,虽然现在Delphi已经易主了.
代码
1 编译器创建对象的过程: 2 假设有这样一个类: 3 THuangJacky= class 4 5 end; 6 7 我们创建一个对象: 8 procedure TForm3.btn1Click(Sender: TObject); 9 var 10 A:THuangJacky; 11 begin 12 A:=THuangJacky.Create; 13 A.Free; 14 end; 15 16 看看编译器都在干什么? 17 Unit3.pas.34: A:=THuangJacky.Create; 18 //这个Dl为什么要设置成1? 19 004A3BE0 B201 mov dl,$01 20 //$004a3b54这个就是THuangJacky类的地址 21 004A3BE2 A1543B4A00 mov eax,[$004a3b54] 22 //由于我们没有在构造方法里面写其他代码所以这里就直接调用TObject.Create 23 004A3BE7 E88C11F6FF call TObject.Create 24 ---------------------------------------------------------- 25 TObject.Create: 26 00404D78 84D2 test dl,dl 27 00404D7A 7408 jz $00404d84 //如果dl为0就跳--------------------| 28 00404D7C 83C4F0 add esp,-$10 //分配堆栈空间给局部变量. | 29 00404D7F E840050000 call @ClassCreate //System.ClassCreate | 30 00404D84 84D2 test dl,dl <-| 31 00404D86 740F jz $00404d97 //为0 跳出构造函数-----------------------| 32 00404D88 E88F050000 call @AfterConstruction | 33 00404D8D 648F0500000000 pop dword ptr fs:[$00000000]//还原SEH | 34 00404D94 83C40C add esp,$0c //堆栈平衡 | 35 00404D97 C3 ret <-| 36 ---------------------------------------------------------------------------------- 37 @ClassCreate: 38 004052C4 52 push edx 39 004052C5 51 push ecx 40 004052C6 53 push ebx 41 004052C7 84D2 test dl,dl 42 004052C9 7C03 jl $004052ce //dl小于0跳----------------------| 43 004052CB FF50F4 call dword ptr [eax-$0c]//这个是NewInstance | 44 004052CE 31D2 xor edx,edx //清空edx=0 <-| 45 004052D0 8D4C2410 lea ecx,[esp+$10] 46 004052D4 648B1A mov ebx,fs:[edx] //这里应该是SEH的操作,和构造无关 47 004052D7 8919 mov [ecx],ebx 48 004052D9 896908 mov [ecx+$08],ebp 49 004052DC C74104ED524000 mov [ecx+$04],$004052ed 50 004052E3 89410C mov [ecx+$0c],eax 51 004052E6 64890A mov fs:[edx],ecx 52 004052E9 5B pop ebx 53 004052EA 59 pop ecx 54 004052EB 5A pop edx 55 004052EC C3 ret 56 004052ED E9E2010000 jmp @HandleAnyException //如果发生异常那么就会调用到这里 57 004052F2 8B44242C mov eax,[esp+$2c] 58 004052F6 8B400C mov eax,[eax+$0c] 59 004052F9 85C0 test eax,eax 60 004052FB 740E jz $0040530b 61 004052FD 8B08 mov ecx,[eax] 62 004052FF B281 mov dl,$81 63 00405301 50 push eax 64 00405302 FF51FC call dword ptr [ecx-$04] 65 00405305 58 pop eax 66 00405306 E809000000 call @ClassDestroy 67 0040530B E8C8050000 call @RaiseAgain 68 00405310 C3 ret 69 00405311 8D4000 lea eax,[eax+$00] 70 ----------------------------------------------------------------- 71 TObject.NewInstance: 72 00404D40 53 push ebx 73 00404D41 8BD8 mov ebx,eax //在ebx中保存THuangJacky类指针 74 00404D43 8BC3 mov eax,ebx //因为InstanceSize返回值会覆盖eax 75 00404D45 E826000000 call TObject.InstanceSize 76 00404D4A E885F4FFFF call @GetMem //分配内存 77 00404D4F 8BD0 mov edx,eax //edx=$00eb0ec0,分配的内存块 78 00404D51 8BC3 mov eax,ebx //eax=$004a3b54,类指针 79 00404D53 E85C000000 call TObject.InitInstance 80 00404D58 5B pop ebx 81 00404D59 C3 ret 82 00404D5A 8BC0 mov eax,eax 83 ------------------------------------------------------------------ 84 TObject.InstanceSize的pascal形式: 85 class function TObject.InstanceSize: Longint; 86 begin 87 Result := PInteger(Integer(Self) + vmtInstanceSize)^; 88 end; 89 就是类指针-52的地址,类指针是多少?刚才赋值给Eax的那个值$004a3b54 90 汇编代码形式: 91 TObject.InstanceSize: 92 00404D70 83C0CC add eax,-$34 //减去52 93 00404D73 8B00 mov eax,[eax] //指向的值传给eax返回,可以看到是8 94 00404D75 C3 ret //退出 95 00404D76 8BC0 mov eax,eax 96 ----------------------------------------------------------------- 97 接下来该分配内存了 98 @GetMem: 99 004041D4 85C0 test eax,eax 100 004041D6 7E13 jle $004041eb //eax小于等于0------------------| 101 004041D8 FF1560A74A00 call dword ptr [$004aa760] //调用SysGetMem | 102 004041DE 85C0 test eax,eax //eax=00eb0ec0 | 103 004041E0 7402 jz $004041e4 //等于0跳,跳不了了----| | 104 004041E2 F3C3 rep ret | | 105 004041E4 B001 mov al,$01 <-| | 106 004041E6 E945010000 jmp Error | 107 004041EB 31C0 xor eax,eax //返回就是空了 <-| 108 004041ED F3C3 rep ret 109 004041EF 90 nop 110 ------------------------------------------------------------------- 111 //这个就是实际分配内存的函数,太长了. 112 //类指针是$004a3b54 113 SysGetMem: 114 00402CC8 8D5003 lea edx,[eax+$03] //edx=11 115 00402CCB C1EA03 shr edx,$03 //edx右移3位,edx=1 116 00402CCE 3D2C0A0000 cmp eax,$00000a2c //eax=8,肯定比$0a2c小 117 00402CD3 53 push ebx //ebx=$004a3bac,偏移是$58(88),查表 是vmtSelfPtr指向虚方法表的指针 118 00402CD4 8A0D51D04A00 mov cl,[$004ad051] //cl=00,因为小头地址 119 00402CDA 0F8748020000 jnbe $00402f28 //如果eax不小于等于$0a2c,跳不了---------------------------------------- 120 00402CE0 84C9 test cl,cl 121 00402CE2 0FB682E0D84A00 movzx eax,[edx+$004ad8e0] //eax=0 122 00402CE9 8D1CC580A04A00 lea ebx,[eax*8+$4aa080] //ebx=$004aa080 123 00402CF0 7556 jnz $00402d48 //cl不等于0,跳不了--------------------------------------- 124 00402CF2 8B5304 mov edx,[ebx+$04] //edx=$00eb0ce0 125 00402CF5 8B4208 mov eax,[edx+$08] //eax=$00eb0ec0 后面一个Word感觉有猫腻 126 00402CF8 B9F8FFFFFF mov ecx,$fffffff8 //ecx=$fffffff8 127 00402CFD 39DA cmp edx,ebx //比较ebx和edx,等于0跳 128 00402CFF 7417 jz $00402d18 //跳不了 129 00402D01 83420C01 add dword ptr [edx+$0c],$01//将edx+$0c地址上面的数+1,位$0000001D 130 00402D05 2348FC and ecx,[eax-$04] //ecx与上eax-$4地址上的数(00000001),ecx=0 131 00402D08 894A08 mov [edx+$08],ecx //edx+$08地址上数为0 132 00402D0B 8950FC mov [eax-$04],edx //$00EB0EBC->$00eb0ce0 133 00402D0E 7428 jz $00402d38 //ecx=0 所以这里跳走----| 134 00402D10 C60300 mov byte ptr [ebx],$00 | 135 00402D13 5B pop ebx | 136 00402D14 C3 ret | 137 00402D15 90 nop | 138 00402D16 90 nop | 139 00402D17 90 nop | 140 00402D18 8B5310 mov edx,[ebx+$10] | 141 00402D1B 0FB74B02 movzx ecx,[ebx+$02] | 142 00402D1F 01C1 add ecx,eax | 143 00402D21 3B430C cmp eax,[ebx+$0c] | 144 00402D24 7776 jnbe $00402d9c | 145 00402D26 83420C01 add dword ptr [edx+$0c],$01 | 146 00402D2A 894B08 mov [ebx+$08],ecx | 147 00402D2D C60300 mov byte ptr [ebx],$00 | 148 00402D30 8950FC mov [eax-$04],edx | 149 00402D33 5B pop ebx | 150 00402D34 C3 ret | 151 00402D35 90 nop | 152 00402D36 90 nop | 153 00402D37 90 nop | 154 00402D38 8B4A04 mov ecx,[edx+$04] //ecx=$004aa080<-| 155 00402D3B 895914 mov [ecx+$14],ebx //[$004aa094]->$004aa080 156 00402D3E 894B04 mov [ebx+$04],ecx //[$004aa084]->$004aa080 这一块内存都成了它了 157 00402D41 C60300 mov byte ptr [ebx],$00 //这个地址本来就是0 158 00402D44 5B pop ebx //弹出对象指针. 159 00402D45 C3 ret //返回了,剩下的代码就省略了 160 --------------------------------------------------------------------- 161 TObject.InitInstance: 162 Pascal版本: 163 class function TObject.InitInstance(Instance: Pointer): TObject; 164 var 165 IntfTable: PInterfaceTable; 166 ClassPtr: TClass; 167 I: Integer; 168 begin 169 //1 首先是将分配对象空间,清0 170 FillChar(Instance^, InstanceSize, 0); 171 //2 将实例与类关联起来 172 PInteger(Instance)^ := Integer(Self); 173 // 174 ClassPtr := Self; 175 //下面的代码是将实例的接口表指向HuangJacky类以及父类的 176 //相关结构体的定义: 177 { 178 PInterfaceEntry = ^TInterfaceEntry; 179 TInterfaceEntry = packed record 180 IID: TGUID; 181 VTable: Pointer;//VMT? 182 IOffset: Integer; 183 ImplGetter: Integer; 184 end; 185 186 PInterfaceTable = ^TInterfaceTable; 187 TInterfaceTable = packed record 188 EntryCount: Integer; 189 Entries: array[0..9999] of TInterfaceEntry; 190 end; 191 } 192 while ClassPtr <> nil do 193 begin 194 IntfTable := ClassPtr.GetInterfaceTable; 195 if IntfTable <> nil then 196 for I := 0 to IntfTable.EntryCount-1 do 197 with IntfTable.Entries[I] do 198 begin 199 if VTable <> nil then 200 PInteger(@PAnsiChar(Instance)[IOffset])^ := Integer(VTable); 201 end; 202 ClassPtr := ClassPtr.ClassParent; 203 end; 204 Result := Instance; 205 end; 206 理解了我们来看 207 汇编版本: 208 00404DB4 53 push ebx //$004A3BAC,vmtSelfPtr 209 00404DB5 56 push esi 210 00404DB6 57 push edi //这3个push都是为了保护现场 211 00404DB7 89C3 mov ebx,eax //ebx=eax=$004a3bac 212 00404DB9 89D7 mov edi,edx //edi=edx=$00eb0ec0 213 00404DBB AB stosd //将eax移到edi中去,一次移动一个DWORD,隐含add edi,4 214 00404DBC 8B4BCC mov ecx,[ebx-$34] //ecx=8,InstanceSize. |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论