Mono源码学习1
起因
结束了一年多的出差,回到了公司上班,最近一直在想着博客的改版,连博客也没怎么写了,正好这两天不怎么忙,便想起看看mono的源码,至于为什么看mono源码而不是选择.Net Core源码,Mono源码是纯c的,看起来更容易一些,不需要c++的知识.其实.Net Core源码直接用VS调试还是很方便的.不过source insight对c++支持的不是很好.阅读源码使用source insight还是很方便的.在Github看Mono已经把zlib源码加入到Mono源码,在编译源码的时候,不用手动链接zlib静态库了.
阅读源码从mini/main.c开始.
函数名以g_开头的,是eglib封装的工具函数.主要用于简化操作.如g_new0/g_free
屏蔽系统API差异
这里以mono_pagesize为例.先认识Windows的源码.//获取系统页大小
int mono_pagesize(void)
{
SYSTEM_INFO info;
static int saved_pagesize = 0; //saved_pagesize 使用static声明
if (saved_pagesize)
return saved_pagesize; //第二次调用mono_pagesize函数,直接返回saved_pagesize
GetSystemInfo(&info); //第一次调用mono_pagesize函数,调用GetSystemInfo系统函数
saved_pagesize = info.dwPageSize; //将获取到系统页大小赋值给saved_pagesize
return saved_pagesize;
}
Linux下
int
mono_pagesize (void)
{
static int saved_pagesize = 0;
if (saved_pagesize)
return saved_pagesize;
// Prefer sysconf () as it's signal safe.
#if defined (HAVE_SYSCONF) && defined (_SC_PAGESIZE)
saved_pagesize = sysconf (_SC_PAGESIZE);
#else
saved_pagesize = getpagesize ();
#endif
return saved_pagesize;
}
根据宏判断是否支持sysconf系统调用.
其实Windows默认的页大小也是4096
mono_main 主函数
main.c
int main(void) //Windows
int main(int argc, char* argv[]) //Linux或Unix及Mac
int mono_main_with_options(argc, argv)
driver.c
int mono_main(argc, argv) //主函数
coree.c
//加载mscoree.dll,Windows独有的, c# exe(Windows下)执行的时候通过ntdll->mscoree.dll->mscorlib.dll
void mono_load_coree(const char* exe_file_name)
mono-config.c
//设置lib和etc,具体可以看 https://www.qiufengblog.com/articles/mono-config.html
void mono_config_parse (const char *filename)
mini-runtime.c
//mono CLR运行时初始化,返回MonoDomain
MonoDomain* mini_init(const char* filename, const char* runtime_version)
dirver.c
static void main_thread_handler (gpointer user_data)
domain.c
//根据domain,加载程序集(并没有执行)
MonoAssembly* mono_domain_assembly_open(MonoDomain* domain, const char* name)
driver.c
//根据domain,执行程序集
int mono_jit_exec (MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[])
mini-runtime.c
//清理domain,释放资源
void mini_cleanup(MonoDomain* domain)
看简化之后的代码:
//函数原型
MonoDomain* mini_init(const char* filename, const char* runtime_version);
//mono 主函数
//函数去除很多代码
int mono_main(int argc, char* argv[])
{
//
//去除解析参数处理,后面用到单独说明
//
MonoThreadArgs main_args;
int i;
int mixed_mode = FALSE;
if (mixed_mode)
{
mono_load_coree(argv[i]);
}
mono_config_parse(config_file);
mono_set_defaults(mini_verbose_level, opt);
MonoDomain* domain = mini_init(argv[i], forced_version);
main_args.domain = domain;
main_args.file = aname;
main_args.argc = argc - i;
main_args.argv = argv + i;
main_args.opts = opt;
main_args.aot_options = aot_options;
main_thread_handler(&main_args);
mono_thread_manage();
mini_cleanup(domain);
i = mono_environment_exitcode_get();
return i;
}
MonoDomain* mini_init(const char* filename, const char* runtime_version)
{
//1.初始化很多东西
//2.注册回调函数
MonoDomain* domain;
//通过mono program.exe 没有指定--runtime=v4.0.30319 ,runtime_version不为真的
if (runtime_version)
{
domain = mono_init_version(filename, runtime_version);
}
else
{
domain = mono_init_from_assembly(filename, filename);
}
//还要处理很多东西
//xxx
//xxx
return domain;
}
在fixed模式下,mono如何加载mscoree.dll
基于mono_load_coree函数源码,改动而来,主要是动手练习一下.也顺便一些系统函数的调用.typedef unsigned __int16 gunichar;
//1. 根据GetSystemDirectory系统函数,获取系统目录
//2. 将获取到系统目录和mscoree.dll进行拼接
//3. 通过LoadLibrary加载到内存
HMODULE load_coree()
{
UINT len = GetSystemDirectory(NULL, 0);
printf("%d\n", len);
printf("unsigned __int16 size:%d\n", sizeof(gunichar));
gunichar* dir = calloc(1, (len + 12) * sizeof(gunichar));
GetSystemDirectory(dir, len);
//打印系统路径
printf("system dir:%ls\n", dir);
if (dir[len - 1] != L'\\')
{
dir[len - 1] = L'\\';
}
//拼接dll路径
memcpy(&dir[len], L"mscoree.dll", 12 * sizeof(gunichar));
printf("dir :%ls\n", dir);
//LoadLibrary (将dll加载内存中,将文件的起始位置返回)
//LoadLibrary和FreeLibrary 配对使用,
HMODULE module = LoadLibraryW(dir);
return module;
}
//验证该句柄是否位pe文件的起始位置
int validate_pe(HMODULE hModule)
{
PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)hModule;
DWORD magic = dos_header->e_magic;
char* a = (char*)&magic;
printf("%s\n", a);
if (magic == IMAGE_DOS_SIGNATURE)
{
printf("pe dos header Signature:MZ\n");
}
PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((char*)dos_header + dos_header->e_lfanew);
if (nt_header->Signature == IMAGE_NT_SIGNATURE)
{
printf("pe dos header Signature:PE\n");
}
return 0;
}
要不是看了源码,怎么也没想到LoadLibrary返回的HMODULE,竟然是文件在内存中的起始地址
秋风
2019-12-18