Common Intermediate Language i kod generowany dynamicznie dla .NET
Spis treści
- Podstawowe pojęcia
- Kod zarządzany (ang. managed) i niezarządzany (ang. unsafe)
- Typy danych w języku pośrednim CIL
- Typy danych bezpośrednio wspierane przez infrastrukture CLI
- Narzędzie typu asembler dla języka pośredniego platformy .NET
- Narzędzie typu dezasembler dla kodu bajtowego platformy .NET
- Lista kodów operacyjnych CIL (ang. opcode)
- Opisy instrukcji języka pośredniego (CIL)
- Generowanie kodu CIL z użyciem klasy ILGenerator
- Dynamiczna kompilacja kodu źródłowego w języku C# za pomocą klasy CSharpCompilation
- Wykonanie surowych bajtów poprzez utworzenie delegatu ze wskaźnika do pamięci niezarządzanej
Podstawowe pojęcia
- Infrastruktura języka pośredniego (ang. Common Language Infrastructure) — specyfikacja środowiska, którego zadaniem jest wykonywanie kodu CIL.
- Język pośredni (ang. Common Intermediate Language) — zestaw instrukcji, których wykonaniem zajmuje się maszyna wirtualna VES. Inne spotykane nazwy języka pośredniego CIL to: .NET IL oraz MSIL. Język pośredni CIL przypomina Asembler, jednak nie używa się tutaj rejestrów — jest oparty na stosie i pozwala operować na obiektach.
- Maszyna wirtualna (ang. Virtual Execution System) — silnik odpowiedzialny za wykonywanie programów stworzonych dla infrastruktury języka pośredniego (ang. Common Language Infrastructure).
- Obiekt (ang. object) — instancja typu referencyjnego. Jest niezależnie istniejący i sam określa swój typ. Jego zawartość może się zmienić, jednak on sam pozostaje niezmienny.
- Odpakowywanie (ang. unboxing) — zamiana wartości opakowanej w typ
System.Object
na dane typu prostego (ang. value type). - Opakowywanie (ang. boxing) — umieszczenie wartości typu prostego (ang. value type) w nowo utworzonym obiekcie typu
System.Object
przechowywanym na stercie (ang. heap). Dostępność tego rodzaju mechanizmu doskonale prezentuje, że nawet dane typów prostych mogą być traktowane jako niezależne obiekty. - Specyfikacja języka pośredniego (ang. Common Language Specification) — zawiera konwencje, które pozwalają na zachowanie zgodności rozwiązań implementowanych przez projektantów języka oraz projektantów bibliotek klas.
- Wartość (ang. value) — ciąg bitów do reprezentowania prostych typów jak np. liczba czy napis. Wartość nie jest obiektem, ale obiekt może zawierać w sobie wartości.
- Wspólny system typów (ang. Common Type System) — system typów używany przez kompilatory, infrastrukturę CLI i inne narzędzia. Pozwala m.in. na zachowanie zgodności typów i wydajne wykonywanie kodu.
Kod zarządzany (ang. managed) i niezarządzany (ang. unsafe)
Kod zarządzany (ang. managed code) nie jest tylko ładowany do pamięci i uruchamiany. Infrastruktura, która zarządza wykonaniem takiego kodu ma dostęp do metadanych opisujących metody, nadzoruje operacje na stosie czy zajmuje się obsługą wyjątków. Dane w kodzie zarządzanym też nie są pozostawione same sobie. Infrastruktura CLI zajmuje się rezerwacją pamięci oraz usuwaniem niepotrzebnych obiektów w procesie nazywanym odśmiecaniem pamięci (ang. garbage collection).
Typy danych w języku pośrednim CIL
Poniżej przedstawiono typy danych w języku CIL oraz ich odpowiedniki w wysokopoziomowej składni w języku C#.
bool
System.Boolean
, bool
char
System.Char
, char
object
System.Object
, object
string
System.String
, string
float32
System.Single
, float
float64
System.Double
, double
int8
System.SByte
, sbyte
int16
System.Int16
, short
int32
System.Int32
, int
int64
System.Int64
, long
native int
System.IntPtr
, nint
native unsigned int
System.UIntPtr
, nuint
typedref
System.TypedReference
unsigned int8
System.Byte
, byte
unsigned int16
System.UInt16
, ushort
unsigned int32
System.UInt32
, uint
unsigned int64
System.UInt64
, ulong
Typy danych bezpośrednio wspierane przez infrastrukture CLI
Poniżej przedstawiono typy danych bezpośrednio wspierane przez infrastrukture CLI.
int8
unsigned int8
int16
unsigned int16
int32
unsigned int32
int64
unsigned int64
float32
float64
native int
native unsigned int
F
O
&
*
Narzędzie typu asembler dla języka pośredniego platformy .NET
W celu zbudowania pliku wykonywalnego *.exe lub *.dll z kodu źródłowego w Asemblerze CIL można użyć narzędzia ilasm.exe
.
Należy w tym celu uruchomić Developer PowerShell for Visual Studio.
Kod źródłowy, który wyświetla proste okno dialogowe typu MessageBox
przedstawiono poniżej.
.assembly extern mscorlib { }
.assembly Hello { }
.method private hidebysig static void Main() cil managed
{
.entrypoint
.custom instance void [System.Runtime]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
.maxstack 8
ldstr "Spreading knowledge like a virus."
ldstr "ethical.blue Magazine"
ldc.i4.0
ldc.i4.s 64
call valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult
[System.Windows.Forms]System.Windows.Forms.MessageBox::Show(
string, string,
valuetype [System.Windows.Forms]System.Windows.Forms.MessageBoxButtons,
valuetype [System.Windows.Forms]System.Windows.Forms.MessageBoxIcon
)
pop
ret
}
W celu zbudowania pliku wykonywalnego *.exe można użyć następującego polecenia:
ilasm hello.il /X64 /SUBSYSTEM=2
Narzędzie typu asembler to ilasm.exe
, hello.il
to nazwa pliku z kodem źródłowym, /X64
to docelowa architektura,
a /SUBSYSTEM=2
oznacza program z interfejsem graficznym użytkownika (GUI).
Stała o wartości dwa oznacza dokładnie ustawienie IMAGE_SUBSYSTEM_WINDOWS_GUI
w słowie Subsystem
w nagłówku _IMAGE_OPTIONAL_HEADER
pliku wykonywalnego PE.
Narzędzie typu dezasembler dla kodu bajtowego platformy .NET
W celu odzyskania kodu źródłowego w języku pośrednim CIL
z pliku PE można użyć narzędzia typu dezasembler np. ildasm.exe
.
Lista kodów operacyjnych CIL (ang. opcode)
Kody operacyjne to binarna forma rozkazów dla maszyny wirtualnej VES. Poniżej przedstawiono listę kodów operacyjnych wraz z odpowiadającą im tekstową formą określaną terminem mnemonik.
Kod operacyjny | Rozkaz |
0x00
|
nop
|
0x01
|
break
|
0x02
|
ldarg.0
|
0x03
|
ldarg.1
|
0x04
|
ldarg.2
|
0x05
|
ldarg.3
|
0x06
|
ldloc.0
|
0x07
|
ldloc.1
|
0x08
|
ldloc.2
|
0x09
|
ldloc.3
|
0x0A
|
stloc.0
|
0x0B
|
stloc.1
|
0x0C
|
stloc.2
|
0x0D
|
stloc.3
|
0x0E
|
ldarg.s
|
0x0F
|
ldarga.s
|
Kod operacyjny | Rozkaz |
0x10
|
starg.s
|
0x11
|
ldloc.s
|
0x12
|
ldloca.s
|
0x13
|
stloc.s
|
0x14
|
ldnull
|
0x15
|
ldc.i4.m1
|
0x16
|
ldc.i4.0
|
0x17
|
ldc.i4.1
|
0x18
|
ldc.i4.2
|
0x19
|
ldc.i4.3
|
0x1A
|
ldc.i4.4
|
0x1B
|
ldc.i4.5
|
0x1C
|
ldc.i4.6
|
0x1D
|
ldc.i4.7
|
0x1E
|
ldc.i4.8
|
0x1F
|
ldc.i4.s
|
Kod operacyjny | Rozkaz |
0x20
|
ldc.i4
|
0x21
|
ldc.i8
|
0x22
|
ldc.r4
|
0x23
|
ldc.r8
|
0x25
|
dup
|
0x26
|
pop
|
0x27
|
jmp
|
0x28
|
call
|
0x29
|
calli
|
0x2A
|
ret
|
0x2B
|
br.s
|
0x2C
|
brfalse.s
|
0x2D
|
brtrue.s
|
0x2E
|
beq.s
|
0x2F
|
bge.s
|
Kod operacyjny | Rozkaz |
0x30
|
bgt.s
|
0x31
|
ble.s
|
0x32
|
blt.s
|
0x33
|
bne.un.s
|
0x34
|
bge.un.s
|
0x35
|
bgt.un.s
|
0x36
|
ble.un.s
|
0x37
|
blt.un.s
|
0x38
|
br
|
0x39
|
brfalse
|
0x3A
|
brtrue
|
0x3B
|
beq
|
0x3C
|
bge
|
0x3D
|
bgt
|
0x3E
|
ble
|
0x3F
|
blt
|
Kod operacyjny | Rozkaz |
0x40
|
bne.un
|
0x41
|
bge.un
|
0x42
|
bgt.un
|
0x43
|
ble.un
|
0x44
|
blt.un
|
0x45
|
switch
|
0x46
|
ldind.i1
|
0x47
|
ldind.u1
|
0x48
|
ldind.i2
|
0x49
|
ldind.u2
|
0x4A
|
ldind.i4
|
0x4B
|
ldind.u4
|
0x4C
|
ldind.i8
|
0x4D
|
ldind.i
|
0x4E
|
ldind.r4
|
0x4F
|
ldind.r8
|
Kod operacyjny | Rozkaz |
0x50
|
ldind.ref
|
0x51
|
stind.ref
|
0x52
|
stind.i1
|
0x53
|
stind.i2
|
0x54
|
stind.i4
|
0x55
|
stind.i8
|
0x56
|
stind.r4
|
0x57
|
stind.r8
|
0x58
|
add
|
0x59
|
sub
|
0x5A
|
mul
|
0x5B
|
div
|
0x5C
|
div.un
|
0x5D
|
rem
|
0x5E
|
rem.un
|
0x5F
|
and
|
Kod operacyjny | Rozkaz |
0x60
|
or
|
0x61
|
xor
|
0x62
|
shl
|
0x63
|
shr
|
0x64
|
shr.un
|
0x65
|
neg
|
0x66
|
not
|
0x67
|
conv.i1
|
0x68
|
conv.i2
|
0x69
|
conv.i4
|
0x6A
|
conv.i8
|
0x6B
|
conv.r4
|
0x6C
|
conv.r8
|
0x6D
|
conv.u4
|
0x6E
|
conv.u8
|
0x6F
|
callvirt
|
Kod operacyjny | Rozkaz |
0x70
|
cpobj
|
0x71
|
ldobj
|
0x72
|
ldstr
|
0x73
|
newobj
|
0x74
|
castclass
|
0x75
|
isinst
|
0x76
|
conv.r.un
|
0x79
|
unbox
|
0x7A
|
throw
|
0x7B
|
ldfld
|
0x7C
|
ldflda
|
0x7D
|
stfld
|
0x7E
|
ldsfld
|
0x7F
|
ldsflda
|
Kod operacyjny | Rozkaz |
0x80
|
stsfld
|
0x81
|
stobj
|
0x82
|
conv.ovf.i1.un
|
0x83
|
conv.ovf.i2.un
|
0x84
|
conv.ovf.i4.un
|
0x85
|
conv.ovf.i8.un
|
0x86
|
conv.ovf.u1.un
|
0x87
|
conv.ovf.u2.un
|
0x88
|
conv.ovf.u4.un
|
0x89
|
conv.ovf.u8.un
|
0x8A
|
conv.ovf.i.un
|
0x8B
|
conv.ovf.u.un
|
0x8C
|
box
|
0x8D
|
newarr
|
0x8E
|
ldlen
|
0x8F
|
ldelema
|
Kod operacyjny | Rozkaz |
0x90
|
ldelem.i1
|
0x91
|
ldelem.u1
|
0x92
|
ldelem.i2
|
0x93
|
ldelem.u2
|
0x94
|
ldelem.i4
|
0x95
|
ldelem.u4
|
0x96
|
ldelem.i8
|
0x97
|
ldelem.i
|
0x98
|
ldelem.r4
|
0x99
|
ldelem.r8
|
0x9A
|
ldelem.ref
|
0x9B
|
stelem.i
|
0x9C
|
stelem.i1
|
0x9D
|
stelem.i2
|
0x9E
|
stelem.i4
|
0x9F
|
stelem.i8
|
Kod operacyjny | Rozkaz |
0xA0
|
stelem.r4
|
0xA1
|
stelem.r8
|
0xA2
|
stelem.ref
|
0xA3
|
ldelem
|
0xA4
|
stelem
|
0xA5
|
unbox.any
|
0xB3
|
conv.ovf.i1
|
0xB4
|
conv.ovf.u1
|
0xB5
|
conv.ovf.i2
|
0xB6
|
conv.ovf.u2
|
0xB7
|
conv.ovf.i4
|
0xB8
|
conv.ovf.u4
|
0xB9
|
conv.ovf.i8
|
0xBA
|
conv.ovf.u8
|
0xC2
|
refanyval
|
0xC3
|
ckfinite
|
Kod operacyjny | Rozkaz |
0xC6
|
mkrefany
|
0xD0
|
ldtoken
|
0xD1
|
conv.u2
|
0xD2
|
conv.u1
|
0xD3
|
conv.i
|
0xD4
|
conv.ovf.i
|
0xD5
|
conv.ovf.u
|
0xD6
|
add.ovf
|
0xD7
|
add.ovf.un
|
0xD8
|
mul.ovf
|
0xD9
|
mul.ovf.un
|
0xDA
|
sub.ovf
|
0xDB
|
sub.ovf.un
|
0xDC
|
endfinally
|
0xDD
|
leave
|
0xDE
|
leave.s
|
Kod operacyjny | Rozkaz |
0xDF
|
stind.i
|
0xE0
|
conv.u
|
0xFE 0x00
|
arglist
|
0xFE 0x01
|
ceq
|
0xFE 0x02
|
cgt
|
0xFE 0x03
|
cgt.un
|
0xFE 0x04
|
clt
|
0xFE 0x05
|
clt.un
|
0xFE 0x06
|
ldftn
|
0xFE 0x07
|
ldvirtftn
|
0xFE 0x09
|
ldarg
|
0xFE 0x0A
|
ldarga
|
0xFE 0x0B
|
starg
|
0xFE 0x0C
|
ldloc
|
0xFE 0x0D
|
ldloca
|
0xFE 0x0E
|
stloc
|
Kod operacyjny | Rozkaz |
0xFE 0x0F
|
localloc
|
0xFE 0x11
|
endfilter
|
0xFE 0x12
|
unaligned.
|
0xFE 0x13
|
volatile.
|
0xFE 0x14
|
tail.
|
0xFE 0x15
|
Initobj
|
0xFE 0x16
|
constrained.
|
0xFE 0x17
|
cpblk
|
0xFE 0x18
|
initblk
|
0xFE 0x19
|
no.
|
0xFE 0x1A
|
rethrow
|
0xFE 0x1C
|
sizeof
|
0xFE 0x1D
|
Refanytype
|
0xFE 0x1E
|
readonly.
|
Opisy instrukcji języka pośredniego (CIL)
Opis każdej instrukcji CIL składa się z następujących elementów.
Najpierw jest nazwa instrukcji z klasy OpCodes
przestrzeni nazw System.Reflection.Emit
,
a dalej w nawiasie mnemonik z argumentami oraz po przecinku kod operacyjny z argumentami.
Należy zaznaczyć, że nie wszystkie instrukcje przyjmują argumenty.
Nop
nop
0x00
Ldc_R8
ldc.r8
num
0x23 <float64>
Switch
switch
(IL_0001, IL_0002 ... IL_000N)
0x45 <unsigned int32> <int32>...<int32>
Lista instrukcji CIL wraz z opisem:
Add (add, 0x58) —
dwie wartości są odkładane na stos. Odłożone wartości są zdejmowane i pierwsza wartość jest dodawana do drugiej. Wynik operacji dodawania jest odkładany na stos. Instrukcja nie wykrywa przepełnienia całkowitoliczbowego (ang. integer overflow). Przepełnienie dla wartości zmiennoprzecinkowych (ang. floating-point) zwraca nieskończoność (ang. ±infinity).Add_Ovf (add.ovf, 0xD6) —
dwie wartości całkowitoliczbowe ze znakiem są odkładane na stos. Odłożone wartości są zdejmowane i pierwsza wartość jest dodawana do drugiej. Wynik operacji dodawania jest odkładany na stos. Instrukcja w przypadku przepełnienia rzuca wyjątekOverflowException
.Add_Ovf_Un (add.ovf.un, 0xD7) —
dwie wartości całkowitoliczbowe bez znaku są odkładane na stos. Odłożone wartości są zdejmowane i pierwsza wartość jest dodawana do drugiej. Wynik operacji dodawania jest odkładany na stos. Instrukcja w przypadku przepełnienia rzuca wyjątekOverflowException
.And (and, 0x5F) —
dwie wartości całkowitoliczbowe są odkładane na stos. Odłożone wartości są zdejmowane i wykonywana jest na nich koniunkcja logiczna (ang. AND). Wynik operacji jest odkładany na stos.Arglist (arglist, 0xFE00) —
instrukcja zwraca uchwyt typu niezarządzany wskaźniknative int
, który reprezentuje listę argumentów bieżącej metody. Uchwyt po wyjściu z metody jest nieważny, ale może zostać wcześniej przekazany do innych metod w bieżącym zakresie.Beq (beq target, 0x3B <int32>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli dwie ostatnio odłożone wartości na stosie są takie same. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Beq_S (beq.s target, 0x2E <int8>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli dwie ostatnio odłożone wartości na stosie są takie same. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Bge (bge target, 0x3C <int32>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość ostatnio odłożona na stos jest większa bądź równa wartości odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Bge_S (bge.s target, 0x2F <int8>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość ostatnio odłożona na stos jest większa bądź równa wartości odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Bge_Un (bge.un target, 0x41 <int32>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość bez znaku ostatnio odłożona na stos jest większa bądź równa wartości bez znaku odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Bge_Un_S (bge.un.s target, 0x34 <int8>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość bez znaku ostatnio odłożona na stos jest większa bądź równa wartości bez znaku odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Bgt (bgt target, 0x3D <int32>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość ostatnio odłożona na stos jest większa od wartości odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Bgt_S (bgt.s target, 0x30 <int8>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość ostatnio odłożona na stos jest większa od wartości odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Bgt_Un (bgt.un target, 0x42 <int32>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość bez znaku ostatnio odłożona na stos jest większa od wartości bez znaku odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Bgt_Un_S (bgt.un.s target, 0x35 <int8>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość bez znaku ostatnio odłożona na stos jest większa od wartości bez znaku odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Ble (ble target, 0x3E <int32>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość ostatnio odłożona na stos jest mniejsza od lub równa wartości odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Ble_S (ble.s target, 0x31 <int8>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość ostatnio odłożona na stos jest mniejsza od lub równa wartości odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Ble_Un (ble.un target, 0x43 <int32>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość bez znaku ostatnio odłożona na stos jest mniejsza od lub równa wartości bez znaku odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Ble_Un_S (ble.un.s target, 0x36 <int8>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość bez znaku ostatnio odłożona na stos jest mniejsza od lub równa wartości bez znaku odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Blt (blt target, 0x3F <int32>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość ostatnio odłożona na stos jest mniejsza od wartości odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Blt_S (blt.s target, 0x32 <int8>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość ostatnio odłożona na stos jest mniejsza od wartości odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Blt_Un (blt.un target, 0x44 <int32>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość bez znaku ostatnio odłożona na stos jest mniejsza od wartości bez znaku odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Blt_Un_S (blt.un.s target, 0x37 <int8>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli pierwsza wartość bez znaku ostatnio odłożona na stos jest mniejsza od wartości bez znaku odłożonej na stos jako druga. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Bne_Un (bne.un target, 0x40 <int32>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli dwie ostatnio odłożone na stos wartości bez znaku są od siebie różne. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Bne_Un_S (bne.un.s target, 0x33 <int8>) —
instrukcja przekazuje wykonanie do instrukcji pod podanym przesunięciem (ang. offset), jeśli dwie ostatnio odłożone na stos wartości bez znaku są od siebie różne. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Box (box token, 0x8C <T>) —
dane typu prostego (ang. value type) są odkładane na stos. Dane są zdejmowane, wykonywana jest operacja opakowywanie (ang. boxing). Po wykonaniu tej operacji na stos odkładany jest typ referencyjny zawierający wartość z typu prostego.Br (br target, 0x38 <int32>) —
instrukcja bezwarunkowo przekazuje wykonanie do rozkazu pod podanym jako argument przesunięciem (ang. offset). Przesunięcie to wartość ze znakiem o rozmiarze czterech bajtów obliczana od początku instrukcji znajdującej się zaraz za bieżącą instrukcją. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Br_S (br.s target, 0x2B <int8>) —
instrukcja bezwarunkowo przekazuje wykonanie do rozkazu pod podanym jako argument przesunięciem (ang. offset). Przesunięcie to wartość ze znakiem o rozmiarze jednego bajta obliczana od początku instrukcji znajdującej się zaraz za bieżącą instrukcją. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Break (break, 0x01) —
instrukcja sygnalizuje CLI, aby poinformować debugger, że nastąpiło wyzwolenie punktu przerwania (ang. breakpoint), nazywanego też pułapką.Brfalse (brfalse target, 0x39 <int32>) —
instrukcja przekazuje wykonanie do przesunięcia (ang. offset) podanego jako argument, jeśli wartość odłożona na stos przez poprzednią operację jest równafalse
, czyli zero (dotyczy wartościint32
,int64
, odwołania do obiektu, wskaźnika zarządzanego i niezarządzanego oraz wartości typunative int
). Należy zaznaczyć, że instrukcja zdejmuje ze stosu sprawdzaną wartość. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Brfalse_S (brfalse.s target, 0x2C <int8>) —
instrukcja przekazuje wykonanie do przesunięcia (ang. offset) podanego jako argument, jeśli wartość odłożona na stos przez poprzednią operację jest równafalse
, czyli zero (dotyczy wartościint32
,int64
, odwołania do obiektu, wskaźnika zarządzanego i niezarządzanego oraz wartości typunative int
). Należy zaznaczyć, że instrukcja zdejmuje ze stosu sprawdzaną wartość. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Brtrue (brtrue target, 0x3A <int32>) —
instrukcja przekazuje wykonanie do przesunięcia (ang. offset) podanego jako argument, jeśli wartość odłożona na stos przez poprzednią operację jest równatrue
, czyli różna od zera (dotyczy wartościint32
,int64
, odwołania do obiektu, wskaźnika zarządzanego i niezarządzanego oraz wartości typunative int
). Należy zaznaczyć, że instrukcja zdejmuje ze stosu sprawdzaną wartość. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Brtrue_S (brtrue.s target, 0x2D <int8>) —
instrukcja przekazuje wykonanie do przesunięcia (ang. offset) podanego jako argument, jeśli wartość odłożona na stos przez poprzednią operację jest równatrue
, czyli różna od zera (dotyczy wartościint32
,int64
, odwołania do obiektu, wskaźnika zarządzanego i niezarządzanego oraz wartości typunative int
). Należy zaznaczyć, że instrukcja zdejmuje ze stosu sprawdzaną wartość. Nie należy używać tej instrukcji do wchodzenia lub wychodzenia z blokówtry
,catch
,filter
orazfinally
.Call (call method, 0x28 <T>) —
instrukcja wywołuje podaną metodę przekazując jej argumenty, które należy umieścić na stosie. Rozkaz przed wywołaniem metody zdejmuje ze stosu wskaźnikthis
(jeśli istnieje) oraz wszystkie odłożone argumenty. Jeśli wywoływana metoda zwraca jakąś wartość, to wartość ta jest umieszczana na stosie po zakończeniu metody.Calli (calli method, 0x29 <T>) —
instrukcja wywołuje podaną metodę przekazując jej argumenty, które należy umieścić na stosie. Po odłożeniu argumentów należy na stosie umieścić wskaźnik do punktu wejścia wywoływanej metody. Jeśli wywoływana metoda zwraca jakąś wartość, to wartość ta jest umieszczana na stosie po zakończeniu metody.Callvirt (callvirt method, 0x6F <T>) —
instrukcja służy do wywoływania metody na rzecz obiektu, gdzie metoda jest wybierana podczas działania programu (ang. runtime) — nazywane jest to późnym wiązaniem (ang. late-bound). Odwołanie do obiektu oraz argumenty metody są odkładane na stos. Odłożone argumenty i odwołanie do obiektu są zdejmowane i wywoływana jest metoda na rzecz tego obiektu z tymi argumentami. Po wykonaniu metody na stos odkładana jest wartość zwrócona.Castclass (castclass typeToken, 0x74 <T>) —
odwołanie do obiektu jest odkładane na stos. Obiekt jest zdejmowany ze stosu i następuje próba rzutowania tego obiektu na określony typ (klasę). Jeśli rzutowanie (ang. cast) się powiedzie, to odwołanie do nowego obiektu jest odkładane na stos.Ceq (ceq, 0xFE01) —
dwie wartości są odkładane na stos. Wartości są zdejmowane, pierwsza wartość jest porównywana z drugą i jeśli są one równe, to na stos odkładana jest wartość jeden typuint32
. W przeciwnym wypadku na stos odkładana jest wartość zero typuint32
. Dla wartości zmiennoprzecinkowych instrukcja zwraca zero, jeśli jedna lub obie wartości są równe NaN. Należy zaznaczyć, że porównywanie ze sobą nieskończoności da wynik, że są one równe.Cgt (cgt, 0xFE02) —
dwie wartości są odkładane na stos. Wartości są zdejmowane, pierwsza wartość jest porównywana z drugą i jeśli pierwsza jest większa, to na stos odkładana jest wartość jeden typuint32
. W przeciwnym wypadku na stos odkładana jest wartość zero typuint32
. Dla wartości zmiennoprzecinkowych instrukcja zwraca zero, jeśli jedna lub obie wartości są równe NaN.Cgt_Un (cgt.un, 0xFE03) —
dwie wartości całkowite bez znaku (lub zmiennoprzecinkowe) są odkładane na stos. Wartości są zdejmowane, pierwsza wartość jest porównywana z drugą i jeśli pierwsza jest większa, to na stos odkładana jest wartość jeden typuint32
. W przeciwnym wypadku na stos odkładana jest wartość zero typuint32
.Ckfinite (ckfinite, 0xC3) —
wartość potrzebna do operacji jest odkładana na stos. Wartość jest zdejmowana ze stosu i jeśli wynosi NaN lub ±INFINITE, to rzucany jest wyjątekArithmeticException
. Wartość na stosie zostaje zachowana, jeśli wspomniany wyjątek nie został rzucony.Clt (clt, 0xFE04) —
dwie wartości są odkładane na stos. Wartości są zdejmowane, pierwsza wartość jest porównywana z drugą i jeśli pierwsza jest mniejsza, to na stos odkładana jest wartość jeden typuint32
. W przeciwnym wypadku na stos odkładana jest wartość zero typuint32
. Dla wartości zmiennoprzecinkowych instrukcja zwraca zero, jeśli jedna lub obie wartości są równe NaN.Clt_Un (clt.un, 0xFE05) —
dwie wartości całkowite bez znaku (lub zmiennoprzecinkowe) są odkładane na stos. Wartości są zdejmowane, pierwsza wartość jest porównywana z drugą i jeśli pierwsza jest mniejsza, to na stos odkładana jest wartość jeden typuint32
. W przeciwnym wypadku na stos odkładana jest wartość zero typuint32
.Constrained_ (constrained. thisType, 0xFE16 <T>) —
prefiks dla instrukcjicallvirt
, który pozwala nałożyć ograniczenie, że obiekt na rzecz którego następuje wywołanie metody jest typuT
.Conv_I (conv.i, 0xD3) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typunative int
. Nowa wartość po konwersji jest odkładana na stos jakonative int
.Conv_I1 (conv.i1, 0x67) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint8
. Nowa wartość po konwersji jest odkładana na stos jakoint32
.Conv_I2 (conv.i2, 0x68) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint16
. Nowa wartość po konwersji jest odkładana na stos jakoint32
.Conv_I4 (conv.i4, 0x69) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint32
. Nowa wartość po konwersji jest odkładana na stos jakoint32
.Conv_I8 (conv.i8, 0x6A) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint64
. Nowa wartość po konwersji jest odkładana na stos jakoint64
.Conv_Ovf_I (conv.ovf.i, 0xD4) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typunative int
. Nowa wartość po konwersji jest odkładana na stos jakonative int
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_I1 (conv.ovf.i1, 0xB3) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint8
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_I1_Un (conv.ovf.i1.un, 0x82) —
wartość bez znaku potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint8
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_I2 (conv.ovf.i2, 0xB5) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint16
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_I2_Un (conv.ovf.i2.un, 0x83) —
wartość bez znaku potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint16
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_I4 (conv.ovf.i4, 0xB7) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint32
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_I4_Un (conv.ovf.i4.un, 0x84) —
wartość bez znaku potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint32
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_I8 (conv.ovf.i8, 0xB9) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint64
. Nowa wartość po konwersji jest odkładana na stos jakoint64
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_I8_Un (conv.ovf.i8.un, 0x85) —
wartość bez znaku potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuint64
. Nowa wartość po konwersji jest odkładana na stos jakoint64
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_I_Un (conv.ovf.i.un, 0x8A) —
wartość bez znaku potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typunative int
. Nowa wartość po konwersji jest odkładana na stos jakonative int
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_U (conv.ovf.u, 0xD5) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typunative unsigned int
. Nowa wartość po konwersji jest odkładana na stos jakonative int
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_U1 (conv.ovf.u1, 0xB4) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int8
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_U1_Un (conv.ovf.u1.un, 0x86) —
wartość bez znaku potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int8
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_U2 (conv.ovf.u2, 0xB6) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int16
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_U2_Un (conv.ovf.u2.un, 0x87) —
wartość bez znaku potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int16
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_U4 (conv.ovf.u4, 0xB8) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int32
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_U4_Un (conv.ovf.u4.un, 0x88) —
wartość bez znaku potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int32
. Nowa wartość po konwersji jest odkładana na stos jakoint32
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_U8 (conv.ovf.u8, 0xBA) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int64
. Nowa wartość po konwersji jest odkładana na stos jakoint64
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_U8_Un (conv.ovf.u8.un, 0x89) —
wartość bez znaku potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int64
. Nowa wartość po konwersji jest odkładana na stos jakoint64
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_Ovf_U_Un (conv.ovf.u.un, 0x8B) —
wartość bez znaku potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typunative unsigned int
. Nowa wartość po konwersji jest odkładana na stos jakonative int
. Instrukcja rzuca wyjątekOverflowException
w przypadku przepełnienia.Conv_R4 (conv.r4, 0x6B) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typufloat32
. Nowa wartość po konwersji jest odkładana na stos jakoF
.Conv_R8 (conv.r8, 0x6C) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typufloat64
. Nowa wartość po konwersji jest odkładana na stos jakoF
.Conv_R_Un (conv.r.un, 0x76) —
wartość typuunsigned integer
potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typufloat
. Nowa wartość po konwersji jest odkładana na stos jakoF
.Conv_U (conv.u, 0xE0) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typunative unsigned int
. Nowa wartość po konwersji jest odkładana na stos jakonative int
.Conv_U1 (conv.u1, 0xD2) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int8
. Nowa wartość po konwersji jest odkładana na stos jakoint32
.Conv_U2 (conv.u2, 0xD1) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int16
. Nowa wartość po konwersji jest odkładana na stos jakoint32
.Conv_U4 (conv.u4, 0x6D) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int32
. Nowa wartość po konwersji jest odkładana na stos jakoint32
.Conv_U8 (conv.u8, 0x6E) —
wartość potrzebna do operacji jest odkładana na stos. Dane są zdejmowane z wierzchołka stosu i następuje konwersja tych danych do typuunsigned int64
. Nowa wartość po konwersji jest odkładana na stos jakoint64
.Cpblk (cpblk, 0xFE17) —
instrukcja kopiuje określoną liczbę bajtów z jednego bloku pamięci do innego. Adres bloku docelowego jest odkładany na stos. Adres bloku źródłowego jest odkładany na stos. Liczba bajtów do skopiowania również powinna zostać odłożona na stos. Rozkazcpblk
kopiuje liczbę bajtów wyrażoną jako typunsigned int32
z adresu źródłowego typunative int
,&
lub*
do adresu docelowego. Domyślnie kopiowany fragment pamięci powinien być wyrównany zgodnie z architekturą maszyny. W przypadku kopiowania bez wyrównania można zastosować prefiksunaligned.
.Cpobj (cpobj typeToken, 0x70 <T>) —
instrukcja kopiuje wartość spod adresu źródłowego typunative int
,&
lub*
do adresu docelowego typunative int
,&
lub*
. Odwołanie do obiektu docelowego jest odkładane na stos. Odwołanie do obiektu źródłowego jest również odkładane na stos. Odłożone dane są zdejmowane i następuje kopiowanie z adresu źródłowego do adresu docelowego. Jeśli adres jest nieprawidłowy może zostać rzucony wyjątekNullReferenceException
.Div (div, 0x5B) —
instrukcja dzielenia. Pierwsza wartość jest odkładana na stos. Druga wartość jest odkładana na stos. Wartości są zdejmowane i pierwsza wartość jest dzielona przez drugą. Wynik operacji dzielenia (iloraz) jest odkładany na stos. W przypadku dzielenia liczb całkowitych wynik jest przycinany (ang. truncated). Dla wartości zmiennoprzecinkowych warto zaznaczyć, że dzielenie przez zero zwraca w wyniku nieskończoność ze znakiem. Dzielenie zero przez zero zwraca w wyniku NaN. Dzielenie nieskończoności przez nieskończoność również zwraca NaN. A dzielenie liczby zmiennoprzecinkowej przez nieskończoność zwraca w wyniku zero.Div_Un (div.un, 0x5C) —
instrukcja dzielenia liczb całkowitych bez znaku. Pierwsza wartość jest odkładana na stos. Druga wartość jest odkładana na stos. Wartości są zdejmowane i pierwsza wartość jest dzielona przez drugą. Wynik operacji dzielenia (iloraz) jest odkładany na stos. Jeśli druga wartość jest równa zero, to rzucany jest wyjątekDivideByZeroException
.Dup (dup, 0x25) —
instrukcja zdejmuje dane z wierzchołka stosu i umieszcza ich kopię z powrotem na stosie. Rezultatem są dwie takie same wartości na górze stosu.Endfilter (endfilter, 0xFE11) —
instrukcja służy do wyjścia z blokufilter
w przypadku wyjątku. Wartość typuint32
jest odkładana na stos. Powinno to być zero (exception_continue_search
) albo jeden (exception_execute_handler
). Wynik w przypadku użycia innych wartości jest niezdefiniowany. Wartość zero oznacza kontynuowanie szukania obsługi wyjątków. Natomiast wartość jeden oznacza przejście do drugiego etapu obsługi wyjątku, gdzie blokifinally
są wykonywane dopóki nie zostanie znaleziona obsługa wyjątków powiązana z aktualnym blokiemfilter
. Po tym uruchamiana jest obsługa wyjątków.Endfinally (endfinally, endfault, 0xDC) —
instrukcja służy do powrotu z blokufinally
lubfault
obsługi wyjątków. Rozkaz ten leksykalnie może występować tylko w blokufinally
. Nie jest jednak wymagane, żeby blok kończył się rozkazemendfinally
. Instrukcja ta zatem wewnątrz bloku może być jedna, może być ich kilka lub może jej nie być wcale.Initblk (initblk, 0xFE18) —
instrukcja inicjalizuje podaną wartością określoną liczbę bajtów pod wskazanym adresem. Adres bloku (native int
lub&
) jest odkładany na stos. Wartość inicjalizująca jest odkładana na stos jakounsigned int8
. Rozmiar bloku pamięci jest również odkładany na stos (jakounsigned int32
). Rozkaz jest przeważnie używany do inicjalizacji struktur, dlatego oczekuje, że blok pamięci będzie wyrównany zgodnie z architekturą maszyny. W przypadku braku wyrównania możliwe jest użycie prefiksuunaligned.
.Initobj (initobj typeToken, 0xFE15 <T>) —
adres typunative int
,&
lub*
jest odkładany na stos. Instrukcja zdejmuje adres ze stosu i wpisuje we wskazywanym miejscu domyślną wartość dla danego typu. Jeśli adres jest typu prostego (ang. value type) otrzymuje zero,null
lub inną wartość domyślną. Natomiast jeśli jest typu referencyjnego, to otrzymuje wartośćnull
.Isinst (isinst, 0x75) —
instrukcja weryfikuje czy obiekt odłożony na stos jest określonego typu (jest instancją określonej klasy). Jeśli tak, to na stos jest odkładane odwołanie do tego obiektu. W przeciwnym wypadku na stos jest odkładane puste odwołanienull
.Jmp (jmp method, 0x27 <T>) —
instrukcja przekazuje kontrolę do podanej metody wraz z bieżącymi argumentami. Stos powinien być pusty podczas wykonywania tego rozkazu. Instrukcja ta nie służy do wychodzenia z blokówtry
,filter
,catch
orazfinally
.Ldarg (ldarg num, 0xFE09 <unsigned int16>) —
instrukcja odkłada argument o indeksie od 0 do 65535 na stos.Ldarg_0 (ldarg.0, 0x02) —
instrukcja odkłada argument o indeksie zero na stos. Argumentem tym domyślnie może być wskaźnikthis
klasy na rzecz której wywoływana jest metoda.Ldarg_1 (ldarg.1, 0x03) —
instrukcja odkłada argument o indeksie jeden na stos.Ldarg_2 (ldarg.2, 0x04) —
instrukcja odkłada argument o indeksie dwa na stos.Ldarg_3 (ldarg.3, 0x05) —
instrukcja odkłada argument o indeksie trzy na stos.Ldarg_S (ldarg.s num, 0x0E <unsigned int8>) —
instrukcja odkłada argument o indeksie od 4 do 255 na stos.Ldarga (ldarga num, 0xFE0A <unsigned int16>) —
instrukcja odkłada adres argumentu o indeksie od 0 do 65535 na stos.Ldarga_S (ldarga.s num, 0x0F <unsigned int8>) —
instrukcja odkłada adres argumentu o indeksie od 0 do 255 na stos.Ldc_I4 (ldc.i4 num, 0x20 <int32>) —
instrukcja odkłada wartość typuint32
na stos jako wartość typuint32
.Ldc_I4_0 (ldc.i4.0, 0x16) —
instrukcja odkłada wartość zero (0
) typuint32
na stos.Ldc_I4_1 (ldc.i4.1, 0x17) —
instrukcja odkłada wartość jeden (+1
) typuint32
na stos.Ldc_I4_2 (ldc.i4.2, 0x18) —
instrukcja odkłada wartość dwa (+2
) typuint32
na stos.Ldc_I4_3 (ldc.i4.3, 0x19) —
instrukcja odkłada wartość trzy (+3
) typuint32
na stos.Ldc_I4_4 (ldc.i4.4, 0x1A) —
instrukcja odkłada wartość cztery (+4
) typuint32
na stos.Ldc_I4_5 (ldc.i4.5, 0x1B) —
instrukcja odkłada wartość pięć (+5
) typuint32
na stos.Ldc_I4_6 (ldc.i4.6, 0x1C) —
instrukcja odkłada wartość sześć (+6
) typuint32
na stos.Ldc_I4_7 (ldc.i4.7, 0x1D) —
instrukcja odkłada wartość siedem (+7
) typuint32
na stos.Ldc_I4_8 (ldc.i4.8, 0x1E) —
instrukcja odkłada wartość osiem (+8
) typuint32
na stos.Ldc_I4_M1 (ldc.i4.m1, 0x15) —
instrukcja odkłada wartość minus jeden (-1
) typuint32
na stos.Ldc_I4_S (ldc.i4.s num, 0x1F <int8>) —
instrukcja odkłada wartość od-128
do127
typuint8
na stos jako wartość typuint32
.Ldc_I8 (ldc.i8 num, 0x21 <int64>) —
instrukcja odkłada wartość typuint64
na stos jako wartość typuint64
.Ldc_R4 (ldc.r4 num, 0x22 <float32>) —
instrukcja odkłada wartość typufloat32
na stos jako wartość typufloat32
.Ldc_R8 (ldc.r8 num, 0x23 <float64>) —
instrukcja odkłada wartość typufloat64
na stos jako wartość typufloat64
.Ldelem (ldelem typeToken, 0xA3 <T>) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy pod wskazywanym indeksem. Odczytany element jest odkładany na stos.Ldelem_I (ldelem.i, 0x97) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy typunative int
pod wskazywanym indeksem. Odczytany element jest odkładany na stos jako wartość typunative int
.Ldelem_I1 (ldelem.i1, 0x90) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy typuint8
pod wskazywanym indeksem. Odczytany element jest odkładany na stos jako wartość typuint32
.Ldelem_I2 (ldelem.i2, 0x92) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy typuint16
pod wskazywanym indeksem. Odczytany element jest odkładany na stos jako wartość typuint32
.Ldelem_I4 (ldelem.i4, 0x94) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy typuint32
pod wskazywanym indeksem. Odczytany element jest odkładany na stos jako wartość typuint32
.Ldelem_I8 (ldelem.i8, 0x96) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy typuint64
pod wskazywanym indeksem. Odczytany element jest odkładany na stos jako wartość typuint64
.Ldelem_R4 (ldelem.r4, 0x98) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy typufloat32
pod wskazywanym indeksem. Odczytany element jest odkładany na stos jako wartość typuF
.Ldelem_R8 (ldelem.r8, 0x99) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy typufloat64
pod wskazywanym indeksem. Odczytany element jest odkładany na stos jako wartość typuF
.Ldelem_Ref (ldelem.ref, 0x9A) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy typuO
(obiekt) pod wskazywanym indeksem. Odczytany element jest odkładany na stos jako obiekt typuO
.Ldelem_U1 (ldelem.u1, 0x91) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy typuunsigned int8
pod wskazywanym indeksem. Odczytany element jest odkładany na stos jako wartość typuint32
.Ldelem_U2 (ldelem.u2, 0x93) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy typuunsigned int16
pod wskazywanym indeksem. Odczytany element jest odkładany na stos jako wartość typuint32
.Ldelem_U4 (ldelem.u4, 0x95) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typunative int
lubint32
. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy typuunsigned int32
pod wskazywanym indeksem. Odczytany element jest odkładany na stos jako wartość typuint32
.Ldelema (ldelema typeToken, 0x8F <T>) —
odwołanie do tablicy jest odkładane na stos. Indeks elementu, który ma zostać odczytany jest odkładany na stos jako wartość typu native int lub int32. Instrukcja zdejmuje dane ze stosu i odczytuje element tablicy pod wskazywanym indeksem. Adres odczytanego elementu jest odkładany na stos jako wskaźnik zarządzany (&
).Ldfld (ldfld field, 0x7B <T>) —
odwołanie do obiektu (typuO
) jest odkładane na stos. Odwołanie do obiektu jest zdejmowane ze stosu i odczytywane jest określone pole w tym obiekcie. Wartość odczytanego pola jest odkładana na stos. Odwołanie do obiektu może być także typunative int
,&
lub*
.Ldflda (ldflda field, 0x7C <T>) —
odwołanie do obiektu (typuO
) jest odkładane na stos. Odwołanie do obiektu jest zdejmowane ze stosu i odczytywany jest adres określonego pola w tym obiekcie. Wartość adresu odczytanego pola jest odkładana na stos. Odwołanie do obiektu może być także typunative int
,&
lub*
.Ldftn (ldftn method, 0xFE06 <T>) —
instrukcja odkłada na stos wskaźnik do natywnego kodu podanej metody.Ldind_I (ldind.i, 0x4D) —
adres danych typunative int
,&
lub*
jest odkładany na stos. Adres jest zdejmowany ze stosu i odczytywana jest wartość typunative int
znajdująca się pod tym adresem. Odczytana wartość jest odkładana na stos jako dane typunative int
.Ldind_I1 (ldind.i1, 0x46) —
adres danych typunative int
,&
lub*
jest odkładany na stos. Adres jest zdejmowany ze stosu i odczytywana jest wartość typuint8
znajdująca się pod tym adresem. Odczytana wartość jest odkładana na stos jako dane typuint32
.Ldind_I2 (ldind.i2, 0x48) —
adres danych typunative int
,&
lub*
jest odkładany na stos. Adres jest zdejmowany ze stosu i odczytywana jest wartość typuint16
znajdująca się pod tym adresem. Odczytana wartość jest odkładana na stos jako dane typuint32
.Ldind_I4 (ldind.i4, 0x4A) —
adres danych typunative int
,&
lub*
jest odkładany na stos. Adres jest zdejmowany ze stosu i odczytywana jest wartość typuint32
znajdująca się pod tym adresem. Odczytana wartość jest odkładana na stos jako dane typuint32
.Ldind_I8 (ldind.i8, 0x4C) —
adres danych typunative int
,&
lub*
jest odkładany na stos. Adres jest zdejmowany ze stosu i odczytywana jest wartość typuint64
znajdująca się pod tym adresem. Odczytana wartość jest odkładana na stos jako dane typuint64
.Ldind_R4 (ldind.r4, 0x4E) —
adres danych typunative int
,&
lub*
jest odkładany na stos. Adres jest zdejmowany ze stosu i odczytywana jest wartość typufloat32
znajdująca się pod tym adresem. Odczytana wartość jest odkładana na stos jako dane typuF
.Ldind_R8 (ldind.r8, 0x4F) —
adres danych typunative int
,&
lub*
jest odkładany na stos. Adres jest zdejmowany ze stosu i odczytywana jest wartość typufloat64
znajdująca się pod tym adresem. Odczytana wartość jest odkładana na stos jako dane typuF
.Ldind_Ref (ldind.ref, 0x50) —
odwołanie do obiektu typunative int
,&
lub*
jest odkładane na stos. Adres jest zdejmowany ze stosu i tworzone jest odwołanie do obiektu znajdującego się pod tym adresem. Odczytane odwołanie jest odkładane na stos jako obiekt typuO
.Ldind_U1 (ldind.u1, 0x47) —
adres danych typunative int
,&
lub*
jest odkładany na stos. Adres jest zdejmowany ze stosu i odczytywana jest wartość typuunsigned int8
znajdująca się pod tym adresem. Odczytana wartość jest odkładana na stos jako dane typuint32
.Ldind_U2 (ldind.u2, 0x49) —
adres danych typunative int
,&
lub*
jest odkładany na stos. Adres jest zdejmowany ze stosu i odczytywana jest wartość typuunsigned int16
znajdująca się pod tym adresem. Odczytana wartość jest odkładana na stos jako dane typuint32
.Ldind_U4 (ldind.u4, 0x4B) —
adres danych typunative int
,&
lub*
jest odkładany na stos. Adres jest zdejmowany ze stosu i odczytywana jest wartość typuunsigned int32
znajdująca się pod tym adresem. Odczytana wartość jest odkładana na stos jako dane typuint32
.Ldlen (ldlen, 0x8E) —
odwołanie do tablicy jest odkładane na stos. Odwołanie jest zdejmowane ze stosu i obliczana jest długość tablicy. Liczba elementów tablicy jest odkładana na stos jako wartość typunative unsigned int
.Ldloc (ldloc index, 0xFE0C <unsigned int16>) —
instrukcja odkłada daną lokalną o indeksie od 0 do 65535 na stos.Ldloc_0 (ldloc.0, 0x06) —
instrukcja odkłada daną lokalną o indeksie zero na stos.Ldloc_1 (ldloc.1, 0x07) —
instrukcja odkłada daną lokalną o indeksie jeden na stos.Ldloc_2 (ldloc.2, 0x08) —
instrukcja odkłada daną lokalną o indeksie dwa na stos.Ldloc_3 (ldloc.3, 0x09) —
instrukcja odkłada daną lokalną o indeksie trzy na stos.Ldloc_S (ldloc.s num, 0x11 <unsigned int8>) —
instrukcja odkłada daną lokalną o indeksie od 0 do 255 na stos.Ldloca (ldloca, 0xFE0D) —
instrukcja odkłada na stos adres przechowywany w danej lokalnej o indeksie od 0 do 65535.Ldloca_S (ldloca.s num, 0x12 <unsigned int8>) —
instrukcja odkłada na stos adres przechowywany w danej lokalnej o indeksie od 0 do 255.Ldnull (ldnull, 0x14) —
instrukcja odkładanull
na stos. Używane do inicjalizacji wartościąnull
lub oznaczania miejsc w pamięci, które nie są już potrzebne (zostają zwolnione).Ldobj (ldobj typeToken, 0x71 <T>) —
adres do danych typu prostego jest odkładany na stos. Odwołanie jest zdejmowane ze stosu i odczytywana jest wartość na którą wskazuje. Odczytana wartość jest odkładana na stos.Ldsfld (ldsfld field, 0x7E <T>) —
instrukcja odkłada na stos wartość statycznego pola. Typ odkładanej wartości jest powiązany z typem statycznego pola. Warto dodać, że pole statyczne jest dostępne niezależnie od instancji klasy (można uzyskać do niego dostęp z każdej instancji).Ldsflda (ldsflda field, 0x7F <T>) —
instrukcja odkłada na stos adres statycznego pola. Typ odkładanego adresu to&
w przypadku pamięci zarządzanej lubnative int
w przypadku pamięci niezarządzanej. Warto dodać, że pole statyczne jest dostępne niezależnie od instancji klasy (można uzyskać do niego dostęp z każdej instancji).Ldstr (ldstr token, 0x72 <T>) —
instrukcja odkłada na stos nowy obiekt zawierający napis. Warto dodać, że CLI zwróci ten sam obiekt, gdy zostanie rozpoznane, że dwa napisy zawierają takie same ciągi znaków.Ldtoken (ldtoken token, 0xD0 <T>) —
instrukcja odkłada na stos uchwytRuntimeHandle
dla określonego rodzaju tokena. Dlamethoddef
/methodref
/methodspec
odłoży na stosRuntimeMethodHandle
. Dlatypedef
/typeref
/typespec
odłoży na stosRuntimeTypeHandle
. Dlafielddef
/fieldref
odłoży na stosRuntimeFieldHandle
.Ldvirtftn (ldvirtftn method, 0xFE07 <T>) —
odwołanie do obiektu jest odkładane na stos. Odwołanie jest zdejmowane ze stosu i odczytywany jest adres początku kodu natywnego ciała metody powiązanej z obiektem. Wskaźnik do natywnego kodu metody jest odkładany na stos. W prostych słowach: Instrukcja odkłada na stos adres metody wirtualnej.Leave (leave target, 0xDD <int32>) —
instrukcja bezwarunkowo przekazuje wykonanie do rozkazu pod podanym jako argument przesunięciem (ang. offset). Przesunięcie to wartość ze znakiem o rozmiarze czterech bajtów obliczana od początku instrukcji znajdującej się zaraz za bieżącą instrukcją. Można używać tej instrukcji do wychodzenia z blokówtry
,catch
orazfilter
. Instrukcja zapewnia, że blokifinally
zostaną wykonane (nie można nią wychodzić z tych bloków). Można używać tej instrukcji nawet do wychodzenia z zagnieżdżonych bloków kodu.Leave_S (leave.s target, 0xDE <int8>) —
instrukcja bezwarunkowo przekazuje wykonanie do rozkazu pod podanym jako argument przesunięciem (ang. offset). Przesunięcie to wartość ze znakiem o rozmiarze jednego bajta obliczana od początku instrukcji znajdującej się zaraz za bieżącą instrukcją. Można używać tej instrukcji do wychodzenia z blokówtry
,catch
orazfilter
. Instrukcja zapewnia, że blokifinally
zostaną wykonane (nie można nią wychodzić z tych bloków). Można używać tej instrukcji nawet do wychodzenia z zagnieżdżonych bloków kodu.Localloc (localloc, 0xFE0F) —
na stos odkładana jest wartość typunative unsigned int
określająca ile bajtów pamięci ma zostać zarezerwowane. Wartość jest zdejmowana i po alokacji pamięci na stos odkładany jest wskaźnik niezarządzany (native int
) do pierwszego bajta w fragmencie pamięci. Pamięć alokowana jest na nowo. Po wyjściu z metody jest zwalniana i może być ponownie użyta. Instrukcja nie może występować w blokachfilter
,catch
,finally
ifault
.Mkrefany (mkrefany token, 0xC6 <T>) —
na stos odkładany jest wskaźnik (native int
,&
,*
) do fragmentu danych. Wskaźnik jest zdejmowany ze stosu, aby z adresu i podanego typu stworzyć odwołanie do obiektu określonego typu. Utworzone odwołanie jest odkładane na stos.Mul (mul, 0x5A) —
pierwsza wartość jest odkładana na stos. Druga wartość jest odkładana na stos. Wartości są zdejmowane ze stosu i wykonywana jest operacja mnożenia, którego wynik umieszczany jest na stosie. Należy zaznaczyć, że dla liczb zmiennoprzecinkowych pomnożenie zero przez nieskończoność zwróci NaN.Mul_Ovf (mul.ovf, 0xD8) —
instrukcja wykonuje mnożenie ze znakiem liczb całkowitych. Pierwsza wartość jest odkładana na stos. Druga wartość jest odkładana na stos. Wartości są zdejmowane ze stosu i wykonywana jest operacja mnożenia, którego wynik umieszczany jest na stosie. W przypadku przepełnienia rzucany jest wyjątekOverflowException
.Mul_Ovf_Un (mul.ovf.un, 0xD9) —
instrukcja wykonuje mnożenie bez znaku liczb całkowitych. Pierwsza wartość jest odkładana na stos. Druga wartość jest odkładana na stos. Wartości są zdejmowane ze stosu i wykonywana jest operacja mnożenia, którego wynik umieszczany jest na stosie. W przypadku przepełnienia rzucany jest wyjątekOverflowException
.Neg (neg, 0x65) —
na stos odkładana jest wartość liczbowa do zanegowania. Wartość jest zdejmowana ze stosu i wykonywana jest negacja (U2). Wynik operacji jest odkładany na stos i ma taki sam typ jak operand. W przypadku liczb całkowitych negacja (U2) najmniejszej możliwej wartości zwraca w wyniku najmniejszą możliwą wartość. W celu wykrycia tego rodzaju przepełnienia należy użyć rozkazusub.ovf
. Negacja liczby zmiennoprzecinkowej nie powoduje przepełnienia. Negacja NaN zwraca w wyniku NaN.Newarr (newarr token, 0x8D <T>) —
na stos odkładana jest wartość określająca liczbę elementów tworzonej tablicy. Wartość jest zdejmowana ze stosu i następuje rezerwacja pamięci dla tablicy wskazanego typu. Na stos odkładane jest odwołanie do jednowymiarowej tablicy, indeksowanej od zera, wypełnionej wartością początkową zero. Warto dodać, że tablice wielowymiarowe są tworzone specjalnymi metodami zawartymi w .NET Framework, a nie tym rozkazem.Newobj (newobj ctor, 0x73 <T>) —
na stos odkładane są argumenty od jeden doN
. Argumenty są zdejmowane ze stosu i przekazywane do konstruktora (ctor
) w celu utworzenia obiektu. Możliwe jest tworzenie obiektów typu prostego (ang. value type) lub referencyjnego. Pola tworzonego obiektu są inicjalizowane zerami,null
lub inną domyślną wartością dla danego typu. Wywoływany jest konstruktor (metoda). Po utworzeniu obiektu na stos odkładane jest odwołanie do niego.Nop (nop, 0x00) —
instrukcja służy do wypełniania miejsca w przypadku modyfikacji kodu bajtowego. Nie wykonuje niczego konkretnego (ang. no operation).Not (not, 0x66) —
wartość liczbowa jest odkładana na stos. Wartość jest zdejmowana ze stosu i wykonywane jest zaprzeczenie logiczne, którego wynik odkładany jest na stos. Typ wyniku jest zgodny z typem podanego operandu.Or (or, 0x60) —
pierwsza liczba całkowita jest odkładana na stos. Druga liczba całkowita jest odkładana na stos. Wartości są zdejmowane ze stosu i wykonywana jest alternatywa logiczna, której wynik odkładany jest na stosie.Pop (pop, 0x26) —
instrukcja zdejmuje (usuwa) dane z wierzchołka stosu.Readonly_ (readonly., 0xFE1E) —
prefiks, który powoduje, że następująca po nim instrukcja operacji adresowania na tablicy nie wykonuje kontroli typu podczas działania (ang. runtime).Refanytype (refanytype, 0xFE1D) —
odwołanie jest odkładane na stos. Odwołanie jest zdejmowane ze stosu i typ niniejszego odwołania jest odkładany na stos. W prostych słowach: Instrukcja pozwala odczytać typ odwołania utworzonego przez rozkazmkrefany
.Refanyval (refanyval token, 0xC2 <T>) —
odwołanie jest odkładane na stos. Odwołanie jest zdejmowane ze stosu i adres typu&
wydobyty niniejszego odwołania jest odkładany na stos. W prostych słowach: Instrukcja pozwala odczytać adres z odwołania utworzonego przez rozkazmkrefany
.Rem (rem, 0x5D) —
instrukcja reszty z dzielenia ze znakiem. Pierwsza wartość jest odkładana na stos. Druga wartość jest odkładana na stos. Wartości są zdejmowane i pierwsza wartość jest dzielona przez drugą. Reszta z operacji dzielenia jest odkładana na stos.Rem_Un (rem.un, 0x5E) —
instrukcja reszty z dzielenia bez znaku. Pierwsza wartość jest odkładana na stos. Druga wartość jest odkładana na stos. Wartości są zdejmowane i pierwsza wartość jest dzielona przez drugą. Reszta z operacji dzielenia jest odkładana na stos.Ret (ret, 0x2A) —
instrukcja wychodzi z bieżącej metody (wraca do metody, która ją wywołała). Jeśli wywoływana metoda zwróciła wartość, to wartość ta jest pobierana z wierzchołka stosu i kopiowana na stos metody nadrzędnej, która wywołała bieżącą metodę. Stos aktualnej metody powinien być pusty z wyjątkiem odłożonej wartości, która ma zostać zwrócona.Rethrow (rethrow, 0xFE1A) —
instrukcja jest dozwolona tylko wewnątrz blokucatch
i rzuca ponownie wyjątek złapany przezcatch
. Nie modyfikuje śladu stosu w obiekcie.Shl (shl, 0x62) —
wartość liczbowa typuint32
,int64
lubnative int
jest odkładana na stos. Liczba bitów o którą nastąpi przesunięcie jest odkładana na stos jakoint32
lubnative int
. Wartości są zdejmowane ze stosu i następuje przesunięcie bitowe w lewo (z prawej strony wchodzą zera), którego wynik jest odkładany na stos.Shr (shr, 0x63) —
wartość liczbowa typuint32
,int64
lubnative int
jest odkładana na stos. Liczba bitów o którą nastąpi przesunięcie jest odkładana na stos jakoint32
lubnative int
. Wartości są zdejmowane ze stosu i następuje przesunięcie bitowe w prawo (na bity z lewej strony powielany jest bit znaku), którego wynik jest odkładany na stos.Shr_Un (shr.un, 0x64) —
wartość liczbowa typuint32
,int64
lubnative int
jest odkładana na stos. Liczba bitów o którą nastąpi przesunięcie jest odkładana na stos jakoint32
lubnative int
. Wartości są zdejmowane ze stosu i następuje przesunięcie bitowe w prawo (na bity z lewej strony wchodzą zera), którego wynik jest odkładany na stos.Sizeof (sizeof token, 0xFE1C <T>) —
instrukcja odkłada na stos rozmiar w bajtach podanego typu. Wartość odkładana na stos jest typuunsigned int32
.Starg (starg num, 0xFE0B <unsigned int16>) —
instrukcja zdejmuje dane z wierzchołka stosu i umieszcza w argumencie o indeksie od 0 do 65535.Starg_S (starg.s num, 0x10 <unsigned int8>) —
instrukcja zdejmuje dane z wierzchołka stosu i umieszcza w argumencie o indeksie od 0 do 255.Stelem (stelem token, 0xA4 <T>) —
na stos odkładane jest odwołanie do tablicy. Odkładany jest indeks elementu tablicy. Odkładana jest także nowa wartość dla elementu. Odłożone wartości są zdejmowane ze stosu i wartość elementu tablicy pod podanym indeksem jest podmieniana na inną.Stelem_I (stelem.i, 0x9B) —
na stos odkładane jest odwołanie do tablicy. Odkładany jest indeks elementu tablicy (native int
lubint32
). Odkładana jest także nowa wartość typunative int
dla elementu. Odłożone wartości są zdejmowane ze stosu i wartość elementu tablicy pod podanym indeksem jest podmieniana na inną.Stelem_I1 (stelem.i1, 0x9C) —
na stos odkładane jest odwołanie do tablicy. Odkładany jest indeks elementu tablicy (native int
lubint32
). Odkładana jest także nowa wartość typuint8
dla elementu. Odłożone wartości są zdejmowane ze stosu i wartość elementu tablicy pod podanym indeksem jest podmieniana na inną.Stelem_I2 (stelem.i2, 0x9D) —
na stos odkładane jest odwołanie do tablicy. Odkładany jest indeks elementu tablicy (native int
lubint32
). Odkładana jest także nowa wartość typuint16
dla elementu. Odłożone wartości są zdejmowane ze stosu i wartość elementu tablicy pod podanym indeksem jest podmieniana na inną.Stelem_I4 (stelem.i4, 0x9E) —
na stos odkładane jest odwołanie do tablicy. Odkładany jest indeks elementu tablicy (native int
lubint32
). Odkładana jest także nowa wartość typuint32
dla elementu. Odłożone wartości są zdejmowane ze stosu i wartość elementu tablicy pod podanym indeksem jest podmieniana na inną.Stelem_I8 (stelem.i8, 0x9F) —
na stos odkładane jest odwołanie do tablicy. Odkładany jest indeks elementu tablicy (native int
lubint32
). Odkładana jest także nowa wartość typuint64
dla elementu. Odłożone wartości są zdejmowane ze stosu i wartość elementu tablicy pod podanym indeksem jest podmieniana na inną.Stelem_R4 (stelem.r4, 0xA0) —
na stos odkładane jest odwołanie do tablicy. Odkładany jest indeks elementu tablicy (native int
lubint32
). Odkładana jest także nowa wartość typufloat32
dla elementu. Odłożone wartości są zdejmowane ze stosu i wartość elementu tablicy pod podanym indeksem jest podmieniana na inną.Stelem_R8 (stelem.r8, 0xA1) —
na stos odkładane jest odwołanie do tablicy. Odkładany jest indeks elementu tablicy (native int
lubint32
). Odkładana jest także nowa wartość typufloat64
dla elementu. Odłożone wartości są zdejmowane ze stosu i wartość elementu tablicy pod podanym indeksem jest podmieniana na inną.Stelem_Ref (stelem.ref, 0xA2) —
na stos odkładane jest odwołanie do tablicy. Odkładany jest indeks elementu tablicy (native int
lubint32
). Odkładana jest także nowa wartość typu referencyjnego dla elementu. Odłożone wartości są zdejmowane ze stosu i wartość elementu tablicy pod podanym indeksem jest podmieniana na inną.Stfld (stfld field, 0x7D <T>) —
na stos odkładane jest odwołanie do obiektu. Odkładana jest także nowa wartość dla pola. Odłożone wartości są zdejmowane ze stosu i wartość pola obiektu jest podmieniana na nową.Stind_I (stind.i, 0xDF) —
adres danych typunative int
,&
lub*
oraz wartość typunative int
są odkładane na stos. Odłożona wartość oraz adres są zdejmowane ze stosu w celu umieszczenia wartości pod adresem.Stind_I1 (stind.i1, 0x52) —
adres danych typunative int
,&
lub*
oraz wartość typuint8
są odkładane na stos. Odłożona wartość oraz adres są zdejmowane ze stosu w celu umieszczenia wartości pod adresem.Stind_I2 (stind.i2, 0x53) —
adres danych typunative int
,&
lub*
oraz wartość typuint16
są odkładane na stos. Odłożona wartość oraz adres są zdejmowane ze stosu w celu umieszczenia wartości pod adresem.Stind_I4 (stind.i4, 0x54) —
adres danych typunative int
,&
lub*
oraz wartość typuint32
są odkładane na stos. Odłożona wartość oraz adres są zdejmowane ze stosu w celu umieszczenia wartości pod adresem.Stind_I8 (stind.i8, 0x55) —
adres danych typunative int
,&
lub*
oraz wartość typuint64
są odkładane na stos. Odłożona wartość oraz adres są zdejmowane ze stosu w celu umieszczenia wartości pod adresem.Stind_R4 (stind.r4, 0x56) —
adres danych typunative int
,&
lub*
oraz wartość typufloat32
są odkładane na stos. Odłożona wartość oraz adres są zdejmowane ze stosu w celu umieszczenia wartości pod adresem.Stind_R8 (stind.r8, 0x57) —
adres danych typunative int
,&
lub*
oraz wartość typufloat64
są odkładane na stos. Odłożona wartość oraz adres są zdejmowane ze stosu w celu umieszczenia wartości pod adresem.Stind_Ref (stind.ref, 0x51) —
adres danych typunative int
,&
lub*
oraz odwołanie do obiektu typuO
są odkładane na stos. Odłożona wartość oraz adres są zdejmowane ze stosu w celu umieszczenia wartości pod tym adresem.Stloc (stloc index, 0xFE0E <unsigned int16>) —
instrukcja zdejmuje dane z wierzchołka stosu i umieszcza w danej lokalnej o indeksie od 0 do 65535.Stloc_0 (stloc.0, 0x0A) —
instrukcja zdejmuje dane z wierzchołka stosu i umieszcza w danej lokalnej o indeksie zero.Stloc_1 (stloc.1, 0x0B) —
instrukcja zdejmuje dane z wierzchołka stosu i umieszcza w danej lokalnej o indeksie jeden.Stloc_2 (stloc.2, 0x0C) —
instrukcja zdejmuje dane z wierzchołka stosu i umieszcza w danej lokalnej o indeksie dwa.Stloc_3 (stloc.3, 0x0D) —
instrukcja zdejmuje dane z wierzchołka stosu i umieszcza w danej lokalnej o indeksie trzy.Stloc_S (stloc.s num, 0x13 <unsigned int8>) —
instrukcja zdejmuje dane z wierzchołka stosu i umieszcza w danej lokalnej o indeksie od 0 do 255.Stobj (stobj token, 0x81 <T>) —
na stos odkładany jest adres (wskaźnik typunative int
,&
lub*
). Na stos odkładany jest także obiekt źródłowy. Dane są zdejmowane ze stosu i obiekt jest kopiowany do miejsca wskazywanego przez adres docelowy.Stsfld (stsfld field, 0x80 <T>) —
nowa wartość dla pola jest odkładana na stos. Wartość jest zdejmowana ze stosu i zachowywana w polu statycznym klasy. Pole w klasie jest określone przez operand instrukcji.Sub (sub, 0x59) —
dwie wartości są odkładane na stos. Odłożone wartości są zdejmowane i od pierwszej wartości jest odejmowana druga. Wynik operacji odejmowania jest odkładany na stos. Instrukcja nie wykrywa przepełnienia całkowitoliczbowego (ang. integer overflow). Przepełnienie (ang. overflow) dla wartości zmiennoprzecinkowych (ang. floating-point) zwraca nieskończoność (ang. ±infinity), natomiast błąd niedomiaru (ang. underflow) zwraca zero.Sub_Ovf (sub.ovf, 0xDA) —
dwie wartości całkowite ze znakiem są odkładane na stos. Odłożone wartości są zdejmowane i od pierwszej wartości jest odejmowana druga. Wynik operacji odejmowania jest odkładany na stos. W przypadku przepełnienia rzucany jest wyjątekOverflowException
.Sub_Ovf_Un (sub.ovf.un, 0xDB) —
dwie wartości całkowite bez znaku są odkładane na stos. Odłożone wartości są zdejmowane i od pierwszej wartości jest odejmowana druga. Wynik operacji odejmowania jest odkładany na stos. W przypadku przepełnienia rzucany jest wyjątekOverflowException
.Switch (switch (IL_0001, IL_0002 ... IL_000N), 0x45 <unsigned int32> <int32>...<int32>) —
instrukcja implementuje tablicę skoków. Podane argumenty to przesunięcia (ang. offset) względem początku instrukcji, która występuje poswitch
. Przykładowa składnia:switch (IL_0022, IL_0041, IL_0060, IL_007F)
. Instrukcja zdejmuje ostatnio odłożoną wartość z wierzchołka stosu i porównuje do liczby argumentówn
. W przykładowym kodzie jest ich cztery. Teraz zgodnie z wynikiem porównania wykonywane są skoki do etykiet (przesunięć), czyli jeśli wartość zdjęta ze stosu jest mniejsza odn
(tutaj cztery), to wykonywany jest skok doIL_007F
. Jeśli jest mniejsza od trzy, to wykonanie jest przekazywane do etykietyIL_0060
. Jeśli mniejsza od dwa, to skok do etykietyIL_0041
. A jeśli mniejsza od jeden, to skok do etykietyIL_0022
.Tail_ (tail., 0xFE14) —
prefiks ten poprzedzając metodę powoduje zwolenienie ramki stosu bieżącej metody przed wywołaniem kolejnej rozkazemcall
,calli
czycallvirt
.Throw (throw, 0x7A) —
na stosie odkładany jest obiekt reprezentujący wyjątek. Obiekt jest zdejmowany ze stosu i rzucany jako wyjątek.Unaligned_ (unaligned. alignment, 0xFE12 <unsigned int8>) —
prefiks ten określa, że adres używany w instrukcjachldind
,stind
,ldfld
,stfld
,ldobj
,stobj
czyinitblk
,cpblk
może nie być wyrównany zgodnie z architekturą maszyny. Wartość operandu powinna być równa jeden (wyrównanie do bajta), dwa (wyrównanie do słowa) lub cztery (wyrównanie do podwójnego słowa). Domyślne wyrównanie dla architektury 32-bitowej to cztery, a dla architektury 64-bitowej to osiem. Jeśli generator kodu powoduje, że podczas działania nie ma pewności wyrównania do ośmiu bajtów powinien być stosowany ten prefiks.Unbox (unbox token, 0x79 <T>) —
opakowane (ang. boxed) dane typu prostego (ang. value type) lub dane typu referencyjnego są odkładane na stos. Obiekt jest zdejmowany i wykonywana jest operacja odpakowywania (ang. unboxing). Po wykonaniu tej operacji na stos odkładana jest wydobyta wartość.Unbox_Any (unbox.any token, 0xA5 <T>) —
opakowane (ang. boxed) dane typu prostego (ang. value type) lub dane typu referencyjnego są odkładane na stos. Dane są zdejmowane i jeśli są opakowanymi (ang. boxed) danymi typu prostego (ang. value type), to wydobywana jest wartość z tego obiektu. Po wykonaniu tej operacji na stos odkładana jest wydobyta wartość. Natomiast jeśli instrukcja jest zastosowana do danych typu referencyjnego, to działa jak rozkazcastclass
, czyli na stos zostanie odłożony obiekt, a nie wydobyta wartość.Volatile_ (volatile., 0xFE13) —
prefiks ostrzega, że adres znajdujący się na wierzchołku stosu jest ulotny. Oznacza to m.in. brak możliwości umieszczenia wyników odczytu danych spod tego adresu w pamięci podręcznej oraz brak możliwości połączenia wielu operacji zapisu do tej lokalizacji. Należy zaznaczyć, że nie ma wymogu, aby dostęp do lokalizacji oznaczonych jako ulotne był operacją niepodzielną (ang. atomic).Xor (xor, 0x61) —
pierwsza liczba całkowita jest odkładana na stos. Druga liczba całkowita jest odkładana na stos. Wartości są zdejmowane ze stosu i wykonywana jest alternatywa wykluczająca, której wynik odkładany jest na stosie.
Generowanie kodu CIL z użyciem klasy ILGenerator
Dynamicznie generowany kod znajduje szerokie zastosowanie w wirusach komputerowych i złośliwym oprogramowaniu (ang. malware).
Może on utrudniać analitykom badanie zachowania próbki, a programom obronnym identyfikację i oznaczenie próbki sygnaturą w postaci wzorca binarnego.
Poniżej przedstawiono kod źródłowy przykładowego programu, który podczas działania
tworzy dynamiczną metodę i umieszcza w niej instrukcje języka pośredniego CIL.
Poszczególne rozkazy są dodawane do strumienia instrukcji za pomocą metody void ILGenerator.Emit(OpCode opcode);
.
Przykładowa metoda utworzona dynamicznie przyjmuje dwa argumenty w postaci liczb całkowitych, wykonuje dodawanie i zwraca w wyniku sumę tych liczb.
Po uruchomieniu przykładowego kodu na konsoli tekstowej powinno się wyświetlić 2 + 1 = 3.
Kod źródłowy przykładu tworzenia dynamicznej metody zaprezentowano poniżej.
using System.Reflection.Emit;
internal class Program
{
private static void Main()
{
DynamicMethod add = new("Add", typeof(int), [typeof(int), typeof(int)]);
ILGenerator generator = add.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Add);
generator.Emit(OpCodes.Ret);
var c = add.Invoke(null, [2, 1]) as int?;
Console.WriteLine($"2 + 1 = {c}");
}
}
Dynamiczna kompilacja kodu źródłowego w języku C# za pomocą klasy CSharpCompilation
Podmioty zagrażające (ang. threat actor) mogą próbować dostarczać kod ładunku (ang. payload) w postaci nieskompilowanej w celu utrudnienia wykrycia.
Metody zabezpieczeń ukierunkowane na pliki zawierające kod binarny mogą być wtedy nieskuteczne.
Z tego powodu kolejny edukacyjny przykład to kompilacja kodu źródłowego w języku C# podczas działania aplikacji (ang. runtime).
W celu przejrzystego zaprezentowania idei działania przykład nie zawiera zaciemnienia (ang. obfuscation).
Przykładowa aplikacja kompiluje kod źródłowy C# do modułu (ang. assembly), który później jest ładowany do pamięci za pomocą Assembly.Load(...);
,
po czym następuje wywołanie metody głównej Main
.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Reflection;
internal class Program
{
private static void Main()
{
string code = "internal class Program { private static void Main() { System.Console.WriteLine(\"ethical.blue Magazine\"); } }";
var root = Path.GetDirectoryName(typeof(object).Assembly.Location) ?? string.Empty;
var tree = SyntaxFactory.ParseSyntaxTree(code);
var compilation = CSharpCompilation.Create(Path.GetRandomFileName())
.WithOptions(new CSharpCompilationOptions(OutputKind.ConsoleApplication,
optimizationLevel: OptimizationLevel.Release))
.AddReferences(MetadataReference.CreateFromFile(Path.Combine(root, "System.dll")))
.AddReferences(MetadataReference.CreateFromFile(Path.Combine(root, "System.Runtime.dll")))
.AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(Console).Assembly.Location))
.AddSyntaxTrees(tree);
using MemoryStream ILCodeStream = new();
var result = compilation.Emit(ILCodeStream);
if (result.Success)
{
var assembly = Assembly.Load(ILCodeStream.ToArray());
_ = assembly.EntryPoint?.Invoke(null, []);
}
#if DEBUG
foreach (var error in result.Diagnostics)
Console.WriteLine(error.ToString());
#endif
}
}
Wykonanie surowych bajtów poprzez utworzenie delegatu ze wskaźnika do pamięci niezarządzanej
Program dla platformy .NET oprócz kodu zarządzanego (ang. managed) posiada także możliwość wywoływania natywnych funkcji systemowych (np. Windows API). Może też uruchamiać ładunki (ang. payload), które są kodem maszynowym dla architektury sprzętowej na której działa aplikacja. W prosty i niegroźny sposób prezentuje to poniższy przykład.
using System.Runtime.InteropServices;
internal class Program
{
internal static uint MEM_COMMIT = 0x1000;
internal static uint PAGE_EXECUTE_READWRITE = 0x40;
[DllImport("kernel32.dll")]
static extern unsafe IntPtr VirtualAlloc(
IntPtr lpAddress, int dwSize, uint flAllocationType, uint flProtect);
internal delegate int BytesLauncher();
static void Main()
{
byte[] rawBytes = [ 0x90, 0x90, 0xC3 ];
var payload = VirtualAlloc(IntPtr.Zero, rawBytes.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(rawBytes, 0, payload, rawBytes.Length);
if (Marshal.GetDelegateForFunctionPointer(
payload, typeof(BytesLauncher)) is BytesLauncher ExecutePayload)
{
_ = ExecutePayload();
}
}
}
Wykaz literatury
- [1] https://ecma-international.org/publications-and-standards/standards/ecma-335/ [dostęp: 2024-04-03]
- [2] https://ecma-international.org/publications-and-standards/standards/ecma-334/ [dostęp: 2024-04-03]
- [3] https://standards.iso.org/ittf/PubliclyAvailableStandards/c075178_ISO_IEC_23270_2018.zip [dostęp: 2024-04-03]
- [4] https://standards.iso.org/ittf/PubliclyAvailableStandards/c058046_ISO_IEC_23271_2012(E).zip [dostęp: 2024-04-03]