IL 汇编学习笔记(二)
原文:http://www.codeproject.com/dotnet/ilassembly.asp
条件语句
先来看一个简单的跳转:
br 的作用类似于高级语言的 goto 语句,如果你能确定 target 语句在 br 语句的 -128 到 127byte 范围内,则可以用 br.s 代替。因为 br.s 会使用 int8 而不是 int32 保存用于跳转的位移量。
有条件的跳转语句
这里 ble 表示如果堆栈中第一个值小于等于第二个,则跳转。
其他还有一些跳转条件:
beq(==), bne(!=), bge(>=), bgt(>) ble(<=), blt(<)
brfalse(栈顶元素为 0),
brtrue(栈顶元素非 0)
循环
循环也是使用普通的分支语句来跳转,可以用一个 loop index 来判断循环的终止条件。
例子:
用比较直观的方式翻译为 C# 代码,大致上相当于:
(注:这个代码是为了便于和 IL 对照,实际并非最简单的写法)
如何创建方法
看代码:
如何用引用的方式传参:
创建类和名称空间
在 ILAsm 中,类可以有如下的访问级别修饰符:
对于类的方法和字段,可以有更多的修饰符,详见 MSDN.
(To be continued)
条件语句
先来看一个简单的跳转:
br JumpOver // 或用 br.s 代替 br
// 这里的其他代码会被跳过。。。
//
JumpOver:
// 这里的代码将被执行
// 这里的其他代码会被跳过。。。
//
JumpOver:
// 这里的代码将被执行
br 的作用类似于高级语言的 goto 语句,如果你能确定 target 语句在 br 语句的 -128 到 127byte 范围内,则可以用 br.s 代替。因为 br.s 会使用 int8 而不是 int32 保存用于跳转的位移量。
有条件的跳转语句
//Branching.il
.method static void main() cil managed
{
.maxstack 2
.entrypoint
//Takes First values from the User
ldstr "Enter First Number"
call void [mscorlib]System.Console::WriteLine (string)
call string [mscorlib]System.Console::ReadLine ()
call int32 [mscorlib]System.Int32::Parse(string)
//Takes Second values from the User
ldstr "Enter Second Number"
call void [mscorlib]System.Console::WriteLine (string)
call string [mscorlib]System.Console::ReadLine ()
call int32 [mscorlib]System.Int32::Parse(string
)
ble Smaller
ldstr "Second Number is smaller than first."
call void [mscorlib]System.Console::WriteLine (string)
br Exit
Smaller:
ldstr "First number is smaller than second."
call void [mscorlib]System.Console::WriteLine (string)
Exit:
ret
}
.method static void main() cil managed
{
.maxstack 2
.entrypoint
//Takes First values from the User
ldstr "Enter First Number"
call void [mscorlib]System.Console::WriteLine (string)
call string [mscorlib]System.Console::ReadLine ()
call int32 [mscorlib]System.Int32::Parse(string)
//Takes Second values from the User
ldstr "Enter Second Number"
call void [mscorlib]System.Console::WriteLine (string)
call string [mscorlib]System.Console::ReadLine ()
call int32 [mscorlib]System.Int32::Parse(string
)
ble Smaller
ldstr "Second Number is smaller than first."
call void [mscorlib]System.Console::WriteLine (string)
br Exit
Smaller:
ldstr "First number is smaller than second."
call void [mscorlib]System.Console::WriteLine (string)
Exit:
ret
}
这里 ble 表示如果堆栈中第一个值小于等于第二个,则跳转。
其他还有一些跳转条件:
beq(==), bne(!=), bge(>=), bgt(>) ble(<=), blt(<)
brfalse(栈顶元素为 0),
brtrue(栈顶元素非 0)
循环
循环也是使用普通的分支语句来跳转,可以用一个 loop index 来判断循环的终止条件。
例子:
.method static void main() cil managed
{
// 定义两个局部变量
.locals init (int32, int32)
.maxstack 2
.entrypoint
ldc.i4 4
stloc.0 // 变量0 = 4,循环的上限,total 5
ldc.i4 0
stloc.1 // 变量1 = 0,计数器
Start:
// 判断计数器是否超出范围
ldloc.1
ldloc.0
bgt Exit
ldloc.1
call void
// 打印出计数器的当前值
[mscorlib]System.Console::WriteLine(int32)
// 增加计数器
ldc.i4 1
ldloc.1
add
stloc.1
br Start
Exit:
ret
}
{
// 定义两个局部变量
.locals init (int32, int32)
.maxstack 2
.entrypoint
ldc.i4 4
stloc.0 // 变量0 = 4,循环的上限,total 5
ldc.i4 0
stloc.1 // 变量1 = 0,计数器
Start:
// 判断计数器是否超出范围
ldloc.1
ldloc.0
bgt Exit
ldloc.1
call void
// 打印出计数器的当前值
[mscorlib]System.Console::WriteLine(int32)
// 增加计数器
ldc.i4 1
ldloc.1
add
stloc.1
br Start
Exit:
ret
}
用比较直观的方式翻译为 C# 代码,大致上相当于:
(注:这个代码是为了便于和 IL 对照,实际并非最简单的写法)
public static void Main()
{
int max, counter;
max = 4;
counter = 0;
while (true)
{
if (counter > max)
break;
Console.WriteLine(counter);
counter++;
}
}
{
int max, counter;
max = 4;
counter = 0;
while (true)
{
if (counter > max)
break;
Console.WriteLine(counter);
counter++;
}
}
如何创建方法
看代码:
//Methods.il
//Creating Methods
.assembly extern mscorlib {}
.assembly Methods
{
.ver 1:0:1:0
}
.module Methods.exe
.method static void main() cil managed
{
.maxstack 2
.entrypoint
ldc.i4 10
ldc.i4 20
call int32 DoSum(int32, int32)
call void PrintSum(int32)
ret
}
.method public static int32 DoSum (int32 , int32 ) cil managed
{
.maxstack 2
ldarg.0
ldarg.1
add
ret
}
.method public static void PrintSum(int32) cil managed
{
.maxstack 2
ldstr "The Result is : "
call void [mscorlib]System.Console::Write(string)
ldarg.0
call void [mscorlib]System.Console::Write(int32)
ret
}
//Creating Methods
.assembly extern mscorlib {}
.assembly Methods
{
.ver 1:0:1:0
}
.module Methods.exe
.method static void main() cil managed
{
.maxstack 2
.entrypoint
ldc.i4 10
ldc.i4 20
call int32 DoSum(int32, int32)
call void PrintSum(int32)
ret
}
.method public static int32 DoSum (int32 , int32 ) cil managed
{
.maxstack 2
ldarg.0
ldarg.1
add
ret
}
.method public static void PrintSum(int32) cil managed
{
.maxstack 2
ldstr "The Result is : "
call void [mscorlib]System.Console::Write(string)
ldarg.0
call void [mscorlib]System.Console::Write(int32)
ret
}
如何用引用的方式传参:
.method static void main() cil managed
{
.maxstack 2
.entrypoint
.locals init (int32, int32)
ldc.i4 10
stloc.0
ldc.i4 20
stloc.1
ldloca 0 // 加载变量0 的地址到 Evaluation Stack
ldloc.1
call void DoSum(int32 &, int32 )
ldloc.0
// 再次加载变量0, 这次是值而不是地址
call void [mscorlib]System.Console::WriteLine(int32)
ret
}
.method public static void DoSum (int32 &, int32 ) cil managed
{
.maxstack 2
.locals init (int32)
// 加载地址,并把值复制到局部变量
ldarg.0
ldind.i4 // 读取栈顶的地址,根据这个地址去读取一个 int 值,把值写到堆栈
stloc.0 // 保存到局部变量
ldloc.0
// 做加法
ldarg.1
add
stloc.0
ldarg.0
ldloc.0
stind.i4 // 设定一个内存地址为整形值。op1: 地址,op2: 值
ret
}
{
.maxstack 2
.entrypoint
.locals init (int32, int32)
ldc.i4 10
stloc.0
ldc.i4 20
stloc.1
ldloca 0 // 加载变量0 的地址到 Evaluation Stack
ldloc.1
call void DoSum(int32 &, int32 )
ldloc.0
// 再次加载变量0, 这次是值而不是地址
call void [mscorlib]System.Console::WriteLine(int32)
ret
}
.method public static void DoSum (int32 &, int32 ) cil managed
{
.maxstack 2
.locals init (int32)
// 加载地址,并把值复制到局部变量
ldarg.0
ldind.i4 // 读取栈顶的地址,根据这个地址去读取一个 int 值,把值写到堆栈
stloc.0 // 保存到局部变量
ldloc.0
// 做加法
ldarg.1
add
stloc.0
ldarg.0
ldloc.0
stind.i4 // 设定一个内存地址为整形值。op1: 地址,op2: 值
ret
}
创建类和名称空间
//Classes.il
//Creating
Classes
.assembly extern
mscorlib {} .assembly Classes
{ .ver 1:0:1:0 }
.module Classes.exe
.namespace HangamaHouse
{
// ansi 表示该类中每一个字符串都要被转换为 ANSI 字符串。可选的其他值有:unicode, autochar
// auto 表示运行时会在非托管的内存中,为该类的成员自动选择合适的内存布局(layout). 可选的其他值有:sequential(顺序布局),explicit(严格定义)。详细参考 msdn 里的 StructLayout 或 LayoutKind 枚举
//
// 不指定的情况下,auto 和 ansi 是默认值。
.class public ansi auto Myclass extends [mscorlib]System.Object
{
.method public static void main() cil managed
{
.maxstack 1
.entrypoint
ldstr "Hello World From HangamaHouse.MyClass::main()"
call void [mscorlib]System.Console::WriteLine(string)
ret
}
}
}
//Creating
Classes
.assembly extern
mscorlib {} .assembly Classes
{ .ver 1:0:1:0 }
.module Classes.exe
.namespace HangamaHouse
{
// ansi 表示该类中每一个字符串都要被转换为 ANSI 字符串。可选的其他值有:unicode, autochar
// auto 表示运行时会在非托管的内存中,为该类的成员自动选择合适的内存布局(layout). 可选的其他值有:sequential(顺序布局),explicit(严格定义)。详细参考 msdn 里的 StructLayout 或 LayoutKind 枚举
//
// 不指定的情况下,auto 和 ansi 是默认值。
.class public ansi auto Myclass extends [mscorlib]System.Object
{
.method public static void main() cil managed
{
.maxstack 1
.entrypoint
ldstr "Hello World From HangamaHouse.MyClass::main()"
call void [mscorlib]System.Console::WriteLine(string)
ret
}
}
}
在 ILAsm 中,类可以有如下的访问级别修饰符:
ILAsm Name | Description | C# Name |
| visible to class, namespace and objects (all) |
|
| visible inside the class only |
|
| visible to class and derived classes only |
|
| visible within same assembly only |
|
| visible within derived classes of the same assembly |
|
| visible to derived classes and those of the same assembly |
|
| as that the private, but it can not be refereneced |
|
对于类的方法和字段,可以有更多的修饰符,详见 MSDN.
(To be continued)