.Net 7里的函数.Ctor和.CCtor是干啥用的呢?你知道吗
楔子
有小伙伴被面试官问到这个问题,本篇彻底解析下这个问题。
为了彻底点,注意本篇是最底层的.Net 7 RC CLR
运行模型(汇编)为基础进行全局剖析,局部业务分析。
如有疏漏,请斧正。
目的非手段
这两个函数比较特殊的存在,.Ctor
是非静态默认实例化。.CCtor
是静态默认实例化。这两个函数伴随着.Net
任何对象的实例化都自动存在于这个对象当中。
跟踪.CCtor
可以在全局静态对象下断点,观察它的里面运行。
跟踪.Ctor
可以通过!name2ee
模块 模块.类名..Ctor
找到JITTED Code Address
,观察它的运行。正如本段题所说,这只是手段,不是目的。所以下面看目的。
.Ctor目的
先来看下非静态默认构造函数.Ctor
。上一段代码:
internal class Program
{
public class ABC
{
}
static void Main(string[] args)
{
ABC abc = new ABC();
Console.ReadLine();
}
}
直接给它反编译:
00007FFDF2FA03B0 55 push rbp
00007FFDF2FA03B1 48 83 EC 40 sub rsp,40h
00007FFDF2FA03B5 48 8D 6C 24 40 lea rbp,[rsp+40h]
00007FFDF2FA03BA C5 D8 57 E4 vxorps xmm4,xmm4,xmm4
00007FFDF2FA03BE C5 FA 7F 65 E8 vmovdqu xmmword ptr [rbp-18h],xmm4
00007FFDF2FA03C3 33 C0 xor eax,eax
00007FFDF2FA03C5 48 89 45 F8 mov qword ptr [rbp-8],rax
00007FFDF2FA03C9 48 89 4D 10 mov qword ptr [rbp+10h],rcx
00007FFDF2FA03CD 83 3D BC E9 19 00 00 cmp dword ptr [7FFDF313ED90h],0
00007FFDF2FA03D4 74 05 je 00007FFDF2FA03DB
00007FFDF2FA03D6 E8 B5 BF 79 5E call JIT_DbgIsJustMyCode (07FFE5173C390h)
00007FFDF2FA03DB 90 nop
00007FFDF2FA03DC 48 B9 30 F6 5B F3 FD 7F 00 00 mov rcx,7FFDF35BF630h
00007FFDF2FA03E6 E8 75 7C C1 5E call JIT_TrialAllocSFastMP_InlineGetThread (07FFE51BB8060h)
00007FFDF2FA03EB 48 89 45 F0 mov qword ptr [rbp-10h],rax
00007FFDF2FA03EF 48 8B 4D F0 mov rcx,qword ptr [rbp-10h]
// 这个地方是调用了.Ctor
00007FFDF2FA03F3 FF 15 0F 8D 60 00 call qword ptr [7FFDF35A9108h]
00007FFDF2FA03F9 48 8B 45 F0 mov rax,qword ptr [rbp-10h]
00007FFDF2FA03FD 48 89 45 F8 mov qword ptr [rbp-8],rax
00007FFDF2FA0401 FF 15 A9 93 60 00 call qword ptr [7FFDF35A97B0h]
00007FFDF2FA0407 48 89 45 E8 mov qword ptr [rbp-18h],rax
00007FFDF2FA040B 90 nop
00007FFDF2FA040C 90 nop
00007FFDF2FA040D 48 83 C4 40 add rsp,40h
00007FFDF2FA0411 5D pop rbp
00007FFDF2FA0412 C3 ret
调用.Ctor
的地方注释了下,如果直接进入会调用到PrecodeFixupThunk
。所以这里需要在PreStubWorker
下断点。一路跟踪下去发现这个.Ctor
是利用预备的IL代码,让RyuJIt
对它进行一个编译
.Ctor
调用堆栈:
coreclr.dll!MethodDesc::JitCompileCodeLocked 行 952 C++
coreclr.dll!MethodDesc::JitCompileCodeLockedEventWrapper 行 823 C++
coreclr.dll!MethodDesc::JitCompileCode 行 763 C++
coreclr.dll!MethodDesc::PrepareILBasedCode 行 426 C++
coreclr.dll!MethodDesc::PrepareCode 行 323 C++
coreclr.dll!CodeVersionManager::PublishVersionableCodeIfNecessary 行 1698 C++
coreclr.dll!MethodDesc::DoPrestub 行 2109 C++
coreclr.dll!PreStubWorker 行 1938
coreclr.dll!ThePreStub(
到JitCompileCodeLocked
里面调用了UnsafeJitFunction
为止,因为后面都是RyuJit的
复杂编译过程,此处不述。
我们来看下UnsafeJitFunction
返回的pCode
地址处的汇编代码:
00007FFDF2F80430 55 push rbp
00007FFDF2F80431 57 push rdi
00007FFDF2F80432 48 83 EC 28 sub rsp,28h
00007FFDF2F80436 48 8D 6C 24 30 lea rbp,[rsp+30h]
00007FFDF2F8043B 48 89 4D 10 mov qword ptr [rbp+10h],rcx
00007FFDF2F8043F 83 3D 4A E9 19 00 00 cmp dword ptr [7FFDF311ED90h],0
00007FFDF2F80446 74 05 je 00007FFDF2F8044D
00007FFDF2F80448 E8 43 BF 7B 5E call JIT_DbgIsJustMyCode (07FFE5173C390h)
00007FFDF2F8044D 48 8B 4D 10 mov rcx,qword ptr [rbp+10h]
00007FFDF2F80451 FF 15 D9 0B E5 FF call qword ptr [7FFDF2DD1030h]
00007FFDF2F80457 90 nop
00007FFDF2F80458 90 nop
00007FFDF2F80459 48 83 C4 28 add rsp,28h
00007FFDF2F8045D 5F pop rdi
00007FFDF2F8045E 5D pop rbp
00007FFDF2F8045F C3 ret
它里面就调用了一个Call
,也就是这句话:
call qword ptr [7FFDF2DD1030h]
这个十六进制的7FFDF2DD1030h
是个啥呢?
继续跟进下:
0x00007FFDF2DD1030 00007ffe50357230
它里面包含了一个地址00007ffe50357230
看下这个地址的汇编代码:
00007FFE50357230 C3 ret
它直接返回了。
所以这得出了一个什么结论呢?也就是说在当前这个例子中,.Ctor啥都没做。
.CCtor目的
来看下静态的默认构造函数干了些啥。先上代码:
internal class Program
{
static string a ="abcd";
static void Main(string[] args)
{
string i = a;
Console.WriteLine(a);
Console.ReadLine();
}
}
同样反编译下:
00007FFDF01903B0 55 push rbp
00007FFDF01903B1 57 push rdi
00007FFDF01903B2 48 83 EC 28 sub rsp,28h
00007FFDF01903B6 48 8D 6C 24 30 lea rbp,[rsp+30h]
00007FFDF01903BB 33 C0 xor eax,eax
00007FFDF01903BD 48 89 45 F0 mov qword ptr [rbp-10h],rax
00007FFDF01903C1 48 89 4D 10 mov qword ptr [rbp+10h],rcx
00007FFDF01903C5 83 3D C4 E9 19 00 00 cmp dword ptr [7FFDF032ED90h],0
00007FFDF01903CC 74 05 je 00007FFDF01903D3
00007FFDF01903CE E8 BD BF 7D 5E call JIT_DbgIsJustMyCode (07FFE4E96C390h)
00007FFDF01903D3 90 nop
00007FFDF01903D4 48 B9 60 EF 32 F0 FD 7F 00 00 mov rcx,7FFDF032EF60h
00007FFDF01903DE BA 04 00 00 00 mov edx,4
// 可以看到这个 string 静态对象并没有调用.CCtor。
// 那是否说明上面的说法不对呢?注意看,他实际调用了
// JIT_GetSharedNonGCStaticBase_SingleAppDomain,
// 而这个就是关键所在
00007FFDF01903E3 E8 48 7E C5 5E call JIT_GetSharedNonGCStaticBase_SingleAppDomain (07FFE4EDE8230h)
00007FFDF01903E8 8B 0D AA EB 19 00 mov ecx,dword ptr [7FFDF032EF98h]
00007FFDF01903EE FF 15 7C 94 60 00 call qword ptr [7FFDF0799870h]
00007FFDF01903F4 90 nop
00007FFDF01903F5 FF 15 9D 93 60 00 call qword ptr [7FFDF0799798h]
00007FFDF01903FB 48 89 45 F0 mov qword ptr [rbp-10h],rax
00007FFDF01903FF 90 nop
00007FFDF0190400 90 nop
00007FFDF0190401 48 83 C4 28 add rsp,28h
00007FFDF0190405 5F pop rdi
00007FFDF0190406 5D pop rbp
00007FFDF0190407 C3 ret
00007FFDF0190408 19 06 sbb dword ptr [rsi],eax
看这段代码上面的注释,这段代码里面并没有.CCtor
被调用的痕迹。
而它的奥秘在
JIT_GetSharedNonGCStaticBase_SingleAppDomain
函数里面。
JIT_GetSharedNonGCStaticBase_SingleAppDomain
又调用了
JIT_GetSharedNonGCStaticBase_Helper
看下堆栈
> coreclr.dll!MethodTable::RunClassInitEx 行 3591 C++
coreclr.dll!MethodTable::DoRunClassInitThrowing 行 3792 C++
coreclr.dll!MethodTable::CheckRunClassInitThrowing 行 3929 C++
coreclr.dll!JIT_GetSharedNonGCStaticBase_Helper 行 1401 C++
函数 RunClassInitEx
代码如下:
BOOL MethodTable::RunClassInitEx(OBJECTREF *pThrowable)
{
//为了方便观看 此处省略部分代码
PCODE pCctorCode = pCanonMT->GetSlot(pCanonMT->GetClassConstructorSlot());
//为了方便观看 此处省略部分代码
PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pCctorCode);
DECLARE_ARGHOLDER_ARRAY(args, 0);
CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE;
CALL_MANAGED_METHOD_NORET(args);
//为了方便观看 此处省略部分代码
变量pCctorCode
就是.CCtor
的函数头地址。而后面的一堆的宏定义实际上是调用了函数 DispatchCallSimple
,
而DispatchCallSimple
又调用了CallDescrWorkerWithHandler
然后又调用了PrecodeFixupThunk
下面调用了PreStubWorker
PreStubWorker
通过 call rax
命令跳转到调用的函数的函数头地址,比如本例的 .CCtor
函数头的地址。
00007FFE8BB289C0 E8 DB FE 8F FF call PreStubWorker (07FFE8B4288A0h)
00007FFE8BB289C5 66 0F 6F 44 24 20 movdqa xmm0,xmmword ptr [rsp+20h]
00007FFE8BB289CB 66 0F 6F 4C 24 30 movdqa xmm1,xmmword ptr [rsp+30h]
00007FFE8BB289D1 66 0F 6F 54 24 40 movdqa xmm2,xmmword ptr [rsp+40h]
00007FFE8BB289D7 66 0F 6F 5C 24 50 movdqa xmm3,xmmword ptr [rsp+50h]
00007FFE8BB289DD 48 8B 8C 24 B0 00 00 00 mov rcx,qword ptr [rsp+0B0h]
00007FFE8BB289E5 48 8B 94 24 B8 00 00 00 mov rdx,qword ptr [rsp+0B8h]
00007FFE8BB289ED 4C 8B 84 24 C0 00 00 00 mov r8,qword ptr [rsp+0C0h]
00007FFE8BB289F5 4C 8B 8C 24 C8 00 00 00 mov r9,qword ptr [rsp+0C8h]
00007FFE8BB289FD 48 83 C4 68 add rsp,68h
00007FFE8BB28A01 5F pop rdi
00007FFE8BB28A02 5E pop rsi
00007FFE8BB28A03 5B pop rbx
00007FFE8BB28A04 5D pop rbp
00007FFE8BB28A05 41 5C pop r12
00007FFE8BB28A07 41 5D pop r13
00007FFE8BB28A09 41 5E pop r14
00007FFE8BB28A0B 41 5F pop r15
// 这个rax 就是 .CCtor的函数头的地址
00007FFE8BB28A0D 48 FF E0 jmp rax
jmp rax
跳转到了如下:
00007FFE2CFE8888 FF 25 FA 0F 00 00 jmp qword ptr [7FFE2CFE9888h]
7FFE2CFE9888h
地址的值是00007FFE8A50C7A0
注意这句代码
static string a ="abcd";
它实际上被编译成了一个函数,当运行到.CCtor
的时候,会调用它,然后对它进行赋值 abcd
>>> 00007ffe`06ac29e0 55 push rbp
00007ffe`06ac29e1 4883ec20 sub rsp,20h
00007ffe`06ac29e5 488d6c2420 lea rbp,[rsp+20h]
00007ffe`06ac29ea 833d9f410c0000 cmp dword ptr [00007ffe`06b86b90],0
00007ffe`06ac29f1 7405 je ConsoleApp3!ConsoleApp3.Program..cctor+0x18 (00007ffe`06ac29f8)
00007ffe`06ac29f3 e8e8a4cd5f call coreclr!JIT_DbgIsJustMyCode (00007ffe`6679cee0)
00007ffe`06ac29f8 48bad83000186c020000 mov rdx,26C180030D8h
00007ffe`06ac2a02 488b12 mov rdx,qword ptr [rdx]
00007ffe`06ac2a05 48b9902e00186c020000 mov rcx,26C18002E90h
00007ffe`06ac2a0f e8fc85bb5f call coreclr!JIT_CheckedWriteBarrier (00007ffe`6667b010)
00007ffe`06ac2a14 90 nop
00007ffe`06ac2a15 4883c420 add rsp,20h
00007ffe`06ac2a19 5d pop rbp
00007ffe`06ac2a1a c3 ret
JIT_CheckedWriteBarrier
的原型如下:
extern "C" HCIMPL2_RAW(VOID, JIT_CheckedWriteBarrier, Object **dst, Object *ref)
很明显,他这就是把ref
指向的object
完整的传递给dst
。也就是赋值给静态字符串a
。
寄存器rcx
表示dst
,rdx
表示ref
。此处可以通过 !dumpobj rdx
来查被看对象。
那么总结下,.CCtor
的作用就是把静态的全局变量对象进行一个初始化,这个结果也说明,静态全局变量不是在CLR
初始化的时候初始化,而是在当前类的.CCtor
里面初始化的。
作者:江湖评谈
原文:https://www.cnblogs.com/tangyanzhi1111/p/16868020.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
.Net 7里的函数.Ctor和.CCtor是干啥用的呢?你知道吗的更多相关文章
- javascript中利用柯里化函数实现bind方法
柯理化函数思想:一个js预先处理的思想:利用函数执行可以形成一个不销毁的作用域的原理,把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个小函数,以后我们执行的都是小函数,在小函数中把之前预 ...
- CTEX里的函数、符号及特殊字符
CTEX里的函数.符号及特殊字符 声调 语法 效果 语法 效果 语法 效果 \bar{a} \acute{a} \check{a} \grave{a} \tilde{a} \hat ...
- c#调用js,以及js调用C#里的函数, c#自己生成js代码,实现对web的控制
using mshtml;using System;using System.Collections.Generic;using System.Linq;using System.Security.P ...
- .ctor,.cctor 以及 对象的构造过程
摘要: .ctor,.cctor 以及 对象的构造过程.ctor:简述:构造函数,在类被实例化时,它会被自动调用.当C#的类被编译后,在IL代码中会出现一个名为.ctor的方法,它就是我们的构造函数, ...
- 柯里化函数之Javascript
柯里化函数之Javascript 定义 依据定义来说,柯里化就是将一个接收"多个"參数的函数拆分成一个或者很多个接收"单一"參数的函数.定义看起来是比較抽象的. ...
- 浅谈JavaScript中的柯里化函数
首先,不可避免的要引经据典啦,什么是柯里化函数呢(from baidu): 在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返 ...
- Swift # 柯里化函数
前言 此次文章,讲述的是Swift的一个新特性(柯里化函数),可能很多iOS开发人员是第一次听这个词汇,包括我自己也是,自己也用了几天时间才总结出来,希望能帮助到各位咯,个人感觉偏向有开发经验的码友, ...
- C语言中如何调用另一个源文件里的函数
在开发大型项目时,我们常常需要将一份源码分成多个源文件来进行编写,这样可以方便后期的维护.下面就介绍如何从一个源文件里调用另一个源文件的函数. 在源文件A1.c中调用A2.c 中的函数有两种方法: 1 ...
- 在js文件里调用另一个js文件里的函数
这个是我今天解决的一个小问题,我在创建界面的时候,根据不同的界面需求对应创建了不同的js文件来搭建界面,搭建完毕之后再将各个生成页面的函数汇总到主界面上,通过visibility属性切换显示,这时候出 ...
随机推荐
- noip2015提高组初赛
一.单项选择题(共15题,每题1.5分,共计22.5分:每题有且仅有一个正确选项) 线性表若采用链表存储结构,要求内存中可用存储单元地址( ). A. 必须连续 B. 部分地址必须连续 C. 一定不连 ...
- 【设计模式】Java设计模式 - 观察者模式
[设计模式]Java设计模式 - 观察者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 @一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长 ...
- 如何查找并简单分析core文件
当系统发生coredump时,通常需要通过分析core文件来定位问题所在,但实际工作中,有时却发现core 文件找不到,或者core文件被删除了. 一.core文件没有生成 KINGBASE core ...
- 066_末晨曦Vue技术_过渡 & 动画之多个元素的过渡
多个元素的过渡 点击打开视频讲解更加详细 我们之后讨论多个组件的过渡,对于原生标签可以使用 v-if/v-else.最常见的多标签过渡是一个列表和描述这个列表为空消息的元素: <transiti ...
- Gitea v1.17.0 正式发布 | 集成软件包管理器、容器镜像仓库
我们自豪地宣布 Gitea v1.17.0 发布了.本次发布带来了诸多新特性和累积的更新,我们强烈建议用户在更新到最新版本之前仔细阅读发行注记. 在 1.17.0 版本的开发中我们一共合并了 645 ...
- JS中如何删除某个父元素下的所有子元素?
JS中如何删除某个父元素下的所有子元素?这里我介绍几种方法: 1.通过元素的 innerHTML 属性来删除 这种方式我觉得是最有方便的,直接找到你想要的父元素,直接令其 element.innerH ...
- 9. 第八篇 kube-controller-manager安装及验证
文章转载自:https://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247483826&idx=1&sn=88f0cef6 ...
- 13. Fluentd输出插件:in_forward用法详解
in_forward插件通常用于从其他节点接收日志事件,这些节点包括其他Fluentd实例.fluent-cat命令行或者Fluentd客户端程序.这是目前效率最高的日志事件接收方法. in_forw ...
- 12. Fluentd部署:多Workers进程模式
介绍如何使用Fluentd的多worker模式处理高访问量的日志事件.此模式会运行多个worker进程以最大利用多核CPU. 原理 默认情况下,一个Fluentd实例会运行一个监控进程和一个工作进程. ...
- 工厂想采购一套信息化生产执行系统mes,不知道用哪家比较好?
好的信息化生产执行系统MES多的是,但是否适用于贵工厂那就不得而知了,要知道,不同行业.不同产品.不同规模的工厂用同一套系统效果呈现出来都不一样的,所以匹配很重要,个性化差异化.变化性等决定了一个工厂 ...