成员函数指针,动态绑定(vc平台)
上一篇介绍了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平台)的更多相关文章
- 函数指针和成员函数指针有什么不同,反汇编带看清成员函数指针的本尊(gcc@x64平台)
函数指针是什么,可能会答指向函数的指针. 成员函数指针是什么,答指向成员函数的指针. 成员函数指针和函数指针有什么不同? 虚函数指针和非虚成员函数指针有什么不同? 你真正了解成员函数指针了吗? 本篇带 ...
- 成员函数指针与高效C++委托 (delegate)
下载实例源代码 - 18.5 Kb 下载开发包库文件 - 18.6 Kb 概要 很遗憾, C++ 标准中没能提供面向对象的函数指针. 面向对象的函数指针也被称为闭包(closures) 或委托(del ...
- 成员函数指针与高性能C++委托
1 引子 标准C++中没有真正的面向对象的函数指针.这一点对C++来说是不幸的,因为面向对象的指针(也叫做“闭包(closure)”或“委托(delegate)”)在一些语言中已经证明了它宝贵的价值. ...
- [转]成员函数指针与高性能的C++委托
原文(作者:Don Clugston):Member Function Pointers and the Fastest Possible C++ Delegates 译文(作者:周翔): 成员函数指 ...
- 为什么 C++ 中成员函数指针是 16 字节?
当我们讨论指针时,通常假设它是一种可以用 void * 指针来表示的东西,在 x86_64 平台下是 8 个字节大小.例如,下面是来自 维基百科中关于 x86_64 的文章 的摘录: Pushes a ...
- C++ 指向成员函数指针问题
成员函数指针与常规指针不同,一个指向成员变量的指针并不指向一个内存位置.通常最清晰的做法是将指向数据成员的指针看作为一个偏移量. class ru_m { public: typedef int (r ...
- C++成员函数指针错误用法警示(成员函数指针与高性能的C++委托,三篇),附好多评论
今天做一个成绩管理系统的并发引擎,用Qt做的,仿照QtConcurrent搞了个模板基类.这里为了隐藏细节,隔离变化,把并发的东西全部包含在模板基类中.子类只需注册需要并发执行的入口函数即可在单独线程 ...
- 自制反汇编工具使用实例 其二(使用xmm寄存器初始化对象,以及空的成员函数指针)
在反汇编代码中,当看到xmm寄存器,第一反应是将要进行浮点操作或访问,但是更加多的情况是在使用xmm寄存器初始化局部对象. 下面是自制反汇编工具翻译出来的代码: // -[CALayer setAll ...
- [Reprint]C++普通函数指针与成员函数指针实例解析
这篇文章主要介绍了C++普通函数指针与成员函数指针,很重要的知识点,需要的朋友可以参考下 C++的函数指针(function pointer)是通过指向函数的指针间接调用函数.相信很多人对指向一般 ...
随机推荐
- echarts折线图动态改变数据时的一个bug
echarts折线图中当增加dataZoom,修改start大于0的时候,会出现折线混乱,变成竖直的线,绘制有问题. 解决方法,在dataZoom中增加filterMode: 'empty' http ...
- css的简单使用
css语法 id选择器: id 选择器可以为标有特定 id 的 HTML 元素指定特定的样式. HTML元素以id属性来设置id选择器,CSS 中 id 选择器以 "#" 来定义. ...
- Mysql高手系列 - 第26篇:聊聊如何使用mysql实现分布式锁
Mysql系列的目标是:通过这个系列从入门到全面掌握一个高级开发所需要的全部技能. 欢迎大家加我微信itsoku一起交流java.算法.数据库相关技术. 这是Mysql系列第26篇. 本篇我们使用my ...
- 由浅入深:Python 中如何实现自动导入缺失的库?
在写 Python 项目的时候,我们可能经常会遇到导入模块失败的错误:ImportError: No module named 'xxx' 或者 ModuleNotFoundError: No mod ...
- 你不知道的DIV+CSS的命名规则
搜索引擎优化(seo)有很多工作要做,其中对代码的优化是一个很关键的步骤.为了更加符合SEO的规范,下面是目前比较好的CSS+DIV的命名规则 1DIV CLASS或者ID 页头:header 登录条 ...
- MyBatis 示例-传递多个参数
映射器的主要元素: 本章介绍 select 元素中传递多个参数的处理方式. 测试类:com.yjw.demo.MulParametersTest 使用 Map 传递参数(不建议使用) 使用 MyBat ...
- 用FILE*指针对象读文件的方式。
先直接上代码: ]; ]; char* ptr1; ]; FILE* fh; QString strpath = getenv("GCDIR"); QString str_in ...
- 基于 HTML5 + Canvas 实现的 PID 可视化系统
前言 随着工业物联网和互联网技术的普及和发展,人工填料的方式已经逐渐被机械设备取代.工业厂商减小误操作.提升设备安全以及追求高效率等制造特点对设备的要求愈加高标准.严要求.同时机械生产以后还需遵从整个 ...
- Java多线程编程(五)定时器Timer
一.定时器Timer的使用 在JDK库中Timer类主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务.Timer类的主要作用就是设置计划任务,但封装任务的类确实TimerTask类,执行计 ...
- 【MySQL】MySQL服务启动失解决方法
写在前面的话:前段时间,为了更加流畅愉快的玩PUBG,我在任务管理器中,关闭了mysqld服务.后来我在使用MySQL数据库服务的时候,发现去到MySQL安装目录下双击运行mysqld.exe(数据库 ...