自定义Rotor: 给Rotor添加一个CIL指令
2009-01-01 15:07
357 查看
快速指南
下载我修改的文件,找一份新的Rotor代码,覆盖,而后Build,运行本文的例子即可引言:
CIL = Common Intermediate Language, 在.Net Framework中,我们称他为MSIL还不知道Rotor? 看看Wiki的解释 (注:我们这里的Rotor如果没有特别注明都是指SSCLI 2.0)
关于添加CIL的文章已经不少了这里,还有这里. 然而中文的,很可惜,无,虽然有前辈(Flier's Sky - (Flier Lu), NeoRAGEx2002's Weblog)做过一些探索,然而终于是没有给俺们看到,于是有了俺的不断探索,虽频遭头痛,偶有心得,分享之。:)
思路概述:
我们先那一个指令来搞清楚CIL的执行过程, 而后在对应的地方作相应的操作,模仿即可. : )线索:
IL指令涉及到Verify, Compile, 和运行期验证的过程. 他是JIT一部分.
在rotor目录中有 clr/src/fjit 这个目录
从拿一个指令开刀说起
很多地方的例子都是乘法,我们也拿他开始. 例子程序// Sample Entrypoint and a sample function here, // Please add your own code in your environment public static void Main() { double x = 2; double y = 5; double z = x * y; Console.Read(); }
按照我之前的如何调试SSCLI的步骤, 到PAL_Initialize的位置。步骤如下
cd E:/Research/Rotor // 切换到SSCLI所在目录 env // 使用环境设置env.bat csc /debug E:/Practices/cil/MULTest.cs // 编译 devenv /debugexe clix MulTest.exe // 运行调试
我们在Visual Studio的类试图中搜索MUL指令, 而且我们知道他是JIT的一部分,于是找到了如下图这个指令,
我们给这个指令加上一个断点,按F5执行。哈哈,抓到断点了 Bingo!
查看Call Stack, 就可以轻易的看到执行过程了,双击 / 按右键 Go to source 也可以轻易的跳转到对应代码了 :D
而后根据call stack跳转到compileCEE_MUL方法之前执行的步骤
有case, 哈哈,那就有switch. :)
看到CEE_PREFIX1, CEE_LDARG_0这种东西你是不是和我一样兴奋,意味着可以在某个地方添加我们的CEE_MYCUSTOM_IL_OPCODE
接着看看MUL的实现代码
聪明的你应该看明白了,至少代码的解释应该是清楚的, 得到栈上(topOp)上第0个和第一个参数,进行运算而后进栈,
BINARY_NUMBERIC_RESULT: 查看两个参数是否为同一类型
TYPE_SWITCH_ARITH: 得到指令的详细格式,例如ldloc.i4 或者 ldloc.i8 等,这里这是emit_mul_i4
关于emit_mul或者CEE_Prefix1等等操作指令定义在fjitdef.h中,我们查看MUL_I4的操作指令
/************* MUL ************************/ #ifndef emit_MUL_I4 #define emit_MUL_I4() \ { \ callInfo.reset(); \ emit_tos_arg( 1, INTERNAL_CALL ); \ emit_tos_arg( 2, INTERNAL_CALL ); \ emit_callhelper_I4I4_I4(MUL_I4_helper); \ emit_pushresult_I4(); \ } #ifdef DECLARE_HELPERS int HELPER_CALL MUL_I4_helper(int i, int j) { return j * i; } #endif #endif
到这里,我们对一个指令的执行过程有了一个认识,于是,开始我们的新指令之旅
添加一门新武器 – EXPON
修改分为如下步骤写一个测试程序
添加新指令到Rotor源码
编译运行
测试程序
.assembly extern mscorlib {}.assembly expi4demo {}
.method static void main() {
.entrypoint
ldc.i4 3
ldc.i4 3
expon
call void [mscorlib]System.Console::WriteLine(int32)
ret
}
添加指令到Rotor
给clr\src\inc\opcode.def 模仿CEE_MUL添加上EXPON.OPDEF(CEE_EXPON, "expon", PopR8+PopR8, PushR8, InlineNone, IPrimitive, 1, 0xFF, 0xA6, NEXT)
并在Reflection.Emit添加这个指令 clr\src\bcl\system\reflection\emit\opcodes.cs
public static readonly OpCode Expon = new OpCode("expon", StackBehaviour.Pop1_pop1,
StackBehaviour.Push1, OperandType.InlineNone, OpCodeType.Primitive, 1, (byte)0xff, (byte)0xa6,
FlowControl.Next, false, -1);
接着要进行JIT指令验证的过程了. 验证的代码在clr\src\vm\validator.cpp中,我们只需要添加指令到vertable.h
VEROPCODE(CEE_EXPON, "N=:-")
所谓”N=:-” N – Number, = - 接着另一个Number : - 结果, - - 入栈.
关于格式的详细解释,在vertable.h的注释中有详尽的解释,有兴趣的朋友不妨一看。
接下来则要给他添加执行过程了, 在clr/src/vm/fjit/fjit.cpp 中的jitCompile方法添加一个switch判断
case CEE_EXPON: JitResult = compileCEE_EXPON(); break;
接着在本类中和头文件中添加对应的实现代码
fjit.cpp
FJitResult FJit::compileCEE_EXPON() { OpType result_xf; BINARY_NUMERIC_RESULT(topOp(), topOp(1), CEE_EXPON, result_xf); TYPE_SWITCH_ARITH(topOp(), emit_EXPON, ()); POP_STACK(2); pushOp(result_xf); return FJIT_OK; }
fjit.h
FJitResult compileCEE_EXPON();
TYPE_SWITCH_ARITH中定义了对应的实现方法,于是我们在clr\src\jit\fjit.def 中添加对应的定义
#ifndef emit_EXPON_I4 #define emit_EXPON_I4() \ { \ callInfo.reset(); \ emit_tos_arg( 1, INTERNAL_CALL ); \ emit_tos_arg( 2, INTERNAL_CALL ); \ emit_callhelper_I4I4_I4(EXPON_I4_helper); \ emit_pushresult_I4(); \ } #ifdef DECLARE_HELPERS int HELPER_CALL EXPON_I4_helper(int i, int j) { return j * i; } #endif #endif #ifndef emit_EXPON_I8 #define emit_EXPON_I8() \ { \ callInfo.reset(); \ int NumRegUsed = 0; \ emit_tos_fixedsize_arg( 1, 8, NumRegUsed, INTERNAL_CALL ); \ emit_tos_fixedsize_arg( 1 + NumRegUsed, 8, NumRegUsed, INTERNAL_CALL ); \ emit_callhelper_I8I8_I8(EXPON_I8_helper); \ emit_pushresult_I8(); \ } #ifdef DECLARE_HELPERS __int64 HELPER_CALL EXPON_I8_helper(__int64 i, __int64 j) { return j * i; } #endif #endif #ifndef emit_EXPON_R4 #define emit_EXPON_R4() \ { \ callInfo.reset(); \ emit_tos_arg( 1, INTERNAL_CALL ); \ emit_tos_arg( 2, INTERNAL_CALL ); \ emit_callhelper_I4I4_I4(EXPON_R4_helper); \ emit_pushresult_I4(); \ } #ifdef DECLARE_HELPERS unsigned int HELPER_CALL EXPON_R4_helper(int i, int j) { float result = (*(float *)&j) * (*(float *)&i); return *(unsigned int*)&result; } #endif #endif #ifndef emit_EXPON_R8 #define emit_EXPON_R8() \ { \ callInfo.reset(); \ int NumRegUsed = 0; \ emit_tos_fixedsize_arg( 1, 8, NumRegUsed, INTERNAL_CALL ); \ emit_tos_fixedsize_arg( 1 + NumRegUsed, 8, NumRegUsed, INTERNAL_CALL ); \ emit_callhelper_R8R8_R8(EXPON_R8_helper); \ emit_pushresult_I8(); \ } #ifdef DECLARE_HELPERS unsigned __int64 HELPER_CALL EXPON_R8_helper(__int64 i, __int64 j) { double result = (*(double *)&j) * (*(double *)&i); return *(unsigned __int64*)&result; } #endif #endif
重新编译Rotor, 切换到开始的IL所在目录并运行
ILAsm expon.il clix expon
编译结果:
在编译的过程中,碰到一些问题,需要注意如下:
文件编码要正确
如果编译错误,删除原来编译结果,而后重新编译
在这里,我把我所修改的文件共享下,覆盖原来的代码文件即可
我修改的文件
结语:
修改本来是件Easy的事情,不幸运的是俺第一次编译失败,而后面对一堆的错误,不知道哪里入手,检查一次又一次代码之后,确定代码无错误,尝试着删除某个编译结果后错误不一样了, 俺兴奋了,俺强烈的兴奋了。 在经过几次尝试和漫长的编译等待之后,env
buildall
等待…… Zzzz Orz…
编译错误, 查看clr/src/build.err
env
buildall
修改过程虽然简单,可惜俺那稳定性不高的编码速度实在害人,总结下,发发牢骚而已 :)
Have Fun :)
相关文章推荐
- 为Unrealircd添加一个model,并加入自定义命令,且操作你的mysql
- Pixhawk---通过串口方式添加一个自定义传感器(超声波为例)
- Openstack组件部署 — 将一个自定义 Service 添加到 Keystone
- 给一个自定义对象添加属性和方法
- 博客园自定义之博客园公告栏添加时钟——利用canvas画出一个时钟
- 使用XIB自定义一个UIView,然后将这个view添加到controller的view 上(相当于所有界面都通过xib来实现)
- iPhone开发学习笔记005——使用XIB自定义一个UIView,然后将这个view添加到controller的view
- VB6 如何添加自定义函数 模块 把代码放到一个模块中
- 在Jquery validation里验证通过后,自定义提交不同的action路径,适合一个窗口既可以作为添加对话框也可以作为编辑对话框
- DA14580 --- 添加一个自定义服务
- datagrid中为每行添加一个自定义按钮(jQuery EASY UI)
- 直接创建一个DataTable,并为之添加数据(自定义DataTable)
- Horizontalscrollview里添加一个布局,该布局里在添加一个自定义的View,为什么View里用Canvas画图无法显示?
- PIXHAWK源码分析之三---通过串口方式添加一个自定义传感器(1)
- Android的一个自定义的动态添加Dialog类
- 博客园自定义之博客园公告栏添加时钟——利用canvas画出一个时钟
- UEditor百度编辑器,工具栏自定义添加一个普通按钮
- 给phpcms v9专题添加一个radio单选按钮,可以多次利用专题创建不同的栏目进行内容编辑,给专题添加一个自定义字段
- 怎样添加一个自定义的LED灯?
- iPhone开发学习笔记005——使用XIB自定义一个UIView,然后将这个view添加到controller的view