00007FF919570B53 mov rcx,7FF9194161A0h // 设置第一个参数(指向MethodTable的指针) 00007FF919570B5D call 00007FF97905E350 // 调用分配内存的函数,默认是JIT_New 00007FF919570B62 [rbp+38h],rax // 把地址设置到临时变量(rbp+38) 00007FF919570B66 mov r8,37BFC73068h 00007FF919570B70 [r8] // 设置第三个参数("hello") 00007FF919570B73 [rbp+38h] // 设置第一个参数(this) 00007FF919570B77 // 设置第二个参数(0x12345678) 00007FF919570B7C call 00007FF9195700B8 // 调用构造函数 00007FF919570B81 [rbp+38h] 00007FF919570B85 [rbp+50h],rcx // 把临时变量复制到myClass变量中
第一个call是分配内存使用的帮助函数,默认调用JIT_New
但是这里实际调用的不是JIT_New而是JIT_TrialAllocSFastMP_InlineGetThread函数,这是一个优化版本允许从TLS(Thread Local Storage)中快速分配内存
我们来看一下JIT_TrialAllocSFastMP_InlineGetThread函数的定义
源代码:
; IN: rcx: MethodTable* ; OUT: rax: new object LEAF_ENTRY JIT_TrialAllocSFastMP_InlineGetThread, _TEXT mov edx, [rcx + OFFSET__MethodTable__m_BaseSize] // 从MethodTable获取需要分配的内存大小,放到edx ; m_BaseSize is guaranteed to be a multiple of 8. PATCHABLE_INLINE_GETTHREAD r11, JIT_TrialAllocSFastMP_InlineGetThread__PatchTLSOffset mov r10, [r11 + OFFSET__Thread__m_alloc_context__alloc_limit] // 获取从TLS分配内存的限制地址,放到r10 mov rax, [r11 + OFFSET__Thread__m_alloc_context__alloc_ptr] // 获取从TLS分配内存的当前地址,放到rax add rdx, rax // 地址 + 需要分配的内存大小,放到rdx cmp rdx, r10 // 判断是否可以从TLS分配内存 ja AllocFailed // if (rdx > r10) mov [r11 + OFFSET__Thread__m_alloc_context__alloc_ptr], rdx // 设置新的当前地址 mov [rax], rcx // 给刚刚分配到的内存设置MethodTable ifdef _DEBUG call DEBUG_TrialAllocSetAppDomain_NoScratchArea endif ; _DEBUG ret // 分配成功,返回 AllocFailed: jmp JIT_NEW // 分配失败,调用默认的JIT_New函数 LEAF_END JIT_TrialAllocSFastMP_InlineGetThread, _TEXT
可以看到做的事情和JIT_New相同,但不是从堆而是从TLS中分配内存
第二个call调用构造函数,call的地址和下面的地址不一致可能是因为中间有一层包装,目前还未解明包装中的处理
最后一个call调用的是JIT_WriteBarrier
验证第二种new(对array的new)反汇编可以看到第二种new只有一个call
00007FF919570B93 mov rcx,7FF9195B4CFAh // 设置第一个参数(指向MethodTable的指针) 00007FF919570B9D // 设置第二个参数(数组的大小) 00007FF919570BA2 call 00007FF97905E440 // 调用分配内存的函数,默认是JIT_NewArr1 00007FF919570BA7 [rbp+30h],rax // 设置到临时变量(rbp+30) 00007FF919570BAB [rbp+30h] 00007FF919570BAF [rbp+48h],rcx // 把临时变量复制到myArray变量中
call实际调用的是JIT_NewArr1VC_MP_InlineGetThread这个函数
和JIT_TrialAllocSFastMP_InlineGetThread一样,同样是从TLS(Thread Local Storage)中快速分配内存的函数
源代码:
具体代码这里就不再分析,有兴趣的可以去阅读上面的源代码
对struct的new会在函数进入的时候从栈分配内存,这里是减少rsp寄存器(栈顶)的值
00007FF919570B22 push rsi // 保存原rsi 00007FF919570B23 sub rsp,60h // 从栈分配内存 00007FF919570B27 mov rbp,rsp // 复制值到rbp 00007FF919570B2A mov rsi,rcx // 保存原rcx到rsi 00007FF919570B2D lea rdi,[rbp+28h] // rdi = rbp+28,有28个字节需要清零 00007FF919570B31 mov ecx,0Eh // rcx = 14 (计数) 00007FF919570B36 xor eax,eax // eax = 0 00007FF919570B38 rep stos dword ptr [rdi] // 把eax的值(short)设置到rdi直到rcx为0,总共清空14*2=28个字节 00007FF919570B3A mov rcx,rsi // 恢复原rcx因为分配的内存已经在栈里面,后面只需要直接调构造函数
00007FF919570BBD lea rcx,[rbp+40h] // 第一个参数 (this) 00007FF919570BC1 mov edx,55667788h // 第二个参数 (0x55667788) 00007FF919570BC6 call 00007FF9195700A0 // 调用构造函数构造函数的反编译
中间有一个call 00007FF97942E260调用的是JIT_DbgIsJustMyCode
在函数结束时会自动释放从栈分配的内存,在最后会让rsp = rbp + 0x60,这样rsp就恢复原值了
参考