自己用上了D2010,然后心中一直想着一句话就是Borland的编译器比其他的都要好很多,虽然现在Delphi已经易主了.
我们现在一天都在说面向对象,但是我们知道对象在内存都是一堆数据而已,那么Delphi编译器是怎么来管理这些数据的呢?
抱着这样的态度,我用简单的代码进行了一些测试,当然技术有限,有所错误,希望朋友指出来不要喷我.
本文首发http://huangjacky.cnblogs.com/,转载请注明,谢谢.
好开始,本文是以D2010为基础的.其他版本上面可能会有所不同.
代码
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.
215 00404DBF 31C0 xor eax,eax // 清空eax,eax = 0
216 00404DC1 51 push ecx //
217 00404DC2 C1E902 shr ecx,$ 02 // 右移,ecx = 2 ,因为一次移动一个DWORD( 4 个字节)
218 00404DC5 49 dec ecx // ecx是循环次数
219 00404DC6 F3AB rep stosd // 这样[EDI] = 0
220 00404DC8 59 pop ecx
221 00404DC9 83E103 and ecx,$ 03 // ecx = 0 , 8 与3( 100 and 011 )
222 00404DCC F3AA rep stosb // 一次移动一个字节,但是ecx = 0 ,所以不移动
223 00404DCE 89D0 mov eax,edx // 从前面我们看到edx是@GetMem返回分配了空间的地址,eax = edx = $00eb0ec0,已经初始化0了
224 00404DD0 89E2 mov edx,esp // edx = esp = 栈顶
225 00404DD2 8B4BAC |-> mov ecx,[ebx - $ 54 ] // ecx = 0 vmtIntfTable = - 84 ;获得类的IntfTable,THuangJacky没有
226 00404DD5 85C9 | test ecx,ecx
227 00404DD7 7401 | jz $00404dda // 跳 ---|
228 00404DD9 51 | push ecx |
229 00404DDA 8B5BD0 | mov ebx,[ebx - $ 30 ] <-| ebx = $0040143c // vmtParent = - 48 ;父类,现在是TObject,它也没有有IntfTable
230 00404DDD 85DB | test ebx,ebx
231 00404DDF 7404 | jz $00404de5 // 不跳 --|
232 00404DE1 8B1B | mov ebx,[ebx] // ebx = $ 00401494 |
233 00404DE3 EBED |-- jmp $00404dd2 // while 循环 跳上去 |
234 00404DE5 39D4 cmp esp,edx // 检测中间是否有压栈 <-|
235 00404DE7 741D jz $ 00404e06 // 没有,跳 --------------------------------------------------------------------------|
236 00404DE9 5B pop ebx // 这里主要是While循环中那个If的操作 |
237 00404DEA 8B0B mov ecx,[ebx] |
238 00404DEC 83C304 add ebx,$ 04 |
239 00404DEF 8B7310 mov esi,[ebx + $ 10 ] |
240 00404DF2 85F6 test esi,esi |
241 00404DF4 7406 jz $00404dfc |
242 00404DF6 8B7B14 mov edi,[ebx + $ 14 ] |
243 00404DF9 893407 mov [edi + eax],esi |
244 00404DFC 83C31C add ebx,$1c |
245 00404DFF 49 dec ecx |
246 00404E00 75ED jnz $00404def |
247 00404E02 39D4 cmp esp,edx |
248 00404E04 75E3 jnz $00404de9 |
249 00404E06 5F pop edi <---|
250 00404E07 5E pop esi
251 00404E08 5B pop ebx // 还原现场
252 00404E09 C3 ret
253 00404E0A 8BC0 mov eax,eax
254 -----------------------------------------------------
255 内存块就变成一个对象了,我们看看内存有什么变化了.
256 00EB0EC0 AC 3B 4A 00 00 00 00 00
257 我们可以看到一个DWORD指向了我们THuangJacky类的地址
258 -----------------------------------------------------
259 构造已接近尾声,看最后一个操作
260 @AfterConstruction:
261 0040531C 55 push ebp
262 0040531D 8BEC mov ebp,esp
263 0040531F 51 push ecx
264 00405320 53 push ebx
265 00405321 56 push esi
266 00405322 57 push edi // 压栈
267 00405323 8945FC mov [ebp - $ 04 ],eax
268 00405326 33D2 xor edx,edx
269 00405328 55 push ebp
270 00405329 684B534000 push $0040534b
271 0040532E 64FF32 push dword ptr fs:[edx]
272 00405331 648922 mov fs:[edx],esp // 这两句代表进入Try了
273 00405334 8B45FC mov eax,[ebp - $ 04 ]
274 00405337 8B10 mov edx,[eax]
275 00405339 FF52E4 call dword ptr [edx - $1c] // 调用THuangJacky.AfterConstructor,vmtAfterConstruction = 24 ,我们没有覆盖这个函数,所以直接返回
276 0040533C 8B45FC mov eax,[ebp - $ 04 ]
277 0040533F 648F0500000000 pop dword ptr fs:[$ 00000000 ]
278 00405346 83C408 add esp,$ 08
279 00405349 EB19 jmp $ 00405364 // 没有异常直接跳过 -------------------------------------|
280 0040534B E984010000 jmp @HandleAnyException // 这里是异常处理 |
281 00405350 B201 mov dl,$ 01 |
282 00405352 8B45FC mov eax,[ebp - $ 04 ] |
283 00405355 E812000000 call @BeforeDestruction |
284 0040535A E879050000 call @RaiseAgain |
285 0040535F E8C8050000 call @DoneExcept |
286 00405364 5F pop edi <-|
287 00405365 5E pop esi
288 00405366 5B pop ebx
289 00405367 59 pop ecx
290 00405368 5D pop ebp
291 00405369 C3 ret
292 0040536A 8BC0 mov eax,eax
293 ------------------------------------------------------------------
294 TObject.AfterConstruction:
295 00405088 C3 ret
296 00405089 8D4000 lea eax,[eax + $ 00 ]
297 ------------------------------------------------------------------
298 整个过程就明了了
299 THuangJacky.Create
300 --> TObject.Create
301 --> @ClassCreate --> @AfterConstruction
302 --> TObject.NewInstance
303 --> TObject.InstanceSize --> @GetMem --> TObject.InitInstance
304
305
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.
215 00404DBF 31C0 xor eax,eax // 清空eax,eax = 0
216 00404DC1 51 push ecx //
217 00404DC2 C1E902 shr ecx,$ 02 // 右移,ecx = 2 ,因为一次移动一个DWORD( 4 个字节)
218 00404DC5 49 dec ecx // ecx是循环次数
219 00404DC6 F3AB rep stosd // 这样[EDI] = 0
220 00404DC8 59 pop ecx
221 00404DC9 83E103 and ecx,$ 03 // ecx = 0 , 8 与3( 100 and 011 )
222 00404DCC F3AA rep stosb // 一次移动一个字节,但是ecx = 0 ,所以不移动
223 00404DCE 89D0 mov eax,edx // 从前面我们看到edx是@GetMem返回分配了空间的地址,eax = edx = $00eb0ec0,已经初始化0了
224 00404DD0 89E2 mov edx,esp // edx = esp = 栈顶
225 00404DD2 8B4BAC |-> mov ecx,[ebx - $ 54 ] // ecx = 0 vmtIntfTable = - 84 ;获得类的IntfTable,THuangJacky没有
226 00404DD5 85C9 | test ecx,ecx
227 00404DD7 7401 | jz $00404dda // 跳 ---|
228 00404DD9 51 | push ecx |
229 00404DDA 8B5BD0 | mov ebx,[ebx - $ 30 ] <-| ebx = $0040143c // vmtParent = - 48 ;父类,现在是TObject,它也没有有IntfTable
230 00404DDD 85DB | test ebx,ebx
231 00404DDF 7404 | jz $00404de5 // 不跳 --|
232 00404DE1 8B1B | mov ebx,[ebx] // ebx = $ 00401494 |
233 00404DE3 EBED |-- jmp $00404dd2 // while 循环 跳上去 |
234 00404DE5 39D4 cmp esp,edx // 检测中间是否有压栈 <-|
235 00404DE7 741D jz $ 00404e06 // 没有,跳 --------------------------------------------------------------------------|
236 00404DE9 5B pop ebx // 这里主要是While循环中那个If的操作 |
237 00404DEA 8B0B mov ecx,[ebx] |
238 00404DEC 83C304 add ebx,$ 04 |
239 00404DEF 8B7310 mov esi,[ebx + $ 10 ] |
240 00404DF2 85F6 test esi,esi |
241 00404DF4 7406 jz $00404dfc |
242 00404DF6 8B7B14 mov edi,[ebx + $ 14 ] |
243 00404DF9 893407 mov [edi + eax],esi |
244 00404DFC 83C31C add ebx,$1c |
245 00404DFF 49 dec ecx |
246 00404E00 75ED jnz $00404def |
247 00404E02 39D4 cmp esp,edx |
248 00404E04 75E3 jnz $00404de9 |
249 00404E06 5F pop edi <---|
250 00404E07 5E pop esi
251 00404E08 5B pop ebx // 还原现场
252 00404E09 C3 ret
253 00404E0A 8BC0 mov eax,eax
254 -----------------------------------------------------
255 内存块就变成一个对象了,我们看看内存有什么变化了.
256 00EB0EC0 AC 3B 4A 00 00 00 00 00
257 我们可以看到一个DWORD指向了我们THuangJacky类的地址
258 -----------------------------------------------------
259 构造已接近尾声,看最后一个操作
260 @AfterConstruction:
261 0040531C 55 push ebp
262 0040531D 8BEC mov ebp,esp
263 0040531F 51 push ecx
264 00405320 53 push ebx
265 00405321 56 push esi
266 00405322 57 push edi // 压栈
267 00405323 8945FC mov [ebp - $ 04 ],eax
268 00405326 33D2 xor edx,edx
269 00405328 55 push ebp
270 00405329 684B534000 push $0040534b
271 0040532E 64FF32 push dword ptr fs:[edx]
272 00405331 648922 mov fs:[edx],esp // 这两句代表进入Try了
273 00405334 8B45FC mov eax,[ebp - $ 04 ]
274 00405337 8B10 mov edx,[eax]
275 00405339 FF52E4 call dword ptr [edx - $1c] // 调用THuangJacky.AfterConstructor,vmtAfterConstruction = 24 ,我们没有覆盖这个函数,所以直接返回
276 0040533C 8B45FC mov eax,[ebp - $ 04 ]
277 0040533F 648F0500000000 pop dword ptr fs:[$ 00000000 ]
278 00405346 83C408 add esp,$ 08
279 00405349 EB19 jmp $ 00405364 // 没有异常直接跳过 -------------------------------------|
280 0040534B E984010000 jmp @HandleAnyException // 这里是异常处理 |
281 00405350 B201 mov dl,$ 01 |
282 00405352 8B45FC mov eax,[ebp - $ 04 ] |
283 00405355 E812000000 call @BeforeDestruction |
284 0040535A E879050000 call @RaiseAgain |
285 0040535F E8C8050000 call @DoneExcept |
286 00405364 5F pop edi <-|
287 00405365 5E pop esi
288 00405366 5B pop ebx
289 00405367 59 pop ecx
290 00405368 5D pop ebp
291 00405369 C3 ret
292 0040536A 8BC0 mov eax,eax
293 ------------------------------------------------------------------
294 TObject.AfterConstruction:
295 00405088 C3 ret
296 00405089 8D4000 lea eax,[eax + $ 00 ]
297 ------------------------------------------------------------------
298 整个过程就明了了
299 THuangJacky.Create
300 --> TObject.Create
301 --> @ClassCreate --> @AfterConstruction
302 --> TObject.NewInstance
303 --> TObject.InstanceSize --> @GetMem --> TObject.InitInstance
304
305
排版效果不是很好大家见谅.
今天就说到这里,关于SEH的知识,我以前博客写过一篇,只是博客挂了,我一会转到CnBlogs来.
谢谢.