以helloworld为例,核心代码:
Console.WriteLine("hello world");
通过被csc编译过后,得到可执行文件或者dll,比如名为app.exe,ilc将app.exe和托管System核心库作为输入,调用JIT编译器
将ILcode变为机器码,比如ILcode:
helloworld字符串入栈;
调用 System库下Method方法中下标是0x11223344的函数;
被转换成机器码:
push "hell world"
Call System::Console
就完事了。
然后形成.o文件,通过LINK可以直接和其他.o链接成exe/ELF/DLL.
还有个特殊点,在BCL库中,只用了C# 的基本语法,相当于只用了C#子集,可以这么理解,把C#当作C语言用了。
BCL里面用了new 这个关键字,我们知道在CPP里面,是可以重定位new 函数的,比如
void *operator new(size_t size)
{
void *p = pvPortMalloc(size);//你自己的内存分配函数接口,这里用的是FreeRTOS的函数接口
memset(p, 0, size);
return p;
}
这样就完成了new的重定位。因此我想这可能有类似的写法:
[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhpNew")]
private static extern IntPtr RhpNew(函数参数);
unsafe Intptr operator new(函数参数)
{
巴拉巴拉;
return RhpNew(........);
}
但是我翻遍了runtime和BCL库都没有这个函数的重定位,百思不得其解:如果不是在runtime/BCL里,那就是在编译时
用了函数替换,把new IL code替换成相应的函数了,果不其然,在ILC编译器源码里,有如下代码:
if (opcode == ILOpcode.newobj)
{
巴拉巴拉;
_dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewObject), reason);
}
....
巴拉巴拉;
case ReadyToRunHelper.NewObject:
mangledName = "RhNewObject";
break;
然后在BCL库里就找到了RhNewObject:
[RuntimeExport("RhNewObject")]
public static unsafe object RhNewObject(MethodTable* pEEType)
{
巴拉巴拉;
return InternalCalls.RhpNewFast(pEEType);
}
这个RhpNewFast函数Runtime CPP文件里面实现的
从此完成了new的操作。
总结一下,调用new,相当于调用RhNewObject/RhNewArray函数(不同场景下的new有不同的函数替换)
就是函数替换魔法!读取il,然后完成函数替换.
PS:
我觉得MichalStrehovsky他这个人很牛逼,他的zerosharp C#写的8KB程序可以运行在DOS下
有没有发现BCL库都是用C#写的,包括非常底层的new和反射,都是C#写的
在编译器做了魔法处理,所以我觉得他的目标远不止这些,我认为后期,极有可能在C#层面完成一个VM+JIT+GC,
也就是说使用C#的子集开发一个VM+JIT+GC,
最底层CPP只提供最基本的函数接口,比如:CreateThread Open Write这就是一个完整的CLR.
那到时候coreCLR就可以退出历史舞台了