还是关于虚拟方法的问题。。

yeliangang 2009-04-26 03:00:36
加精
看到一篇文章说一个对象的VMT表中除了自己定义的虚拟方法外,还有它的祖先的所有的虚拟方法。但是今天跟进去看了看VMT发现只有它自己的虚拟方法,这是什么原因,难道又是编译器决定在某些时候不在VMT中写入祖先的虚拟方法?
...全文
330 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
pathuang68 2009-04-29
  • 打赏
  • 举报
回复
awesome!
Seamour 2009-04-28
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 yeliangang 的回复:]
基本上从 [VMT地址-48d]得到的DMT地址通常都是紧接着VMT的了?那这么做了还设置"固定值"块里的DMT地址干什么,直接可以通过紧接VMT这个特点找到DMT了吧?还是紧紧是为了兼容性等的方面问题非得把它弄在那块固定值里,或者其他什么原因?[/Quote]
1. 不是每个类都有vmt和dmt
2. 用你说的办法怎么找到dmt?
lj322203 2009-04-28
  • 打赏
  • 举报
回复
不错的东西 学习中
neweipeng 2009-04-28
  • 打赏
  • 举报
回复
顶,学习中……
yeliangang 2009-04-28
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 Seamour 的回复:]
引用 9 楼 yeliangang 的回复:
基本上从 [VMT地址-48d]得到的DMT地址通常都是紧接着VMT的了?那这么做了还设置"固定值"块里的DMT地址干什么,直接可以通过紧接VMT这个特点找到DMT了吧?还是紧紧是为了兼容性等的方面问题非得把它弄在那块固定值里,或者其他什么原因?
1. 不是每个类都有vmt和dmt
2. 用你说的办法怎么找到dmt?

[/Quote]
这样就明白了,我以为每个类都有VMT,这样DMT直接就跟在VMT后就好找了,还有没有VMT的情况没考虑进去,终于算比较明白这个了,非常感谢啊
sparklerl 2009-04-28
  • 打赏
  • 举报
回复
up

学习

thx
bossmin 2009-04-28
  • 打赏
  • 举报
回复
yeliangang 2009-04-27
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 Seamour 的回复:]
画了一张win32平台的虚拟地址示意图,还有一个例子用来说明delphi对象/类的内存布局
因为到处都是指针,所以看起来很乱,我尽量区别颜色,希望能有助于区分指向的目标。结合前面的回贴再慢慢理解一下吧,看不懂的话我也没辙了

图片地址:http://seamours.googlepages.com/delphiObj.PNG

[/Quote]
回答很精彩很全面啊,简直是经验与智慧的结晶,熟悉了不少知识,但是又下来一个问题:
基本上从 [VMT地址-48d]得到的DMT地址通常都是紧接着VMT的了?那这么做了还设置"固定值"块里的DMT地址干什么,直接可以通过紧接VMT这个特点找到DMT了吧?还是紧紧是为了兼容性等的方面问题非得把它弄在那块固定值里,或者其他什么原因?
willflyz 2009-04-27
  • 打赏
  • 举报
回复
学习!这图画得很好啊.
liangpei2008 2009-04-27
  • 打赏
  • 举报
回复
顶,学习
chys3584 2009-04-27
  • 打赏
  • 举报
回复
顶,学习... ...
lihuasoft 2009-04-27
  • 打赏
  • 举报
回复
版主呢?

来给此贴授精
Seamour 2009-04-27
  • 打赏
  • 举报
回复
画了一张win32平台的虚拟地址示意图,还有一个例子用来说明delphi对象/类的内存布局
因为到处都是指针,所以看起来很乱,我尽量区别颜色,希望能有助于区分指向的目标。结合前面的回贴再慢慢理解一下吧,看不懂的话我也没辙了
[img=http://seamours.googlepages.com/delphiObj.PNG]Win32中Delphi对象和类的内存布局[/img]
图片地址:http://seamours.googlepages.com/delphiObj.PNG
yeliangang 2009-04-26
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 Seamour 的回复:]
先构造一这样段代码:
Delphi(Pascal) codetypeTBase=class
publicprocedureFooVirtual1; virtual;procedureFooVirtual2; virtual;procedureFooDynamic1; dynamic;procedureFooDynamic2; dynamic;procedureGetAddrs(varV1, V2, D1, D2: Pointer);end;

TFoo=class(TBase)procedureFooVirtual1; override;procedureFooDynamic1; override;end;procedureTForm1.Button1Click(Sender: TObject);varv1, v2, d1, d2: Pointer;beg…
[/Quote]
有点疑惑,就是偏移地址是相对对象首址来说的么,还有就是我看到VMT里也有类似DMT中动态方法的相关信息,为什么会还要再在VMT中出现呢?
Seamour 2009-04-26
  • 打赏
  • 举报
回复
先构造一这样段代码:

type
TBase = class
public
procedure FooVirtual1; virtual;
procedure FooVirtual2; virtual;
procedure FooDynamic1; dynamic;
procedure FooDynamic2; dynamic;
procedure GetAddrs(var V1, V2, D1, D2: Pointer);
end;

TFoo = class(TBase)
procedure FooVirtual1; override;
procedure FooDynamic1; override;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
v1, v2, d1, d2: Pointer;
begin
Memo1.Clear;
with TBase.Create do
begin
GetAddrs(v1, v2, d1, d2);
Memo1.Lines.Add(Format('[%p] %s', [Pointer(ClassType), ClassName]));
Memo1.Lines.Add(Format(#9'v1: %p'#9'v2: %p'#9'd1: %p'#9'd2: %p',
[v1, v2, d1, d2]));
Free;
end;

with TFoo.Create do
begin
GetAddrs(v1, v2, d1, d2);
Memo1.Lines.Add(Format('[%p] %s', [Pointer(ClassType), ClassName]));
Memo1.Lines.Add(Format(#9'v1: %p'#9'v2: %p'#9'd1: %p'#9'd2: %p',
[v1, v2, d1, d2]));
Free;
end;
end;

{ TBase }

procedure TBase.FooDynamic1;
begin
end;

procedure TBase.FooDynamic2;
begin
end;

procedure TBase.FooVirtual1;
begin
end;

procedure TBase.FooVirtual2;
begin
end;

procedure TBase.GetAddrs(var V1, V2, D1, D2: Pointer);
type
Tfoo = procedure of object;
var
method : TMethod;
foo : TFoo absolute method;
begin
foo := FooVirtual1;
V1 := method.Code;
foo := FooVirtual2;
V2 := method.Code;
foo := FooDynamic1;
D1 := method.Code;
foo := FooDynamic2;
D2 := method.Code;
end;

{ TFoo }

procedure TFoo.FooDynamic1;
begin
end;

procedure TFoo.FooVirtual1;
begin
end;

这个代码就是构造一对virtual方法和一对dynamic方法,都有被子类继承和没被继承的,然后取得运行时的方法地址
在我的电脑上运行结果是:
[00450630] TBase
v1: 00450954 v2: 00450958 d1: 0045094C d2: 00450950
[00450698] TFoo
v1: 004509C8 v2: 00450958 d1: 004509C4 d2: 00450950
从上面的输出结果可以观察到方法对应的地址

不知道你会不会用OllyDbg,用它查东西比用delphi ide方便很多,下面是我用OllyDbg找到的相应地址的数据:

// [00450630] TBase
00450600 38 06 45 00 46 06 45 00 04 00 00 00 A0 10 40 00
~~~~~~~~~~~ Offset = -48: 00450638
00450610 BC 39 40 00 C8 39 40 00 CC 39 40 00 D0 39 40 00
00450620 C4 39 40 00 0C 37 40 00 28 37 40 00 64 37 40 00
00450630 54 09 45 00 58 09 45 00 02 00 FF FF FE FF 4C 09
TBase->^ ^[00450638]
00450640 45 00 50 09 45 00 05 54 42 61 73 65 98 06 45 00

// [00450698] TFoo
00450660 00 00 00 00 00 00 00 00 A0 06 45 00 A8 06 45 00
~~~~~~~~~~~ Offset = -48: 004506A0
00450670 04 00 00 00 E4 05 45 00 BC 39 40 00 C8 39 40 00
00450680 CC 39 40 00 D0 39 40 00 C4 39 40 00 0C 37 40 00
00450690 28 37 40 00 64 37 40 00 C8 09 45 00 58 09 45 00
TFoo->^
004506A0 01 00 FF FF C4 09 45 00 04 54 46 6F 6F 8D 40 00
^004506A0

class本身指向的是VMT(Virtual Method Table),而与class相关的数据采用System.pas中标注 Virtual method table entries 的偏移量,而动态方法的偏移量 vmtDynamicTable=-48(我用的是d7,这个偏移量在之后的版本应该都是一致的)。

先看TBase的继承类TFoo,vmt中头两个dword是004509C8、00450958,与输出中的TFoo的v1、v2是一致的。而v2并在TFoo中并没有被继承,但在vmt相应位置还是有父类该方法的地址(TBase的v2也是00450958)。

先说明一点,在调用动态方法的时候实际上会调用System.GetDynaMethod(可以到System.pas中看源代码)。如果不懂汇编的话,我大概解释一下过程。首先,
在调用动态方法的时候,会先传入class的地址和该动态方法对应的index(该index是个16位的有符号整型,序号从-1(ffff)开始),然后通过查dmt表,如果表中有对应的index,则通过计算返回地址,否则将到父类中继续查找。
先找到vmtDynamicTable的内容,Offset=-48位置的dword指向dmt,先看TBase的。第一个word(0002)是该dmt的长度,也就是说一共记录了2个动态方法。接下来是index表。第一个word(ffff)是-1,就是说明这个表里有序号为-1的动态方法,第二个同理是word是-2序号。然后就是动态方法的地址了,第一个dword就是TBase.d1的地址0045094C,第二个dword就是TBase.d2的地址00450950。
接着看TFoo的,第一个word(0001)说明该表中只有一个动态方法的地址,接下来的ffff就是index=-1,然后的dword就是TFoo.d1的地址004509C4。
如果还不明白的话,可以把TFoo继承d1改为继承d2,然后再看dmt,那里的值就应该是 01 00 FE FF,也就是表中记录了1个动态方法(0001),它的index=-2(fffe),而之后的dword应该就是d2的地址

不知道我讲清楚了没有
yeliangang 2009-04-26
  • 打赏
  • 举报
回复
明白了,自己理解错误。。。另外还有就是查看VMT时发现动态方法的地址也在VMT里,只是前面有ffff0001标志,难道实际上是运行时根据DMT找到函数地址在VMT中的地址而取得的函数地址?

16,748

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 语言基础/算法/系统设计
社区管理员
  • 语言基础/算法/系统设计社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧