上一篇介绍了gcc对成员函数指针做了thunk的处理,本篇介绍vc对成员函数指针如何处理,还有动态绑定相关的处理。

同样用回上一篇的例子:

struct point {float x,y;};
struct obj
{
virtual ~obj {}
void foo(int) {}
void foo(point) {}
virtual void vfoo() {}
};
struct objobj : public obj
{
virtual ~objobj {}
virtual void vfoo() {}
}; void main()
{
obj o;
objobj oo;
//void* pofp = (void*) (void(obj::*)(point))&obj::foo; // error C2440: “类型转换”: 无法从“void (__cdecl obj::* )(point)”转换为“void *”
void(obj::*pi)(int) = &obj::foo;
void(obj::*pp)(point) = &obj::foo;
void(objobj::*vp)() = &objobj::vfoo;
NOOP
((&oo)->*vp)();
NOOP
((&oo)->*pi)();
NOOP
((&o)->*pp)(pt);
}

成员函数指针定义以及调用的代码,所对应的反汇编:

`3f461159 488d05e6feffff  lea     rax,[test!ILT+(?fooobjQEAAXHZ) (`3f461046)]
`3f461160 48898424d8000000 mov qword ptr [rsp+0D8h],rax ; void(obj::*pi)(int) = &obj::foo;
`3f461168 488d05b4feffff lea rax,[test!ILT+(?fooobjQEAAXUpointZ) (`3f461023)]
`3f46116f 48898424e0000000 mov qword ptr [rsp+0E0h],rax ; void(obj::*pp)(point) = &obj::foo;
`3f461177 488d05b9feffff lea rax,[test!ILT+(??_9objobj$B7AA) (`3f461037)]
`3f46117e 48898424e8000000 mov qword ptr [rsp+0E8h],rax ; void(objobj::*vp)() = &objobj::vfoo;
`3f461186 488d8c2498000000 lea rcx,[rsp+98h]
`3f46118e ff9424e8000000 call qword ptr [rsp+0E8h] ; ((&oo)->*vp)(); test!ILT+50(??_9objobj$B7AA) (00000001`3f461037)
`3f461195 ba01000000 mov edx,
`3f46119a 488d8c2498000000 lea rcx,[rsp+98h]
`3f4611a2 ff9424d8000000 call qword ptr [rsp+0D8h] ; ((&oo)->*pi)(1); test!ILT+65(?fooobjQEAAXHZ) (00000001`3f461046)

上面有三处指针赋值,被赋地址分别信息分别如下:

test!ILT+(?fooobjQEAAXHZ):
`3f461046 e955020000 jmp test!obj::foo (`3f4612a0)
:> dt `3f4612a0
obj::foo
void test!obj::foo+(
int) test!ILT+(?fooobjQEAAXUpointZ):
`3f461023 e9a8020000 jmp test!obj::foo (`3f4612d0)
:> dt `3f4612d0
obj::foo
void test!obj::foo+(
point) test!ILT+(??_9objobj$B7AA):
`3f461037 e964050000 jmp test!objobj::`vcall'{8}' (`3f4615a0)
:> dt `3f4615a0
objobj::`vcall'{8}'
Symbol not found.
:> u `3f4615a0 L3
test!objobj::`vcall'{8}':
`3f4615a0 488b01 mov rax,qword ptr [rcx]
`3f4615a3 ff6008 jmp qword ptr [rax+] ; => jmp test!objobj::vfoo

函数的调用都经由一个间接跳转,这是hook的基础,天生带上了M属性,这不是本篇的主题。忽视这个间接跳转(或者看作短路),我们认为非虚的成员函数指针直接指向成员函数本体,但是虚函数指针指向的是一小块类似thunk的处理代码。虚函数是动态绑定的,thunk相关的处理是必要的。vc编译器在可以正确分析出绑定的情况下,将这段thunk处理内联到调用处罢了。

SDK中使用thunk的地方还有,atlthunk.h, olecall_.s, oledisp1.cpp, qithunk.s, stdcallthunk.s。这些使用thunk的地方大都与com相关,目的各不相同。例如,qithunk.s就是queryinterface thunk,也就是用于调试com, 别有用心加了一层模仿IUnknown调用虚函数的过程,使得com的方法被调用前都必须先经过qithunk的虚函数,从而可以被中断而不用知道执行的是哪种具体的com。当你不知道com的调试信息时,也可以中断到com的每个方法入口。qithunk是这里面我认为比较容易分析的,只要了解IUKnown接口和虚函数表就可以分析了。

又如IDispatch是用于实现动态绑定的接口,vbscript和jscript中使用到的对象都实现了这个接口。在script中调用对象的属性或方法时,是通过属性名或方法名来绑定com的执行函数。这种方式跟objc的消息调用在形式上有点像。window.getElementById("form"), 调用的是window.invoke(GETDISPID("getElementById"), ..., args("form"),...); 在objc中[window getElementById:"form"],调用是objc_sendMsg(window, "getElementById:", "form");
本篇浅略提及了thunk和动态绑定,有了感性认识后,分析objc中SEL的动态绑定就不会太陌生了。分析objc的文章也请在未来的日子关注。

成员函数指针,动态绑定(vc平台)的更多相关文章

  1. 函数指针和成员函数指针有什么不同,反汇编带看清成员函数指针的本尊(gcc@x64平台)

    函数指针是什么,可能会答指向函数的指针. 成员函数指针是什么,答指向成员函数的指针. 成员函数指针和函数指针有什么不同? 虚函数指针和非虚成员函数指针有什么不同? 你真正了解成员函数指针了吗? 本篇带 ...

  2. 成员函数指针与高效C++委托 (delegate)

    下载实例源代码 - 18.5 Kb 下载开发包库文件 - 18.6 Kb 概要 很遗憾, C++ 标准中没能提供面向对象的函数指针. 面向对象的函数指针也被称为闭包(closures) 或委托(del ...

  3. 成员函数指针与高性能C++委托

    1 引子 标准C++中没有真正的面向对象的函数指针.这一点对C++来说是不幸的,因为面向对象的指针(也叫做“闭包(closure)”或“委托(delegate)”)在一些语言中已经证明了它宝贵的价值. ...

  4. [转]成员函数指针与高性能的C++委托

    原文(作者:Don Clugston):Member Function Pointers and the Fastest Possible C++ Delegates 译文(作者:周翔): 成员函数指 ...

  5. 为什么 C++ 中成员函数指针是 16 字节?

    当我们讨论指针时,通常假设它是一种可以用 void * 指针来表示的东西,在 x86_64 平台下是 8 个字节大小.例如,下面是来自 维基百科中关于 x86_64 的文章 的摘录: Pushes a ...

  6. C++ 指向成员函数指针问题

    成员函数指针与常规指针不同,一个指向成员变量的指针并不指向一个内存位置.通常最清晰的做法是将指向数据成员的指针看作为一个偏移量. class ru_m { public: typedef int (r ...

  7. C++成员函数指针错误用法警示(成员函数指针与高性能的C++委托,三篇),附好多评论

    今天做一个成绩管理系统的并发引擎,用Qt做的,仿照QtConcurrent搞了个模板基类.这里为了隐藏细节,隔离变化,把并发的东西全部包含在模板基类中.子类只需注册需要并发执行的入口函数即可在单独线程 ...

  8. 自制反汇编工具使用实例 其二(使用xmm寄存器初始化对象,以及空的成员函数指针)

    在反汇编代码中,当看到xmm寄存器,第一反应是将要进行浮点操作或访问,但是更加多的情况是在使用xmm寄存器初始化局部对象. 下面是自制反汇编工具翻译出来的代码: // -[CALayer setAll ...

  9. [Reprint]C++普通函数指针与成员函数指针实例解析

    这篇文章主要介绍了C++普通函数指针与成员函数指针,很重要的知识点,需要的朋友可以参考下   C++的函数指针(function pointer)是通过指向函数的指针间接调用函数.相信很多人对指向一般 ...

随机推荐

  1. 让button的文字换行和左对齐

    btn.titleLabel.numberOfLines = 0;   btn.contentHorizontalAlignment = UIControlContentHorizontalAlign ...

  2. 【MongoDB详细使用教程】五、MongoDB的数据库管理

    目录 1.数据库安全 1.1.创建管理员账号和密码 1.2.设置服务状态为需要验证用户 1.3.创建用户账户和密码 1.4.忘记密码/修改密码 2.主从服务器 2.1.创建服务器目录,用于分别存放主从 ...

  3. rabbitmq学习-如何安装rabbitmq

    学习当然还是需要看官网地址的哈 官网地址 你可能会说老铁,看不懂英文咋办?我只能说各大翻译软件以及广大网友总有一款是你喜欢的 广大网友翻译的 中文文档 什么是rabbitmq? rabbitmq (R ...

  4. Nginx 热部署和日志切割,你学会了吗?

    上篇文章,我们已经安装好 Nginx,并且配置好 Nginx 文件后,这个时候我就需要操作 Nginx 的命令行了,这篇文章主要讲解 Nginx 命令行相关知识,并通过日常遇到的热部署.切割日志文件场 ...

  5. 使用VS2013操作MYSQL8 (ADO.NET方式 & EF6)

    今天有时间测试了一下通过.net环境操作MYSQL数据库,测试过程及结果记录如下: 1.MYSQL安装 (1)我是从MYSQL官网下载的最新版,即MYSQL8.0,在MySql官网的下载页面,找到“M ...

  6. scrapy爬取京东iPhone11评论(一)

    咨询行业中经常接触到文本类信息,无论是分词做词云图,还是整理编码分析用,都非常具有价值. 本文将记录使用scrapy框架爬取京东IPhone11评论的过程,由于一边学习一边实践,更新稍慢请见谅. 1. ...

  7. 第二十五章 system v消息队列(一)

    IPC对象的持续性 随进程持续 :一直存在直到打开的最后一个进程结束.(如pipe和FIFO) 随内核持续 :一直存在直到内核自举(内核自举就是把主引导记录加载到内存,并跳转执行这段内存)或显示删除( ...

  8. Jenkins 结合 ANT 发送测试报告

    全局变量配置 点击 ManageJenkins进入Jenkins 管理 点击 Global Tool Configuration 进入全局变量配置 配置 Ant ,Name 自己定义一个比较好理解的名 ...

  9. MIT线性代数:2.消元法

  10. 学习笔记34_EF上下文管理

    *上下文对象dbContext最好不要频繁的使用Using(var dbContext = new ....):那么就会产生过多的数据库交互:而且每个dbContext中村的数据,由于操作不同,数据可 ...