成员函数指针,动态绑定(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)是通过指向函数的指针间接调用函数.相信很多人对指向一般 ...
随机推荐
- Javascript字符串常用方法详解
字符串 字符串就是一个或多个排列在一起的字符,放在单引号或双引号之中. 'abc'"abc" length属性 js里的字符串类似于数组,都是一个一个字符拼凑在一起组成的,因此可以 ...
- python深拷贝与浅拷贝的区别
可变对象:一个对象在不改变其所指向的地址的前提下,可以修改其所指向的地址中的值 不可变对象:一个对象所指向的地址上值是不能修改的,如果你修改了这个对象的值,那么它指向的地址就改变了,相当于你把这个对象 ...
- 百万年薪python之路 -- re模块
re模块 re模块是python用来描述正则表达式的一个模块. 正则表达式本身也和python没有什么关系,就是匹配字符串内容的一种规则. 官方定义:正则表达式是对字符串操作的一种逻辑公式,就是用事先 ...
- Git 项目提交新仓库
提示:进入项目文件操作 步骤: 1.git init ----------初始化git仓库 2.git remote add origin 你的项目地址 ------------------如: ...
- 文本查重算法SimHash
1.介绍 爬虫采集了大量的文本数据,如何进行去重?可以使用文本计算MD5,然后与已经抓取下来的MD5集合进行比较,但这种做法有个问题,文本稍有不同MD5值都会大相径庭, 无法处理文本相似问题.另一种方 ...
- 数据结构(四十一)多路查找树(B树)
一.多路查找树的背景 前面所讨论的查找算法都是在内存中进行的,它们适用于较小的文件,而对于较大的.存放在外存储器上的文件就不合适了,对于此类大规模的文件,即使是采用了平衡二叉树,在查找效率上仍然较低. ...
- StackView在Android的应用
StackView是AdapterViewAnimator的子类,它用于显示Adapter提供的一系列View.StackView将会以“堆叠”的方式来显示多个列表项.为了控制StackView现实的 ...
- spring cloud 2.x版本 Eureka Server服务注册中心教程
本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 1.创建服务注册中心 1.1 新建Spring boot工程:eureka-server 1 ...
- 开源.Net Standard版华为物联网北向接口SDK
最近用到了华为的物联网平台API,但是官方没有.Net版的SDK,所以就自己封装了一个,开源出来给有需要的朋友,同时也算是为.Net Core的发展做点小贡献~ 源码地址:https://github ...
- 爬虫基本库的使用---requests库
使用requests---实现Cookies.登录验证.代理设置等操作 处理网页验证和Cookies时,需要写Opener和Handler来处理,为了更方便地实现这些操作,就有了更强大的库reques ...