公司网站如何注册四川网站建设价格
web/
2025/10/3 7:49:23/
文章来源:
公司网站如何注册,四川网站建设价格,产品宣传册设计与制作,常州武进网站建设首先说说为什么要写这个系列#xff0c;大概有两点原因。这种文章阅读量确实高...对 IL 和 汇编代码 的学习巩固所以就决定写一下这个系列#xff0c;如果大家能从中有所收获#xff0c;那就更好啦#xff01;一#xff1a;params 应用层玩法 首先上一段 测试代码。class … 首先说说为什么要写这个系列大概有两点原因。这种文章阅读量确实高...对 IL 和 汇编代码 的学习巩固所以就决定写一下这个系列如果大家能从中有所收获那就更好啦一params 应用层玩法 首先上一段 测试代码。class Program{static void Main(string[] args){Test(100, 200, 300);Test();}static void Test(params int[] list){Console.WriteLine($list.length{list.Length});}}输出结果如下可以看出如果给 方法形参 加上 params 前缀在传递 方法实参 上就特别灵活点赞。接下来我们来看看这么灵活的 实参传递 底层到底是怎么玩的我们先从 IL 层面探究下。二IL 层面解读 用 ILSpy 打开我们的 exe 看看 IL 代码.method private hidebysig static void Main (string[] args) cil managed
{// Method begins at RVA 0x2050// Code size 37 (0x25).maxstack 8.entrypointIL_0000: nopIL_0001: ldc.i4.3IL_0002: newarr [mscorlib]System.Int32IL_0007: dupIL_0008: ldtoken field valuetype PrivateImplementationDetails/__StaticArrayInitTypeSize12 PrivateImplementationDetails::9AC9CF706FBD14D039E0436219C5D852927E5F69295F2EF423AE897345197B2AIL_000d: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle)IL_0012: call void ConsoleApp2.Program::Test(int32[])IL_0017: nopIL_0018: ldc.i4.0IL_0019: newarr [mscorlib]System.Int32IL_001e: call void ConsoleApp2.Program::Test(int32[])IL_0023: nopIL_0024: ret
} // end of method Program::Main.method private hidebysig static void Test (int32[] list) cil managed
{.param [1].custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() (01 00 00 00)// Method begins at RVA 0x2076// Code size 26 (0x1a).maxstack 8IL_0000: nopIL_0001: ldstr list.length{0}IL_0006: ldarg.0IL_0007: ldlenIL_0008: conv.i4IL_0009: box [mscorlib]System.Int32IL_000e: call string [mscorlib]System.String::Format(string, object)IL_0013: call void [mscorlib]System.Console::WriteLine(string)IL_0018: nopIL_0019: ret
} // end of method Program::Test上面是 Main 和 Test 方法的IL代码我们逐一看一下。1. Test 方法从 int32[] list 参数看并没有所谓的 params这也就说明它是 C#编译器 玩的一个手段而已在方法调用前就已经构建好了。2. Main方法可以看到 IL 层面的 Test(100, 200, 300) 已经变成了下面五行代码。IL_0002: newarr [mscorlib]System.Int32IL_0007: dupIL_0008: ldtoken field valuetype PrivateImplementationDetails/__StaticArrayInitTypeSize12 PrivateImplementationDetails::9AC9CF706FBD14D039E0436219C5D852927E5F69295F2EF423AE897345197B2AIL_000d: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle)IL_0012: call void ConsoleApp2.Program::Test(int32[])逻辑大概就是:用 newarr 构建初始化 int[] 数组。用 ldtoken 从程序元数据中提取 1,2,3。用 InitializeArray 来将 123 构建到数组中。有了这些指令我相信 JIT 就知道怎么做了。再看 Test() 的 IL 指令只有一行 newarr [mscorlib]System.Int32 初始化。所以本质上来说就是提前构建好 array然后进行参数传递仅此而已。。。三汇编层面解读 有了 newarr ldtoken call 三条指令接下来我们读一下汇编层做了什么使用 windbg 打开 exe简化后的汇编代码如下0:000 !U /d 009b0848
Normal JIT generated code
ConsoleApp2.Program.Main(System.String[])
Begin 009b0848, size 77D:\net5\ConsoleApp4\ConsoleApp2\Program.cs 14:009b0848 55 push ebp
009b0849 8bec mov ebp,esp
009b084b 83ec10 sub esp,10h
009b084e 33c0 xor eax,eax
009b0850 8945f4 mov dword ptr [ebp-0Ch],eax
009b0853 8945f8 mov dword ptr [ebp-8],eax
009b0856 8945f0 mov dword ptr [ebp-10h],eax
009b0859 894dfc mov dword ptr [ebp-4],ecx
009b085c 833df042710000 cmp dword ptr ds:[7142F0h],0
009b0863 7405 je 009b086a
009b0865 e816f55264 call clr!JIT_DbgIsJustMyCode (64edfd80)
009b086a 90 nopD:\net5\ConsoleApp4\ConsoleApp2\Program.cs 15:
009b086b b95e186763 mov ecx,offset mscorlib_ni!System.Text.Encoding.GetEncodingCodePage(Int32)$##6006719 PERF (mscorlib_ni0x185e) (6367185e)
009b0870 ba03000000 mov edx,3
009b0875 e8b229d5ff call 0070322c (JitHelp: CORINFO_HELP_NEWARR_1_VC)
009b087a 8945f4 mov dword ptr [ebp-0Ch],eax
009b087d b9e04d7100 mov ecx,714DE0h
009b0882 e819c91f64 call clr!JIT_GetRuntimeFieldStub (64bad1a0)
009b0887 8945f8 mov dword ptr [ebp-8],eax
009b088a 8d45f8 lea eax,[ebp-8]
009b088d ff30 push dword ptr [eax]
009b088f 8b4df4 mov ecx,dword ptr [ebp-0Ch]
009b0892 e8f9c61f64 call clr!ArrayNative::InitializeArray (64bacf90)
009b0897 8b4df4 mov ecx,dword ptr [ebp-0Ch]
009b089a ff15904d7100 call dword ptr ds:[714D90h] (ConsoleApp2.Program.Test(Int32[]), mdToken: 06000002)
009b08a0 90 nopD:\net5\ConsoleApp4\ConsoleApp2\Program.cs 16:
009b08a1 b95e186763 mov ecx,offset mscorlib_ni!System.Text.Encoding.GetEncodingCodePage(Int32)$##6006719 PERF (mscorlib_ni0x185e) (6367185e)
009b08a6 33d2 xor edx,edx
009b08a8 e87f29d5ff call 0070322c (JitHelp: CORINFO_HELP_NEWARR_1_VC)
009b08ad 8945f0 mov dword ptr [ebp-10h],eax
009b08b0 8b4df0 mov ecx,dword ptr [ebp-10h]
009b08b3 ff15904d7100 call dword ptr ds:[714D90h] (ConsoleApp2.Program.Test(Int32[]), mdToken: 06000002)
009b08b9 90 nopD:\net5\ConsoleApp4\ConsoleApp2\Program.cs 17:
009b08ba 90 nop
009b08bb 8be5 mov esp,ebp
009b08bd 5d pop ebp
009b08be c3 ret1. newarr可以很清楚的看到newarr 调用了 CLR 中的jithelper函数 CORINFO_HELP_NEWARR_1_VC 下的 JIT_NewArr1 方法大家有兴趣可以看下 jitheapler.cpp调用完之后初始化数组就出来了。从dp看数组只申明了 length3,还并没有数组元素也就说所谓的初始化。2. ldtoken InitializeArray刚才也说到了, ldtoken 是在运行时提取元数据那就必须要解析 PE 头在 clr 层面有一个 PEDecoder::GetRvaData 方法就是用来解析运行时数据它是发生在 ArrayNative::InitializeArray 方法中所以我们下两个 bu 命令拦截。0:000 bu clr!ArrayNative::InitializeArray
0:000 bu clr!PEDecoder::GetRvaData
0:000 g
Breakpoint 3 hit
eax0019f500 ebx0019f5ac ecx024c2338 edx006fb930 esi00000000 edi0019f520
eip64bacf90 esp0019f4f0 ebp0019f508 iopl0 nv up ei pl zr na pe nc
cs0023 ss002b ds002b es002b fs0053 gs002b efl00000246
clr!ArrayNative::InitializeArray:
64bacf90 6a78 push 78h
0:000 g
Breakpoint 0 hit
eax00713448 ebx00624044 ecx0071344c edx0071dc28 esi00624044 edi00624de0
eip64befcfc esp0019f400 ebp0019f410 iopl0 nv up ei pl zr na pe nc
cs0023 ss002b ds002b es002b fs0053 gs002b efl00000246
clr!PEDecoder::GetRvaData:
64befcfc 55 push ebp
0:000 k # ChildEBP RetAddr
00 0019f410 64b63be5 clr!PEDecoder::GetRvaData
01 0019f410 64b63bb3 clr!Module::GetRvaField0x40
02 0019f44c 64bad0a7 clr!FieldDesc::GetStaticAddressHandle0xdd
03 0019f4ec 00680897 clr!ArrayNative::InitializeArray0x11a
0:000 bp 64b63be5
0:000 g
eax00402928 ebx00624044 ecx0071344c edx0071dc28 esi00624044 edi00624de0
eip64b63be5 esp0019f40c ebp0019f410 iopl0 nv up ei pl nz na pe nc
cs0023 ss002b ds002b es002b fs0053 gs002b efl00000206
clr!Module::GetRvaField0x40:
64b63be5 5e pop esi从输出看上面的 GetRvaField 方法的返回值会送到 eax 上接下来我们验证下 eax 上的值是不是参数 100200300 。0:000 dp eax L3
00402928 00000064 000000c8 0000012c上面三个就是 16进制的表示接下来我们再验证下这三个值是怎么赋到初始化数组中的可以用 ba 命令对 内存地址 进行拦截。0:000 ba r4 023e2338 0x8
0:000 ba r4 023e2338 0x8 0x4
0:000 ba r4 023e2338 0x8 0x4 0x4
0:000 g
Breakpoint 3 hit
eax0000000c ebx00000000 ecx00000003 edx00000064 esi00402928 edi023e2340
eip6a91d68b esp0019f440 ebp0019f4ec iopl0 nv up ei pl nz na pe nc
cs0023 ss002b ds002b es002b fs0053 gs002b efl00000206
VCRUNTIME140_CLR0400!memcpy0x50b:
6a91d68b 83c704 add edi,4
0:000 g
Breakpoint 4 hit
eax0000000c ebx00000000 ecx00000002 edx000000c8 esi0040292c edi023e2344
eip6a91d68b esp0019f440 ebp0019f4ec iopl0 nv up ei pl nz na po nc
cs0023 ss002b ds002b es002b fs0053 gs002b efl00000202
VCRUNTIME140_CLR0400!memcpy0x50b:
6a91d68b 83c704 add edi,4
0:000 g
Breakpoint 5 hit
eax0000000c ebx00000000 ecx00000001 edx0000012c esi00402930 edi023e2348
eip6a91d68b esp0019f440 ebp0019f4ec iopl0 nv up ei pl nz na po nc
cs0023 ss002b ds002b es002b fs0053 gs002b efl00000202
VCRUNTIME140_CLR0400!memcpy0x50b:
6a91d68b 83c704 add edi,40:000 dp 023e2338 L5
023e2338 6368426c 00000003 00000064 000000c8
023e2348 0000012c接下来稍微解释下 ba r4 023e2338 0x8 命令。023e2338 是初始化数组的首地址。023e23380x8 初始化数组第一个元素的地址。023e2338 0x8 0x4 初始化数组第二个元素的地址。r4 按4byte对地址块读写进行监控。当三个断点命中后可以看到初始化数组 023e2338 上的三个元素值都已经填上了就说这么多吧相信大家对 params 机制有一定的理解。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/86100.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!