From cc335b70746f926e2a0b69cf2bd9a1535807f5e9 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sat, 6 Jul 2024 01:16:06 -0400 Subject: [PATCH 01/20] Started dasm stuff. --- .../Disassembler/Generate-Opcode-Enums.ps1 | 40 ++++ .../Disassembler/Opcodes.txt | 219 ++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 create mode 100644 Reemit.Decompiler.Clr/Disassembler/Opcodes.txt diff --git a/Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 b/Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 new file mode 100644 index 0000000..a54b39a --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 @@ -0,0 +1,40 @@ +$OpcodeFile = "Opcodes.txt" +$OpcodeTable = @() +$ExtendedOpcodeTable = @() + +Get-Content $OpcodeFile | %{ + $P = $_.Split(' ') + + if ($P.Length -eq 2) { + $OpcodeTable += @{ Opcode = $P[0]; Mnemonic = $P[1] } + } else { + $ExtendedOpcodeTable += @{ Opcode = $P[1]; Mnemonic = $P[2] } + } +} + +function Create-Enum { + param($EnumName, $OpcodeTable, $IsExtended) + $CS = "" + $CS += "public enum " + $EnumName + " : byte`r`n" + $CS += "{`r`n" + + $OpcodeTable | %{ + $CS += " [CilMnemonic(`"$($_.Mnemonic)`")]`r`n" + $CS += " $($_.Mnemonic.TrimEnd(".").Replace(".", "_")) = $($_.Opcode),`r`n" + } + + if ($IsExtended -eq $False) { + $CS += " Extended = 0xFE,`r`n" + } + + $CS += "}`r`n" + + return $CS +} + +$Enum1 = Create-Enum "Opcode" $OpcodeTable $False +$Enum2 = Create-Enum "ExtendedOpcode" $ExtendedOpcodeTable $True +Write-Host $Enum2 + +#$OpcodeTable | ConvertTo-Json | Write-Host +#$ExtendedOpcodeTable | ConvertTo-Json | Write-Host diff --git a/Reemit.Decompiler.Clr/Disassembler/Opcodes.txt b/Reemit.Decompiler.Clr/Disassembler/Opcodes.txt new file mode 100644 index 0000000..cd9a0f8 --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/Opcodes.txt @@ -0,0 +1,219 @@ +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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. \ No newline at end of file From e0cd34828cbc10d21c1c43aab3c2a780f26c1166 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sat, 6 Jul 2024 01:28:17 -0400 Subject: [PATCH 02/20] Opcode enum updates. --- .../Disassembler/CilMnemonicAttribute.cs | 7 +++++++ .../Disassembler/ExtendedOpcode.g.cs | Bin 0 -> 3026 bytes .../Disassembler/Generate-Opcode-Enums.ps1 | 13 ++++++------- Reemit.Decompiler.Clr/Disassembler/Opcode.g.cs | Bin 0 -> 20258 bytes 4 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 Reemit.Decompiler.Clr/Disassembler/CilMnemonicAttribute.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/Opcode.g.cs diff --git a/Reemit.Decompiler.Clr/Disassembler/CilMnemonicAttribute.cs b/Reemit.Decompiler.Clr/Disassembler/CilMnemonicAttribute.cs new file mode 100644 index 0000000..b6a7b63 --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/CilMnemonicAttribute.cs @@ -0,0 +1,7 @@ +namespace Reemit.Decompiler.Clr.Disassembler; + +[AttributeUsage(AttributeTargets.Field)] +public class CilMnemonicAttribute(string mnemonic) : Attribute +{ + public string Mnemonic { get; } = mnemonic; +} diff --git a/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs b/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs new file mode 100644 index 0000000000000000000000000000000000000000..d41aaeea034cce29975a06422893423a95025028 GIT binary patch literal 3026 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 literal 0 HcmV?d00001 From dd51d233d3d4c3401da05c01acee0d5b62a53d58 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sat, 6 Jul 2024 01:28:53 -0400 Subject: [PATCH 03/20] Removed opcode enum generation files from csproj. --- Reemit.Decompiler.Clr/Reemit.Decompiler.Clr.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Reemit.Decompiler.Clr/Reemit.Decompiler.Clr.csproj b/Reemit.Decompiler.Clr/Reemit.Decompiler.Clr.csproj index 6620c70..3578fd3 100644 --- a/Reemit.Decompiler.Clr/Reemit.Decompiler.Clr.csproj +++ b/Reemit.Decompiler.Clr/Reemit.Decompiler.Clr.csproj @@ -7,6 +7,11 @@ Reemit.Decompiler.Clr + + + + + From 212ccc52aa38c14bc35684e98316def427bd7001 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sat, 6 Jul 2024 01:32:11 -0400 Subject: [PATCH 04/20] Opcode enum updates. --- .../Disassembler/CilMnemonicAttribute.cs | 4 +++- .../Disassembler/ExtendedOpcode.g.cs | Bin 3026 -> 3362 bytes .../Disassembler/Generate-Opcode-Enums.ps1 | 2 +- .../Disassembler/Opcode.g.cs | Bin 20258 -> 22932 bytes 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Reemit.Decompiler.Clr/Disassembler/CilMnemonicAttribute.cs b/Reemit.Decompiler.Clr/Disassembler/CilMnemonicAttribute.cs index b6a7b63..53fb513 100644 --- a/Reemit.Decompiler.Clr/Disassembler/CilMnemonicAttribute.cs +++ b/Reemit.Decompiler.Clr/Disassembler/CilMnemonicAttribute.cs @@ -1,7 +1,9 @@ namespace Reemit.Decompiler.Clr.Disassembler; [AttributeUsage(AttributeTargets.Field)] -public class CilMnemonicAttribute(string mnemonic) : Attribute +public class CilMnemonicAttribute(string mnemonic, bool isExtended) : Attribute { public string Mnemonic { get; } = mnemonic; + + public bool IsExtended { get; } = isExtended; } diff --git a/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs b/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs index d41aaeea034cce29975a06422893423a95025028..421d338e5861d9d622c0c6d66f8d67bbdcde4bba 100644 GIT binary patch literal 3362 zcmbW4+e*Vg5QgVk@Ew9z3Z=B34+v^o!3z<*6_GSOSWT0frqwFGy86$iA(OVaJ0+yq z-Tt%R&SZCI^ZOH#Peba`m_{fCD0y^`-!57Qlv0(NCx7`yZ3z z_GV>eD#@s;7<~tS0{EW7hr*9tHKU4HMGoFD>B(p%Z%@SwoqS@IWbCUMu0Ano*{1^q zFT@P)p%;4u3#Ih!P|2ENrYGRJGs_l|eWYY(Yt6RC*w0KUSto0;eCxa{XntwVTq=0H zbGZ3dD5Y;#3Kowy#10lXd6p?9tFac#Eshzj9LJ`D$I)@~tx!teS_)P^*3gl-sF&Pp z1vkrJJ(G_(pC#7!xNK3d>!isEyifZk*Y#22ELyeFmu)0nlP#v0wITSa@xAN7UbK*8 zP2S+u$@$;zZLX9gP4>7!T=^Un<23SOjcr*inVJm3r=ZB3UJ~P{>ziF|Cr6>lEdkDB ze42Z;dR-_bOOs0m^C-LV%4*3xR;=3yQMS(?R!M>;Yb98bDLrFHi0R!L#j;v5b(w2G zpWwzg57_eZ7&AN9N}eX8@V7z_votbi^%>?&*UDXGBl(&P!%_3G@9~NWF$Vh^U9^zo NJl}kqlRtlV`~iy$;z9rb delta 226 zcmZ1^bxC}}lgWozgeEgF`hn>@MmI2hiO~m4+b}_B0cN(zYnUK%0?YwGIkCx2%t2uK z8<>`vT)+a7PXg<6Vg;EeHd%p60AnsM-_L=;MM{M#0ZixDS+)y)lfM)VI F005>OQC9!} diff --git a/Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 b/Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 index 2473cba..49955df 100644 --- a/Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 +++ b/Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 @@ -22,7 +22,7 @@ function Create-Enum { $CS += "{`r`n" $OpcodeTable | %{ - $CS += " [CilMnemonic(`"$($_.Mnemonic)`")]`r`n" + $CS += " [CilMnemonic(`"$($_.Mnemonic)`", $($IsExtended.ToString().ToLower()))]`r`n" $CS += " @$($_.Mnemonic.TrimEnd(".").Replace(".", "_")) = $($_.Opcode),`r`n" } diff --git a/Reemit.Decompiler.Clr/Disassembler/Opcode.g.cs b/Reemit.Decompiler.Clr/Disassembler/Opcode.g.cs index ed7eecdfa074f37b3cb297a62204acc33afb3a55..43578d6fdcf980bbf0a2c08c8b3c2d96400ee2fb 100644 GIT binary patch literal 22932 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 KEU#;Q>ii2hcOuRJ From d19d89b8b12e107c9c400441e0f9369b4ed2cd29 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sat, 6 Jul 2024 19:11:43 -0400 Subject: [PATCH 05/20] Dasm updates. --- .../Disassembler/ExtendedOpcode.g.cs | Bin 3362 -> 3346 bytes .../Disassembler/Generate-Opcode-Enums.ps1 | 46 ++++++++++-------- .../Disassembler/IDecoder.cs | 6 +++ .../Disassembler/Instruction.cs | 22 +++++++++ .../Disassembler/InstructionDecoder.cs | 37 ++++++++++++++ ...monicAttribute.cs => MnemonicAttribute.cs} | 2 +- .../Disassembler/Opcode.g.cs | Bin 22932 -> 22550 bytes .../Disassembler/OpcodeDecoder.cs | 13 +++++ .../Disassembler/OpcodeInfo.cs | 34 +++++++++++++ .../Disassembler/PrefixDetector.cs | 16 ++++++ 10 files changed, 154 insertions(+), 22 deletions(-) create mode 100644 Reemit.Decompiler.Clr/Disassembler/IDecoder.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/Instruction.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs rename Reemit.Decompiler.Clr/Disassembler/{CilMnemonicAttribute.cs => MnemonicAttribute.cs} (70%) create mode 100644 Reemit.Decompiler.Clr/Disassembler/OpcodeDecoder.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/OpcodeInfo.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/PrefixDetector.cs diff --git a/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs b/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs index 421d338e5861d9d622c0c6d66f8d67bbdcde4bba..87fed0a53cb945dce76072307b1e942b5cab7879 100644 GIT binary patch literal 3346 zcmbW4+fKqj5QgX4#CK@C(u5H3e29q(n0TQk-Wp>{IangqpRN~eWnr=%(D zZvLHbXS&nb{{4pJ(tx@&rV&~mS`Iznw~JAqVyaRTZI6cNk1!IT7h$x47XLRfHpa{` zE}qk#E6!+>Hq1Sui5c}_#epV7e}GntK4G8x9sG9a9`>2TJ%>(lw`pb+Z0XzBaxlu* z9dx15ougNSr5divf|=?b>DK55M$aqQrY)>CqCVEiE058yz($O*2`ui=Q;u{|P&>2^ zk>mPqB_%3JsH=VQ;E~;zL?sEkB?(nTDO7mEt9dqBN!u$y3snBFN)q-<614s>XW5?v zH7&rt?!ksVc9~N8by$it#omrU(-ZSEB>AWmImsXDDk}iS?D<8q8n#OU_`IRZ9UoAB$U1y-=74?#O zt)?bbtY_*Y*R;s`{FY~AA#M;+J_8ML5_z>oHLsRLeU;$zPiRghk@1nwIGQKAdY)W^ zzJ7Q(hw&-y&FXQclq7w<@aOT>(ei3ZJXYsvi1^xf4XdPmeO-$1ykdGm{txNH8o#_+ z63f&kpT0mHqSo8;evI9mY9&oyHTcV*hn*RkllcrgrE8_Gnj`7@D#Gz{kq?|B0ajpt PlEoR4D{IQn;>+P5OYO=P literal 3362 zcmbW4+e*Vg5QgVk@Ew9z3Z=B34+v^o!3z<*6_GSOSWT0frqwFGy86$iA(OVaJ0+yq z-Tt%R&SZCI^ZOH#Peba`m_{fCD0y^`-!57Qlv0(NCx7`yZ3z z_GV>eD#@s;7<~tS0{EW7hr*9tHKU4HMGoFD>B(p%Z%@SwoqS@IWbCUMu0Ano*{1^q zFT@P)p%;4u3#Ih!P|2ENrYGRJGs_l|eWYY(Yt6RC*w0KUSto0;eCxa{XntwVTq=0H zbGZ3dD5Y;#3Kowy#10lXd6p?9tFac#Eshzj9LJ`D$I)@~tx!teS_)P^*3gl-sF&Pp z1vkrJJ(G_(pC#7!xNK3d>!isEyifZk*Y#22ELyeFmu)0nlP#v0wITSa@xAN7UbK*8 zP2S+u$@$;zZLX9gP4>7!T=^Un<23SOjcr*inVJm3r=ZB3UJ~P{>ziF|Cr6>lEdkDB ze42Z;dR-_bOOs0m^C-LV%4*3xR;=3yQMS(?R!M>;Yb98bDLrFHi0R!L#j;v5b(w2G zpWwzg57_eZ7&AN9N}eX8@V7z_votbi^%>?&*UDXGBl(&P!%_3G@9~NWF$Vh^U9^zo NJl}kqlRtlV`~iy$;z9rb diff --git a/Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 b/Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 index 49955df..9aea0f4 100644 --- a/Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 +++ b/Reemit.Decompiler.Clr/Disassembler/Generate-Opcode-Enums.ps1 @@ -4,35 +4,39 @@ $OpcodeTable = @() $ExtendedOpcodeTable = @() Get-Content $OpcodeFile | %{ - $P = $_.Split(' ') + $P = $_.Split(' ') - if ($P.Length -eq 2) { - $OpcodeTable += @{ Opcode = $P[0]; Mnemonic = $P[1] } - } else { - $ExtendedOpcodeTable += @{ Opcode = $P[1]; Mnemonic = $P[2] } - } + if ($P.Length -eq 2) { + $OpcodeTable += @{ Opcode = $P[0]; Mnemonic = $P[1] } + } else { + $ExtendedOpcodeTable += @{ Opcode = $P[1]; Mnemonic = $P[2] } + } } function Create-Enum { - param($EnumName, $OpcodeTable, $IsExtended) - $CS = "" - $CS += "namespace $Namespace;`r`n" - $CS += "`r`n" - $CS += "public enum " + $EnumName + " : byte`r`n" - $CS += "{`r`n" + param($EnumName, $OpcodeTable, $IsExtended) + $CS = "" + $CS += "namespace $Namespace;`r`n" + $CS += "`r`n" + $CS += "public enum " + $EnumName + " : byte`r`n" + $CS += "{`r`n" - $OpcodeTable | %{ - $CS += " [CilMnemonic(`"$($_.Mnemonic)`", $($IsExtended.ToString().ToLower()))]`r`n" - $CS += " @$($_.Mnemonic.TrimEnd(".").Replace(".", "_")) = $($_.Opcode),`r`n" - } + if ($IsExtended) { + $CS += " None = 0x00,`r`n`r`n" + } - if ($IsExtended -eq $False) { - $CS += " Extended = 0xFE,`r`n" - } + $OpcodeTable | %{ + $CS += " [Mnemonic(`"$($_.Mnemonic)`", $($IsExtended.ToString().ToLower()))]`r`n" + $CS += " @$($_.Mnemonic.TrimEnd(".").Replace(".", "_")) = $($_.Opcode),`r`n`r`n" + } - $CS += "}`r`n" + if ($IsExtended -eq $False) { + $CS += " Extended = 0xFE,`r`n" + } - return $CS + $CS += "}`r`n" + + return $CS } Create-Enum "Opcode" $OpcodeTable $False | Out-File "Opcode.g.cs" diff --git a/Reemit.Decompiler.Clr/Disassembler/IDecoder.cs b/Reemit.Decompiler.Clr/Disassembler/IDecoder.cs new file mode 100644 index 0000000..6f80333 --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/IDecoder.cs @@ -0,0 +1,6 @@ +namespace Reemit.Decompiler.Clr.Disassembler; + +public interface IDecoder +{ + T Decode(); +} diff --git a/Reemit.Decompiler.Clr/Disassembler/Instruction.cs b/Reemit.Decompiler.Clr/Disassembler/Instruction.cs new file mode 100644 index 0000000..be50260 --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/Instruction.cs @@ -0,0 +1,22 @@ +namespace Reemit.Decompiler.Clr.Disassembler; + +public class Instruction +{ + public OpcodeInfo OpcodeInfo { get; } + + public IReadOnlyCollection Operand { get; } + + public Instruction(OpcodeInfo opcodeInfo) + : this(opcodeInfo, Array.Empty()) + { + + } + + public Instruction( + OpcodeInfo opcodeInfo, + IReadOnlyCollection operand) + { + OpcodeInfo = opcodeInfo; + Operand = operand; + } +} diff --git a/Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs b/Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs new file mode 100644 index 0000000..6a90562 --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs @@ -0,0 +1,37 @@ +using System.Collections.Immutable; + +namespace Reemit.Decompiler.Clr.Disassembler; + +public class InstructionDecoder(Stream stream) : IDecoder +{ + private readonly BinaryReader _binaryReader = new(stream); + + private readonly OpcodeDecoder _opcodeDecoder = new(stream); + + public Instruction Decode() + { + var opcode = _opcodeDecoder.Decode(); + var operand = DecodeOperand(opcode); + + return new Instruction(opcode, operand); + } + + private byte[] DecodeOperand(OpcodeInfo opcodeInfo) => + opcodeInfo.IsExtended ? + DecodeOperand(opcodeInfo.ExtendedOpcode) : + DecodeOperand(opcodeInfo.Opcode); + + private byte[] DecodeOperand(Opcode opcode) => + opcode switch + { + Opcode.call => DecodeOperand32(), + _ => Array.Empty(), + }; + + private byte[] DecodeOperand(ExtendedOpcode extendedOpcode) + { + return []; + } + + private byte[] DecodeOperand32() => _binaryReader.ReadBytes(4); +} \ No newline at end of file diff --git a/Reemit.Decompiler.Clr/Disassembler/CilMnemonicAttribute.cs b/Reemit.Decompiler.Clr/Disassembler/MnemonicAttribute.cs similarity index 70% rename from Reemit.Decompiler.Clr/Disassembler/CilMnemonicAttribute.cs rename to Reemit.Decompiler.Clr/Disassembler/MnemonicAttribute.cs index 53fb513..fb0e4ae 100644 --- a/Reemit.Decompiler.Clr/Disassembler/CilMnemonicAttribute.cs +++ b/Reemit.Decompiler.Clr/Disassembler/MnemonicAttribute.cs @@ -1,7 +1,7 @@ namespace Reemit.Decompiler.Clr.Disassembler; [AttributeUsage(AttributeTargets.Field)] -public class CilMnemonicAttribute(string mnemonic, bool isExtended) : Attribute +public class MnemonicAttribute(string mnemonic, bool isExtended) : Attribute { public string Mnemonic { get; } = mnemonic; diff --git a/Reemit.Decompiler.Clr/Disassembler/Opcode.g.cs b/Reemit.Decompiler.Clr/Disassembler/Opcode.g.cs index 43578d6fdcf980bbf0a2c08c8b3c2d96400ee2fb..365aeae0b9558c9d93112ee2759775f631d9d746 100644 GIT binary patch literal 22550 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 o{ShpzeLp4f^@c9Lws&+qyYYQ +{ + public OpcodeInfo Decode() + { + var opcode = (Opcode)stream.ReadByte(); + + return new( + opcode, + opcode == Opcode.Extended ? (ExtendedOpcode)stream.ReadByte() : ExtendedOpcode.None); + } +} \ No newline at end of file diff --git a/Reemit.Decompiler.Clr/Disassembler/OpcodeInfo.cs b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfo.cs new file mode 100644 index 0000000..ddd94b5 --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfo.cs @@ -0,0 +1,34 @@ +namespace Reemit.Decompiler.Clr.Disassembler; + +// Todo: +// User struct instead? +public class OpcodeInfo +{ + public Opcode Opcode { get; } + + public ExtendedOpcode ExtendedOpcode { get; } + + public bool IsExtended => Opcode == Opcode.Extended; + + public bool IsPrefix => PrefixDetector.IsPrefix(ExtendedOpcode); + + public OpcodeInfo(Opcode opcode) + : this(opcode, ExtendedOpcode.None) + { + } + + public OpcodeInfo(ExtendedOpcode extendedOpcode) + : this(Opcode.Extended, extendedOpcode) + { + } + + public OpcodeInfo(Opcode opcode, ExtendedOpcode extendedOpcode) + { + Opcode = opcode; + ExtendedOpcode = extendedOpcode; + } + + public static implicit operator OpcodeInfo(Opcode opcode) => new(opcode); + + public static implicit operator OpcodeInfo(ExtendedOpcode extendedOpcode) => new(extendedOpcode); +} diff --git a/Reemit.Decompiler.Clr/Disassembler/PrefixDetector.cs b/Reemit.Decompiler.Clr/Disassembler/PrefixDetector.cs new file mode 100644 index 0000000..0f4ce7e --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/PrefixDetector.cs @@ -0,0 +1,16 @@ +namespace Reemit.Decompiler.Clr.Disassembler; + +public static class PrefixDetector +{ + public static bool IsPrefix(ExtendedOpcode extendedOpcode) => + extendedOpcode switch + { + ExtendedOpcode.constrained or + ExtendedOpcode.no or + ExtendedOpcode.@readonly or + ExtendedOpcode.tail or + ExtendedOpcode.unaligned or + ExtendedOpcode.@volatile => true, + _ => false, + }; +} From 40ce22ab0e398bf2ea6677ad0f0e37bbcf9de92b Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sat, 6 Jul 2024 22:35:40 -0400 Subject: [PATCH 06/20] Dasm updates. --- .../Disassembler/InstructionDecoder.cs | 11 +- .../Disassembler/OperandSizeAttribute.cs | 22 ++++ .../Disassembler/OperandSizeTable.cs | 111 ++++++++++++++++++ .../Disassembler/OperandTypeFlags.cs | 43 +++++++ 4 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 Reemit.Decompiler.Clr/Disassembler/OperandSizeAttribute.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/OperandTypeFlags.cs diff --git a/Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs b/Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs index 6a90562..b3c0e91 100644 --- a/Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs +++ b/Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs @@ -24,7 +24,8 @@ private byte[] DecodeOperand(OpcodeInfo opcodeInfo) => private byte[] DecodeOperand(Opcode opcode) => opcode switch { - Opcode.call => DecodeOperand32(), + Opcode.beq_s => Read8(), + Opcode.call => Read32(), _ => Array.Empty(), }; @@ -33,5 +34,11 @@ private byte[] DecodeOperand(ExtendedOpcode extendedOpcode) return []; } - private byte[] DecodeOperand32() => _binaryReader.ReadBytes(4); + private byte[] Read8() => _binaryReader.ReadBytes(1); + + private byte[] Read16() => _binaryReader.ReadBytes(2); + + private byte[] Read32() => _binaryReader.ReadBytes(4); + + private byte[] DecodeOperand64() => _binaryReader.ReadBytes(8); } \ No newline at end of file diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandSizeAttribute.cs b/Reemit.Decompiler.Clr/Disassembler/OperandSizeAttribute.cs new file mode 100644 index 0000000..130c73a --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/OperandSizeAttribute.cs @@ -0,0 +1,22 @@ +namespace Reemit.Decompiler.Clr.Disassembler; + +[AttributeUsage(AttributeTargets.Field)] +public class OperandSizeAttribute : Attribute +{ + public int SizeInBits { get; } + + public int SizeInBytes => SizeInBits * 8; + + public bool IsFixedLength { get; } + + public OperandSizeAttribute(int sizeInBits) + : this(isFixedLength: true, sizeInBits) + { + } + + public OperandSizeAttribute(bool isFixedLength, int sizeInBits = 0) + { + SizeInBits = sizeInBits; + IsFixedLength = isFixedLength; + } +} diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs b/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs new file mode 100644 index 0000000..d9688e9 --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs @@ -0,0 +1,111 @@ +using System.Collections.ObjectModel; + +namespace Reemit.Decompiler.Clr.Disassembler; + +public static class OperandSizeTable +{ + public static ReadOnlyDictionary Standard { get; } = + new ReadOnlyDictionary( + new Dictionary() + { + // Partition III 3.5 + { Opcode.beq, OperandTypeFlags.Int32 }, + { Opcode.beq_s, OperandTypeFlags.Int8 }, + + // Partition III 3.6 + { Opcode.bge, OperandTypeFlags.Int32 }, + { Opcode.bge_s, OperandTypeFlags.Int8 }, + + // Partition III 3.7 + { Opcode.bge_un, OperandTypeFlags.Int32 }, + { Opcode.bge_un_s, OperandTypeFlags.Int8 }, + + // Partition III 3.8 + { Opcode.bgt, OperandTypeFlags.Int32 }, + { Opcode.bgt_s, OperandTypeFlags.Int8 }, + + // Partition III 3.9 + { Opcode.bgt_un, OperandTypeFlags.Int32 }, + { Opcode.bgt_un_s, OperandTypeFlags.Int8 }, + + // Partition III 3.10 + { Opcode.ble, OperandTypeFlags.Int32 }, + { Opcode.ble_s, OperandTypeFlags.Int8 }, + + // Partition III 3.11 + { Opcode.ble_un, OperandTypeFlags.Int32 }, + { Opcode.ble_un_s, OperandTypeFlags.Int8 }, + + // Partition III 3.12 + { Opcode.blt, OperandTypeFlags.Int32 }, + { Opcode.blt_s, OperandTypeFlags.Int8 }, + + // Partition III 3.13 + { Opcode.blt_un, OperandTypeFlags.Int32 }, + { Opcode.blt_un_s, OperandTypeFlags.Int8 }, + + // Partition III 3.14 + { Opcode.bne_un, OperandTypeFlags.Int32 }, + { Opcode.bne_un_s, OperandTypeFlags.Int8 }, + + // Partition III 3.15 + { Opcode.br, OperandTypeFlags.Int32 }, + { Opcode.br_s, OperandTypeFlags.Int8 }, + + // Partition III 3.17 + { Opcode.brfalse, OperandTypeFlags.Int32 }, + { Opcode.brfalse_s, OperandTypeFlags.Int8 }, + + // Partition III 3.18 + { Opcode.brtrue, OperandTypeFlags.Int32 }, + { Opcode.brtrue_s, OperandTypeFlags.Int8 }, + + // Partition III 3.19 + { Opcode.call, OperandTypeFlags.MetadataToken }, + + // Partition III 3.20 + { Opcode.calli, OperandTypeFlags.MetadataToken }, + + // Partition III 3.37 + { Opcode.jmp, OperandTypeFlags.MetadataToken }, + + // Partition III 3.38 + // Todo: extended + { Opcode.ldarg_s, OperandTypeFlags.UInt8 }, + + // Partition III 3.39 + // Todo: extended + { Opcode.ldarga_s, OperandTypeFlags.UInt8 }, + + // Partition III 3.40 + { Opcode.ldc_i4, OperandTypeFlags.Int32 }, + { Opcode.ldc_i8, OperandTypeFlags.Int64 }, + { Opcode.ldc_r4, OperandTypeFlags.Float32 }, + { Opcode.ldc_r8, OperandTypeFlags.Float64 }, + { Opcode.ldc_i4_s, OperandTypeFlags.Int8 }, + + // Partition III 3.43 + // Todo: extended + { Opcode.ldloc_s, OperandTypeFlags.UInt8 }, + + // Partition III 3.44 + // Todo: extended + { Opcode.ldloca_s, OperandTypeFlags.UInt8 }, + + // Partition III 3.46 + { Opcode.leave, OperandTypeFlags.Int32 }, + { Opcode.leave_s, OperandTypeFlags.Int8 }, + + // Partition III 3.61 + // Todo: extended + { Opcode.starg_s, OperandTypeFlags.UInt8 }, + + // Partition III 3.63 + // Todo: extended + { Opcode.stloc_s, OperandTypeFlags.UInt8 }, + + // Partition III 3.63 + // Todo: extended + { Opcode.@switch, OperandTypeFlags.JumpTable }, + }); +} diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandTypeFlags.cs b/Reemit.Decompiler.Clr/Disassembler/OperandTypeFlags.cs new file mode 100644 index 0000000..f3a795a --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/OperandTypeFlags.cs @@ -0,0 +1,43 @@ +namespace Reemit.Decompiler.Clr.Disassembler; + +public enum OperandTypeFlags +{ + [OperandSize(0)] + None, + + [OperandSize(8)] + Int8, + + [OperandSize(16)] + Int16, + + [OperandSize(32)] + Int32, + + [OperandSize(64)] + Int64, + + [OperandSize(8)] + UInt8, + + [OperandSize(16)] + UInt16, + + [OperandSize(32)] + UInt32, + + [OperandSize(64)] + UInt64, + + [OperandSize(32)] + Float32, + + [OperandSize(64)] + Float64, + + [OperandSize(32)] + MetadataToken, + + [OperandSize(isFixedLength: false)] + JumpTable, +} From 8015d92d51b8a856668af3f386e95d60fad67eb0 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sat, 6 Jul 2024 22:57:02 -0400 Subject: [PATCH 07/20] Dasm updates. --- ...randSizeTable.cs => OpcodeOperandTable.cs} | 75 ++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) rename Reemit.Decompiler.Clr/Disassembler/{OperandSizeTable.cs => OpcodeOperandTable.cs} (61%) diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs b/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs similarity index 61% rename from Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs rename to Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs index d9688e9..e859eff 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs @@ -2,7 +2,7 @@ namespace Reemit.Decompiler.Clr.Disassembler; -public static class OperandSizeTable +public static class OpcodeOperandTable { public static ReadOnlyDictionary Standard { get; } = new ReadOnlyDictionary( @@ -105,7 +105,78 @@ public static class OperandSizeTable { Opcode.stloc_s, OperandTypeFlags.UInt8 }, // Partition III 3.63 - // Todo: extended { Opcode.@switch, OperandTypeFlags.JumpTable }, + + // Partition III 4.1 + { Opcode.box, OperandTypeFlags.MetadataToken }, + + // Partition III 4.2 + { Opcode.callvirt, OperandTypeFlags.MetadataToken }, + + // Partition III 4.3 + { Opcode.castclass, OperandTypeFlags.MetadataToken }, + + // Partition III 4.4 + { Opcode.cpobj, OperandTypeFlags.MetadataToken }, + + // Partition III 4.6 + { Opcode.isinst, OperandTypeFlags.MetadataToken }, + + // Partition III 4.7 + { Opcode.ldelem, OperandTypeFlags.MetadataToken }, + + // Partition III 4.9 + { Opcode.ldelema, OperandTypeFlags.MetadataToken }, + + // Partition III 4.10 + { Opcode.ldfld, OperandTypeFlags.MetadataToken }, + + // Partition III 4.11 + { Opcode.ldflda, OperandTypeFlags.MetadataToken }, + + // Partition III 4.13 + { Opcode.ldobj, OperandTypeFlags.MetadataToken }, + + // Partition III 4.14 + { Opcode.ldsfld, OperandTypeFlags.MetadataToken }, + + // Partition III 4.15 + { Opcode.ldsflda, OperandTypeFlags.MetadataToken }, + + // Partition III 4.16 + { Opcode.ldstr, OperandTypeFlags.MetadataToken }, + + // Partition III 4.17 + { Opcode.ldtoken, OperandTypeFlags.MetadataToken }, + + // Partition III 4.19 + { Opcode.mkrefany, OperandTypeFlags.MetadataToken }, + + // Partition III 4.20 + { Opcode.newarr, OperandTypeFlags.MetadataToken }, + + // Partition III 4.21 + { Opcode.newobj, OperandTypeFlags.MetadataToken }, + + // Partition III 4.23 + { Opcode.refanyval, OperandTypeFlags.MetadataToken }, + + // Partition III 4.26 + { Opcode.stelem, OperandTypeFlags.MetadataToken }, + + // Partition III 4.28 + { Opcode.stfld, OperandTypeFlags.MetadataToken }, + + // Partition III 4.29 + { Opcode.stobj, OperandTypeFlags.MetadataToken }, + + // Partition III 4.30 + { Opcode.stsfld, OperandTypeFlags.MetadataToken }, + + // Partition III 4.32 + { Opcode.unbox, OperandTypeFlags.MetadataToken }, + + // Partition III 4.33 + { Opcode.unbox_any, OperandTypeFlags.MetadataToken }, }); } From aa43a351fb2cb5e8333d08c57f227a4fdc58c4b4 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sun, 7 Jul 2024 00:01:57 -0400 Subject: [PATCH 08/20] Dasm updates. --- .../Disassembler/Instruction.cs | 8 +- .../Disassembler/InstructionDecoder.cs | 39 ++-- .../Disassembler/OpcodeOperandTable.cs | 204 ++++++++++++------ Reemit.Decompiler.Clr/Disassembler/Operand.cs | 10 + .../Disassembler/OperandSizeAttribute.cs | 2 +- .../Disassembler/OperandSizeTable.cs | 16 ++ .../{OperandTypeFlags.cs => OperandType.cs} | 2 +- 7 files changed, 186 insertions(+), 95 deletions(-) create mode 100644 Reemit.Decompiler.Clr/Disassembler/Operand.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs rename Reemit.Decompiler.Clr/Disassembler/{OperandTypeFlags.cs => OperandType.cs} (94%) diff --git a/Reemit.Decompiler.Clr/Disassembler/Instruction.cs b/Reemit.Decompiler.Clr/Disassembler/Instruction.cs index be50260..80fe1fd 100644 --- a/Reemit.Decompiler.Clr/Disassembler/Instruction.cs +++ b/Reemit.Decompiler.Clr/Disassembler/Instruction.cs @@ -4,17 +4,15 @@ public class Instruction { public OpcodeInfo OpcodeInfo { get; } - public IReadOnlyCollection Operand { get; } + public Operand Operand { get; } public Instruction(OpcodeInfo opcodeInfo) - : this(opcodeInfo, Array.Empty()) + : this(opcodeInfo, Operand.None) { } - public Instruction( - OpcodeInfo opcodeInfo, - IReadOnlyCollection operand) + public Instruction(OpcodeInfo opcodeInfo, Operand operand) { OpcodeInfo = opcodeInfo; Operand = operand; diff --git a/Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs b/Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs index b3c0e91..d9fc99f 100644 --- a/Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs +++ b/Reemit.Decompiler.Clr/Disassembler/InstructionDecoder.cs @@ -16,29 +16,32 @@ public Instruction Decode() return new Instruction(opcode, operand); } - private byte[] DecodeOperand(OpcodeInfo opcodeInfo) => - opcodeInfo.IsExtended ? - DecodeOperand(opcodeInfo.ExtendedOpcode) : - DecodeOperand(opcodeInfo.Opcode); + private Operand DecodeOperand(OpcodeInfo opcodeInfo) + { + var operandType = OpcodeOperandTable.GetOperandType(opcodeInfo); + byte[] operandValue; - private byte[] DecodeOperand(Opcode opcode) => - opcode switch + if (operandType != OperandType.JumpTable) + { + var operandSize = OperandSizeTable.SizeTable[operandType]; + operandValue = _binaryReader.ReadBytes(operandSize); + } + else { - Opcode.beq_s => Read8(), - Opcode.call => Read32(), - _ => Array.Empty(), - }; + operandValue = DecodeJumpTable(); + } - private byte[] DecodeOperand(ExtendedOpcode extendedOpcode) - { - return []; + return new(operandType, operandValue); } - private byte[] Read8() => _binaryReader.ReadBytes(1); - - private byte[] Read16() => _binaryReader.ReadBytes(2); + private byte[] DecodeJumpTable() + { + var jumpCount = _binaryReader.ReadUInt32(); - private byte[] Read32() => _binaryReader.ReadBytes(4); + // Technically incorrect given jumpCount is an unsigned int32. + var jumps = _binaryReader.ReadBytes((int)(jumpCount * 4)); - private byte[] DecodeOperand64() => _binaryReader.ReadBytes(8); + // Somewhat inefficient, but doing it this way for now. + return BitConverter.GetBytes(jumpCount).Concat(jumps).ToArray(); + } } \ No newline at end of file diff --git a/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs b/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs index e859eff..2d2e7e1 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs @@ -4,179 +4,243 @@ namespace Reemit.Decompiler.Clr.Disassembler; public static class OpcodeOperandTable { - public static ReadOnlyDictionary Standard { get; } = - new ReadOnlyDictionary( - new Dictionary() + public static ReadOnlyDictionary Standard { get; } = + new ReadOnlyDictionary( + new Dictionary() { // Partition III 3.5 - { Opcode.beq, OperandTypeFlags.Int32 }, - { Opcode.beq_s, OperandTypeFlags.Int8 }, + { Opcode.beq, OperandType.Int32 }, + { Opcode.beq_s, OperandType.Int8 }, // Partition III 3.6 - { Opcode.bge, OperandTypeFlags.Int32 }, - { Opcode.bge_s, OperandTypeFlags.Int8 }, + { Opcode.bge, OperandType.Int32 }, + { Opcode.bge_s, OperandType.Int8 }, // Partition III 3.7 - { Opcode.bge_un, OperandTypeFlags.Int32 }, - { Opcode.bge_un_s, OperandTypeFlags.Int8 }, + { Opcode.bge_un, OperandType.Int32 }, + { Opcode.bge_un_s, OperandType.Int8 }, // Partition III 3.8 - { Opcode.bgt, OperandTypeFlags.Int32 }, - { Opcode.bgt_s, OperandTypeFlags.Int8 }, + { Opcode.bgt, OperandType.Int32 }, + { Opcode.bgt_s, OperandType.Int8 }, // Partition III 3.9 - { Opcode.bgt_un, OperandTypeFlags.Int32 }, - { Opcode.bgt_un_s, OperandTypeFlags.Int8 }, + { Opcode.bgt_un, OperandType.Int32 }, + { Opcode.bgt_un_s, OperandType.Int8 }, // Partition III 3.10 - { Opcode.ble, OperandTypeFlags.Int32 }, - { Opcode.ble_s, OperandTypeFlags.Int8 }, + { Opcode.ble, OperandType.Int32 }, + { Opcode.ble_s, OperandType.Int8 }, // Partition III 3.11 - { Opcode.ble_un, OperandTypeFlags.Int32 }, - { Opcode.ble_un_s, OperandTypeFlags.Int8 }, + { Opcode.ble_un, OperandType.Int32 }, + { Opcode.ble_un_s, OperandType.Int8 }, // Partition III 3.12 - { Opcode.blt, OperandTypeFlags.Int32 }, - { Opcode.blt_s, OperandTypeFlags.Int8 }, + { Opcode.blt, OperandType.Int32 }, + { Opcode.blt_s, OperandType.Int8 }, // Partition III 3.13 - { Opcode.blt_un, OperandTypeFlags.Int32 }, - { Opcode.blt_un_s, OperandTypeFlags.Int8 }, + { Opcode.blt_un, OperandType.Int32 }, + { Opcode.blt_un_s, OperandType.Int8 }, // Partition III 3.14 - { Opcode.bne_un, OperandTypeFlags.Int32 }, - { Opcode.bne_un_s, OperandTypeFlags.Int8 }, + { Opcode.bne_un, OperandType.Int32 }, + { Opcode.bne_un_s, OperandType.Int8 }, // Partition III 3.15 - { Opcode.br, OperandTypeFlags.Int32 }, - { Opcode.br_s, OperandTypeFlags.Int8 }, + { Opcode.br, OperandType.Int32 }, + { Opcode.br_s, OperandType.Int8 }, // Partition III 3.17 - { Opcode.brfalse, OperandTypeFlags.Int32 }, - { Opcode.brfalse_s, OperandTypeFlags.Int8 }, + { Opcode.brfalse, OperandType.Int32 }, + { Opcode.brfalse_s, OperandType.Int8 }, // Partition III 3.18 - { Opcode.brtrue, OperandTypeFlags.Int32 }, - { Opcode.brtrue_s, OperandTypeFlags.Int8 }, + { Opcode.brtrue, OperandType.Int32 }, + { Opcode.brtrue_s, OperandType.Int8 }, // Partition III 3.19 - { Opcode.call, OperandTypeFlags.MetadataToken }, + { Opcode.call, OperandType.MetadataToken }, // Partition III 3.20 - { Opcode.calli, OperandTypeFlags.MetadataToken }, + { Opcode.calli, OperandType.MetadataToken }, // Partition III 3.37 - { Opcode.jmp, OperandTypeFlags.MetadataToken }, + { Opcode.jmp, OperandType.MetadataToken }, // Partition III 3.38 // Todo: extended - { Opcode.ldarg_s, OperandTypeFlags.UInt8 }, + { Opcode.ldarg_s, OperandType.UInt8 }, // Partition III 3.39 // Todo: extended - { Opcode.ldarga_s, OperandTypeFlags.UInt8 }, + { Opcode.ldarga_s, OperandType.UInt8 }, // Partition III 3.40 - { Opcode.ldc_i4, OperandTypeFlags.Int32 }, - { Opcode.ldc_i8, OperandTypeFlags.Int64 }, - { Opcode.ldc_r4, OperandTypeFlags.Float32 }, - { Opcode.ldc_r8, OperandTypeFlags.Float64 }, - { Opcode.ldc_i4_s, OperandTypeFlags.Int8 }, + { Opcode.ldc_i4, OperandType.Int32 }, + { Opcode.ldc_i8, OperandType.Int64 }, + { Opcode.ldc_r4, OperandType.Float32 }, + { Opcode.ldc_r8, OperandType.Float64 }, + { Opcode.ldc_i4_s, OperandType.Int8 }, // Partition III 3.43 // Todo: extended - { Opcode.ldloc_s, OperandTypeFlags.UInt8 }, + { Opcode.ldloc_s, OperandType.UInt8 }, // Partition III 3.44 // Todo: extended - { Opcode.ldloca_s, OperandTypeFlags.UInt8 }, + { Opcode.ldloca_s, OperandType.UInt8 }, // Partition III 3.46 - { Opcode.leave, OperandTypeFlags.Int32 }, - { Opcode.leave_s, OperandTypeFlags.Int8 }, + { Opcode.leave, OperandType.Int32 }, + { Opcode.leave_s, OperandType.Int8 }, // Partition III 3.61 // Todo: extended - { Opcode.starg_s, OperandTypeFlags.UInt8 }, + { Opcode.starg_s, OperandType.UInt8 }, // Partition III 3.63 // Todo: extended - { Opcode.stloc_s, OperandTypeFlags.UInt8 }, + { Opcode.stloc_s, OperandType.UInt8 }, // Partition III 3.63 - { Opcode.@switch, OperandTypeFlags.JumpTable }, + { Opcode.@switch, OperandType.JumpTable }, // Partition III 4.1 - { Opcode.box, OperandTypeFlags.MetadataToken }, + { Opcode.box, OperandType.MetadataToken }, // Partition III 4.2 - { Opcode.callvirt, OperandTypeFlags.MetadataToken }, + { Opcode.callvirt, OperandType.MetadataToken }, // Partition III 4.3 - { Opcode.castclass, OperandTypeFlags.MetadataToken }, + { Opcode.castclass, OperandType.MetadataToken }, // Partition III 4.4 - { Opcode.cpobj, OperandTypeFlags.MetadataToken }, + { Opcode.cpobj, OperandType.MetadataToken }, // Partition III 4.6 - { Opcode.isinst, OperandTypeFlags.MetadataToken }, + { Opcode.isinst, OperandType.MetadataToken }, // Partition III 4.7 - { Opcode.ldelem, OperandTypeFlags.MetadataToken }, + { Opcode.ldelem, OperandType.MetadataToken }, // Partition III 4.9 - { Opcode.ldelema, OperandTypeFlags.MetadataToken }, + { Opcode.ldelema, OperandType.MetadataToken }, // Partition III 4.10 - { Opcode.ldfld, OperandTypeFlags.MetadataToken }, + { Opcode.ldfld, OperandType.MetadataToken }, // Partition III 4.11 - { Opcode.ldflda, OperandTypeFlags.MetadataToken }, + { Opcode.ldflda, OperandType.MetadataToken }, // Partition III 4.13 - { Opcode.ldobj, OperandTypeFlags.MetadataToken }, + { Opcode.ldobj, OperandType.MetadataToken }, // Partition III 4.14 - { Opcode.ldsfld, OperandTypeFlags.MetadataToken }, + { Opcode.ldsfld, OperandType.MetadataToken }, // Partition III 4.15 - { Opcode.ldsflda, OperandTypeFlags.MetadataToken }, + { Opcode.ldsflda, OperandType.MetadataToken }, // Partition III 4.16 - { Opcode.ldstr, OperandTypeFlags.MetadataToken }, + { Opcode.ldstr, OperandType.MetadataToken }, // Partition III 4.17 - { Opcode.ldtoken, OperandTypeFlags.MetadataToken }, + { Opcode.ldtoken, OperandType.MetadataToken }, // Partition III 4.19 - { Opcode.mkrefany, OperandTypeFlags.MetadataToken }, + { Opcode.mkrefany, OperandType.MetadataToken }, // Partition III 4.20 - { Opcode.newarr, OperandTypeFlags.MetadataToken }, + { Opcode.newarr, OperandType.MetadataToken }, // Partition III 4.21 - { Opcode.newobj, OperandTypeFlags.MetadataToken }, + { Opcode.newobj, OperandType.MetadataToken }, // Partition III 4.23 - { Opcode.refanyval, OperandTypeFlags.MetadataToken }, + { Opcode.refanyval, OperandType.MetadataToken }, // Partition III 4.26 - { Opcode.stelem, OperandTypeFlags.MetadataToken }, + { Opcode.stelem, OperandType.MetadataToken }, // Partition III 4.28 - { Opcode.stfld, OperandTypeFlags.MetadataToken }, + { Opcode.stfld, OperandType.MetadataToken }, // Partition III 4.29 - { Opcode.stobj, OperandTypeFlags.MetadataToken }, + { Opcode.stobj, OperandType.MetadataToken }, // Partition III 4.30 - { Opcode.stsfld, OperandTypeFlags.MetadataToken }, + { Opcode.stsfld, OperandType.MetadataToken }, // Partition III 4.32 - { Opcode.unbox, OperandTypeFlags.MetadataToken }, + { Opcode.unbox, OperandType.MetadataToken }, // Partition III 4.33 - { Opcode.unbox_any, OperandTypeFlags.MetadataToken }, + { Opcode.unbox_any, OperandType.MetadataToken }, }); + + public static ReadOnlyDictionary Extended { get; } = + new ReadOnlyDictionary( + new Dictionary() + { + // Partition III 2.1 + { ExtendedOpcode.constrained, OperandType.MetadataToken }, + + // Partition III 2.2 + { ExtendedOpcode.no, OperandType.UInt8 }, + + // Partition III 2.5 + { ExtendedOpcode.unaligned, OperandType.UInt8 }, + + // Partition III 3.38 + { ExtendedOpcode.ldarg, OperandType.UInt16 }, + + // Partition III 3.39 + { ExtendedOpcode.ldarga, OperandType.UInt16 }, + + // Partition III 3.41 + { ExtendedOpcode.ldftn, OperandType.MetadataToken }, + + // Partition III 3.43 + { ExtendedOpcode.ldloc, OperandType.UInt16 }, + + // Partition III 3.44 + { ExtendedOpcode.ldloca, OperandType.UInt16 }, + + // Partition III 3.61 + { ExtendedOpcode.starg, OperandType.UInt16 }, + + // Partition III 3.63 + { ExtendedOpcode.stloc, OperandType.UInt16 }, + + // Partition III 4.5 + { ExtendedOpcode.Initobj, OperandType.MetadataToken }, + + // Partition III 4.18 + { ExtendedOpcode.ldvirtftn, OperandType.MetadataToken }, + + // Partition III 4.25 + { ExtendedOpcode.@sizeof, OperandType.MetadataToken }, + }); + + public static OperandType GetOperandType(OpcodeInfo opcodeInfo) => + opcodeInfo.IsExtended ? + GetOperandType(opcodeInfo.ExtendedOpcode) : + GetOperandType(opcodeInfo.Opcode); + + public static OperandType GetOperandType(Opcode opcode) => + GetOperandType(Standard, opcode); + + public static OperandType GetOperandType(ExtendedOpcode extendedOpcode) => + GetOperandType(Extended, extendedOpcode); + + private static OperandType GetOperandType( + IReadOnlyDictionary table, + TOpcode opcode) => + // Assuming no entry means no operand for now. However, this behavior + // may need to be changed to ensure we reject invalid opcodes. + table.TryGetValue(opcode, out var value) ? + value : + OperandType.None; } diff --git a/Reemit.Decompiler.Clr/Disassembler/Operand.cs b/Reemit.Decompiler.Clr/Disassembler/Operand.cs new file mode 100644 index 0000000..1f475dc --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/Operand.cs @@ -0,0 +1,10 @@ +namespace Reemit.Decompiler.Clr.Disassembler; + +public class Operand(OperandType operandType, IReadOnlyCollection operandValue) +{ + public static readonly Operand None = new(OperandType.None, Array.Empty()); + + public OperandType OperandType { get; } = operandType; + + public IReadOnlyCollection OperandValue { get; } = operandValue; +} diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandSizeAttribute.cs b/Reemit.Decompiler.Clr/Disassembler/OperandSizeAttribute.cs index 130c73a..01823a9 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OperandSizeAttribute.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OperandSizeAttribute.cs @@ -5,7 +5,7 @@ public class OperandSizeAttribute : Attribute { public int SizeInBits { get; } - public int SizeInBytes => SizeInBits * 8; + public int SizeInBytes => SizeInBits / 8; public bool IsFixedLength { get; } diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs b/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs new file mode 100644 index 0000000..269efdd --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs @@ -0,0 +1,16 @@ +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.Reflection; + +namespace Reemit.Decompiler.Clr.Disassembler; + +public static class OperandSizeTable +{ + public static IReadOnlyDictionary SizeTable { get; } = + typeof(OperandType) + .GetFields() + .Where(x => !x.IsSpecialName) + .ToDictionary( + x => (OperandType)x.GetRawConstantValue()!, + x => x.GetCustomAttribute()!.SizeInBytes); +} diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandTypeFlags.cs b/Reemit.Decompiler.Clr/Disassembler/OperandType.cs similarity index 94% rename from Reemit.Decompiler.Clr/Disassembler/OperandTypeFlags.cs rename to Reemit.Decompiler.Clr/Disassembler/OperandType.cs index f3a795a..340c945 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OperandTypeFlags.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OperandType.cs @@ -1,6 +1,6 @@ namespace Reemit.Decompiler.Clr.Disassembler; -public enum OperandTypeFlags +public enum OperandType { [OperandSize(0)] None, From 44ec070f784c0b1b0a95d9b2d4bde3e856c75c2b Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sun, 7 Jul 2024 00:47:36 -0400 Subject: [PATCH 09/20] Dasm stuff. --- .../Disassembler/InstructionEmitter.cs | 43 +++++++++++++++++++ .../Disassembler/MnemonicTable.cs | 30 +++++++++++++ .../Disassembler/OperandSizeTable.cs | 2 + .../Disassembler/StreamDisassembler.cs | 20 +++++++++ 4 files changed, 95 insertions(+) create mode 100644 Reemit.Decompiler.Clr/Disassembler/InstructionEmitter.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/MnemonicTable.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/StreamDisassembler.cs diff --git a/Reemit.Decompiler.Clr/Disassembler/InstructionEmitter.cs b/Reemit.Decompiler.Clr/Disassembler/InstructionEmitter.cs new file mode 100644 index 0000000..b21cc68 --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/InstructionEmitter.cs @@ -0,0 +1,43 @@ +using System.Text; + +namespace Reemit.Decompiler.Clr.Disassembler; + +public class InstructionEmitter +{ + public string Emit(IReadOnlyCollection instructions) + { + var sb = new StringBuilder(); + + foreach (var inst in instructions) + { + var mnemonic = MnemonicTable.GetMnemonic(inst.OpcodeInfo); + + sb.Append(mnemonic); + + if (inst.Operand.OperandValue.Any()) + { + sb.Append(' '); + sb.Append(inst.Operand.OperandType); + sb.Append(" {"); + + var operandHex = string.Join( + " ", + inst.Operand.OperandValue.Select(x => string.Format("{0:x2}", x))); + + sb.Append(operandHex); + sb.Append('}'); + } + + if (!inst.OpcodeInfo.IsPrefix) + { + sb.Append(Environment.NewLine); + } + else + { + sb.Append(' '); + } + } + + return sb.ToString(); + } +} diff --git a/Reemit.Decompiler.Clr/Disassembler/MnemonicTable.cs b/Reemit.Decompiler.Clr/Disassembler/MnemonicTable.cs new file mode 100644 index 0000000..97c2573 --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/MnemonicTable.cs @@ -0,0 +1,30 @@ +using System.Reflection; + +namespace Reemit.Decompiler.Clr.Disassembler; + +public static class MnemonicTable +{ + public static IReadOnlyDictionary Standard { get; } = CreateTable(); + + public static IReadOnlyDictionary Extended { get; } = CreateTable(); + + public static IReadOnlyDictionary CreateTable() + where TOpcode : Enum => + typeof(TOpcode) + .GetFields() + .Where(x => !x.IsSpecialName) + .Select(x => new + { + Opcode = (TOpcode)x.GetRawConstantValue()!, + MnemonicAttr = x.GetCustomAttribute(), + }) + .Where(x => x.MnemonicAttr != null) + .ToDictionary( + x => x.Opcode, + x => x.MnemonicAttr!.Mnemonic); + + public static string GetMnemonic(OpcodeInfo opcodeInfo) => + opcodeInfo.IsExtended ? + Extended[opcodeInfo.ExtendedOpcode] : + Standard[opcodeInfo.Opcode]; +} \ No newline at end of file diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs b/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs index 269efdd..3f287f9 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OperandSizeTable.cs @@ -6,6 +6,8 @@ namespace Reemit.Decompiler.Clr.Disassembler; public static class OperandSizeTable { + // Todo: + // Get rid of duplicate enum field code public static IReadOnlyDictionary SizeTable { get; } = typeof(OperandType) .GetFields() diff --git a/Reemit.Decompiler.Clr/Disassembler/StreamDisassembler.cs b/Reemit.Decompiler.Clr/Disassembler/StreamDisassembler.cs new file mode 100644 index 0000000..0120389 --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/StreamDisassembler.cs @@ -0,0 +1,20 @@ +using System.Collections.Immutable; + +namespace Reemit.Decompiler.Clr.Disassembler; + +public class StreamDisassembler(Stream stream) +{ + private InstructionDecoder _decoder = new(stream); + + public IReadOnlyCollection Disassemble() + { + var instructions = new List(); + + while (stream.Position < stream.Length) + { + instructions.Add(_decoder.Decode()); + } + + return instructions.ToImmutableArray(); + } +} From 37a28da690b7853a39babc1b69302d9eadec1ef4 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sun, 7 Jul 2024 01:03:20 -0400 Subject: [PATCH 10/20] Fixed initobj typo. --- .../Disassembler/ExtendedOpcode.g.cs | Bin 3346 -> 3346 bytes .../Disassembler/OpcodeOperandTable.cs | 2 +- .../Disassembler/Opcodes.txt | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs b/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs index 87fed0a53cb945dce76072307b1e942b5cab7879..2ec529c4f4ea5c7c8f750abc4f90bd1307e31ba4 100644 GIT binary patch delta 24 gcmbOvHA!lN9OvXZE{@4+oNANTaB*x7;oQdz0A85~T>t<8 delta 20 bcmbOvHA!lN94DjaWO+_qM$gT@oJW}fJ9!0t diff --git a/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs b/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs index 2d2e7e1..e33ac63 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs @@ -215,7 +215,7 @@ public static class OpcodeOperandTable { ExtendedOpcode.stloc, OperandType.UInt16 }, // Partition III 4.5 - { ExtendedOpcode.Initobj, OperandType.MetadataToken }, + { ExtendedOpcode.initobj, OperandType.MetadataToken }, // Partition III 4.18 { ExtendedOpcode.ldvirtftn, OperandType.MetadataToken }, diff --git a/Reemit.Decompiler.Clr/Disassembler/Opcodes.txt b/Reemit.Decompiler.Clr/Disassembler/Opcodes.txt index cd9a0f8..90b26f5 100644 --- a/Reemit.Decompiler.Clr/Disassembler/Opcodes.txt +++ b/Reemit.Decompiler.Clr/Disassembler/Opcodes.txt @@ -208,7 +208,7 @@ 0xFE 0x12 unaligned. 0xFE 0x13 volatile. 0xFE 0x14 tail. -0xFE 0x15 Initobj +0xFE 0x15 initobj 0xFE 0x16 constrained. 0xFE 0x17 cpblk 0xFE 0x18 initblk From 6a78bc1a85c5556509d12b90b7168949f06680d3 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sun, 7 Jul 2024 16:34:30 -0400 Subject: [PATCH 11/20] Dasm updates. --- .../Disassembler/OpcodeDecoderTests.cs | 52 +++++++++++++++++ .../Disassembler/OpcodeOperandTableTests.cs | 54 ++++++++++++++++++ .../Reemit.Decompiler.Clr.UnitTests.csproj | 4 +- .../Disassembler/ExtendedOpcode.g.cs | Bin 3346 -> 3342 bytes .../Disassembler/Generate-Opcode-Enums.ps1 | 7 +-- .../Disassembler/OpcodeOperandTable.cs | 8 +-- 6 files changed, 112 insertions(+), 13 deletions(-) create mode 100644 Reemit.Decompiler.Clr.UnitTests/Disassembler/OpcodeDecoderTests.cs create mode 100644 Reemit.Decompiler.Clr.UnitTests/Disassembler/OpcodeOperandTableTests.cs diff --git a/Reemit.Decompiler.Clr.UnitTests/Disassembler/OpcodeDecoderTests.cs b/Reemit.Decompiler.Clr.UnitTests/Disassembler/OpcodeDecoderTests.cs new file mode 100644 index 0000000..5f8bc10 --- /dev/null +++ b/Reemit.Decompiler.Clr.UnitTests/Disassembler/OpcodeDecoderTests.cs @@ -0,0 +1,52 @@ +using Reemit.Decompiler.Clr.Disassembler; + +namespace Reemit.Decompiler.Clr.UnitTests.Disassembler; + +public class OpcodeDecoderTests +{ + [Theory] + [InlineData(Opcode.nop, 0x00)] + [InlineData(Opcode.add, 0x58)] + [InlineData(Opcode.conv_u, 0xe0)] + public void DecodeOpcode_Standard_OpcodeDecoded(Opcode expectedOpcode, byte opcodeByte) + { + // Arrange + + // Act + var actualOpcode = DecodeBuffer([ opcodeByte ]); + + // Assert + Assert.Equal(expectedOpcode, actualOpcode.Opcode); + Assert.False(actualOpcode.IsExtended); + Assert.Equal(ExtendedOpcode.None, actualOpcode.ExtendedOpcode); + } + + [Theory] + [InlineData(ExtendedOpcode.arglist, false, 0x00)] + [InlineData(ExtendedOpcode.ldarga, false, 0x0a)] + [InlineData(ExtendedOpcode.@readonly, true, 0x1e)] + public void DecodeOpcode_Extended_OpcodeDecoded( + ExtendedOpcode expectedExtendedOpcode, + bool expectedPrefix, + byte opcodeByte) + { + // Arrange + + // Act + var actualOpcode = DecodeBuffer([ 0xfe, opcodeByte ]); + + // Assert + Assert.Equal(Opcode.Extended, actualOpcode.Opcode); + Assert.True(actualOpcode.IsExtended); + Assert.Equal(expectedExtendedOpcode, actualOpcode.ExtendedOpcode); + Assert.Equal(expectedPrefix, actualOpcode.IsPrefix); + } + + private OpcodeInfo DecodeBuffer(byte[] buffer) + { + using var stream = new MemoryStream(buffer); + var decoder = new OpcodeDecoder(stream); + + return decoder.Decode(); + } +} diff --git a/Reemit.Decompiler.Clr.UnitTests/Disassembler/OpcodeOperandTableTests.cs b/Reemit.Decompiler.Clr.UnitTests/Disassembler/OpcodeOperandTableTests.cs new file mode 100644 index 0000000..42fa4a1 --- /dev/null +++ b/Reemit.Decompiler.Clr.UnitTests/Disassembler/OpcodeOperandTableTests.cs @@ -0,0 +1,54 @@ +using Reemit.Decompiler.Clr.Disassembler; + +namespace Reemit.Decompiler.Clr.UnitTests.Disassembler; + +public class OpcodeOperandTableTests +{ + [Fact] + public void ResolveOperandType_Standard_OperandTypeResolved() + { + // Arrange + var opcode = Opcode.call; + OpcodeInfo opcodeInfo = opcode; + + // Act + var actual1 = OpcodeOperandTable.GetOperandType(opcode); + var actual2 = OpcodeOperandTable.GetOperandType(opcodeInfo); + + // Assert + Assert.Equal(OperandType.MetadataToken, actual1); + Assert.Equal(OperandType.MetadataToken, actual2); + } + + [Fact] + public void ResolveOperandType_Extended_OperandTypeResolved() + { + // Arrange + var opcode = ExtendedOpcode.constrained; + OpcodeInfo opcodeInfo = opcode; + + // Act + var actual1 = OpcodeOperandTable.GetOperandType(opcode); + var actual2 = OpcodeOperandTable.GetOperandType(opcodeInfo); + + // Assert + Assert.Equal(OperandType.MetadataToken, actual1); + Assert.Equal(OperandType.MetadataToken, actual2); + } + + [Fact] + public void ResolveOperandType_Standard_OperandTypeNotResolved() + { + // Arrange + var opcode = Opcode.add; + OpcodeInfo opcodeInfo = opcode; + + // Act + var actual1 = OpcodeOperandTable.GetOperandType(opcode); + var actual2 = OpcodeOperandTable.GetOperandType(opcodeInfo); + + // Assert + Assert.Equal(OperandType.None, actual1); + Assert.Equal(OperandType.None, actual2); + } +} diff --git a/Reemit.Decompiler.Clr.UnitTests/Reemit.Decompiler.Clr.UnitTests.csproj b/Reemit.Decompiler.Clr.UnitTests/Reemit.Decompiler.Clr.UnitTests.csproj index edbc95a..8f402cc 100644 --- a/Reemit.Decompiler.Clr.UnitTests/Reemit.Decompiler.Clr.UnitTests.csproj +++ b/Reemit.Decompiler.Clr.UnitTests/Reemit.Decompiler.Clr.UnitTests.csproj @@ -14,8 +14,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs b/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs index 2ec529c4f4ea5c7c8f750abc4f90bd1307e31ba4..63a7adc0805f2107a11f2e25fb78fd40080ec365 100644 GIT binary patch delta 42 scmbOv)h9J!$K-v?VjI5+@l0OB$T4w;kcb Date: Sun, 7 Jul 2024 22:53:11 -0400 Subject: [PATCH 12/20] Dasm updates. --- .../Disassembler/StreamDisassemblerTests.cs | 77 +++++++++++++++++++ .../Disassembler/Instruction.cs | 1 - .../Disassembler/InstructionComparer.cs | 21 +++++ .../Disassembler/OpcodeInfoComparer.cs | 20 +++++ .../Disassembler/OperandComparer.cs | 29 +++++++ 5 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 Reemit.Decompiler.Clr.UnitTests/Disassembler/StreamDisassemblerTests.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs create mode 100644 Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs diff --git a/Reemit.Decompiler.Clr.UnitTests/Disassembler/StreamDisassemblerTests.cs b/Reemit.Decompiler.Clr.UnitTests/Disassembler/StreamDisassemblerTests.cs new file mode 100644 index 0000000..d74065e --- /dev/null +++ b/Reemit.Decompiler.Clr.UnitTests/Disassembler/StreamDisassemblerTests.cs @@ -0,0 +1,77 @@ +using Reemit.Decompiler.Clr.Disassembler; + +namespace Reemit.Decompiler.Clr.UnitTests.Disassembler; + +public class StreamDisassemblerTests +{ + [Theory] + [MemberData(nameof(GetInstructionTestCases))] + public void Disassemble_Buffer_InstructionsDisassembled( + IEnumerable expectedInstructions, + byte[] bytecode) + { + // Arrange + using var stream = new MemoryStream(bytecode); + var disassembler = new StreamDisassembler(stream); + + // Act + var actualInstructions = disassembler.Disassemble(); + + // Assert + Assert.Equal(expectedInstructions, actualInstructions, InstructionComparer.Instance); + } + + public static IEnumerable GetInstructionTestCases() => + new[] + { + CreateInstructionTestCase( + [ + new Instruction(Opcode.nop, Operand.None), + new Instruction(Opcode.nop, Operand.None), + new Instruction(Opcode.nop, Operand.None), + ], + [ + 0x00, + 0x00, + 0x00, + ]), + CreateInstructionTestCase( + [ + new Instruction( + Opcode.call, + new Operand( + OperandType.MetadataToken, + [ 0x50, 0x20, 0x30, 0x00 ])), + ], + [ + 0x28, + 0x50, + 0x20, + 0x30, + 0x00, + ]), + CreateInstructionTestCase( + [ + new Instruction( + Opcode.@switch, + new Operand( + OperandType.JumpTable, + [ + 0x02, 0x00, 0x00, 0x00, // Jump count + 0x50, 0x20, 0x30, 0x00, // Jump 1 + 0x10, 0x50, 0x90, 0x20, // Jump 2 + ])), + ], + [ + 0x45, // switch opcode + 0x02, 0x00, 0x00, 0x00, // Jump count + 0x50, 0x20, 0x30, 0x00, // Jump 1 + 0x10, 0x50, 0x90, 0x20, // Jump 2 + ]) + }; + + private static object[] CreateInstructionTestCase( + IEnumerable expectedInstructions, + byte[] bytecode) => + new object[] { expectedInstructions, bytecode }; +} diff --git a/Reemit.Decompiler.Clr/Disassembler/Instruction.cs b/Reemit.Decompiler.Clr/Disassembler/Instruction.cs index 80fe1fd..962d4d1 100644 --- a/Reemit.Decompiler.Clr/Disassembler/Instruction.cs +++ b/Reemit.Decompiler.Clr/Disassembler/Instruction.cs @@ -9,7 +9,6 @@ public class Instruction public Instruction(OpcodeInfo opcodeInfo) : this(opcodeInfo, Operand.None) { - } public Instruction(OpcodeInfo opcodeInfo, Operand operand) diff --git a/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs b/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs new file mode 100644 index 0000000..0784c61 --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Reemit.Decompiler.Clr.Disassembler; + +public class InstructionComparer : IEqualityComparer +{ + public static InstructionComparer Instance = new InstructionComparer(); + + public bool Equals(Instruction? x, Instruction? y) => + OpcodeInfoComparer.Instance.Equals(x?.OpcodeInfo, y?.OpcodeInfo) && + OperandComparer.Instance.Equals(x?.Operand, y?.Operand); + + public int GetHashCode([DisallowNull] Instruction obj) + { + var hc = new HashCode(); + hc.Add(obj.Operand); + hc.Add(obj.OpcodeInfo); + + return hc.ToHashCode(); + } +} diff --git a/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs new file mode 100644 index 0000000..38cae4f --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs @@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Reemit.Decompiler.Clr.Disassembler; + +public class OpcodeInfoComparer : IEqualityComparer +{ + public static OpcodeInfoComparer Instance = new OpcodeInfoComparer(); + + public bool Equals(OpcodeInfo? x, OpcodeInfo? y) => + x?.Opcode == y?.Opcode && x?.ExtendedOpcode == y?.ExtendedOpcode; + + public int GetHashCode([DisallowNull] OpcodeInfo obj) + { + var hc = new HashCode(); + hc.Add(obj.Opcode); + hc.Add(obj.ExtendedOpcode); + + return hc.ToHashCode(); + } +} diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs b/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs new file mode 100644 index 0000000..ca7a7cf --- /dev/null +++ b/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs @@ -0,0 +1,29 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Reemit.Decompiler.Clr.Disassembler; + +public class OperandComparer : IEqualityComparer +{ + public static OperandComparer Instance = new OperandComparer(); + + public bool Equals(Operand? x, Operand? y) => + x == null && y == null ? + true : + x == null || y == null || x.OperandType != y.OperandType ? + false : + x.OperandValue.SequenceEqual(y.OperandValue); + + public int GetHashCode([DisallowNull] Operand obj) + { + var hc = new HashCode(); + hc.Add(obj.OperandType); + hc.Add(obj.OperandValue.Count); + + foreach (var b in obj.OperandValue) + { + hc.Add(b); + } + + return hc.ToHashCode(); + } +} From 55d30dd2e57cd987d066a3c21a033bdcc68ba2fd Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Sun, 7 Jul 2024 23:54:25 -0400 Subject: [PATCH 13/20] Dasm stuff --- Reemit.Common.UnitTests/RangeMappedTests.cs | 16 ++++++++++++++++ Reemit.Common/RangeMapped.cs | 2 ++ .../Disassembler/StreamDisassemblerTests.cs | 2 +- .../Disassembler/InstructionEmitter.cs | 15 ++++++++------- .../Disassembler/StreamDisassembler.cs | 13 +++++++++---- 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Reemit.Common.UnitTests/RangeMappedTests.cs b/Reemit.Common.UnitTests/RangeMappedTests.cs index 8023e6a..a556697 100644 --- a/Reemit.Common.UnitTests/RangeMappedTests.cs +++ b/Reemit.Common.UnitTests/RangeMappedTests.cs @@ -48,6 +48,22 @@ public void Cast_Called_ConstructsWithNewValue() Assert.IsType>(actualRangeMapped); } + [Fact] + public void At_Called_ConstructsWithNewPosition() + { + // Arrange + var rangeMapped = new RangeMapped(0x52, 0x30, 0x1592); + + // Act + var actualRangeMapped = rangeMapped.At(0x4); + + // Assert + Assert.Equal(0x1592, actualRangeMapped.Value); + Assert.IsType(actualRangeMapped.Value); + Assert.IsType>(actualRangeMapped); + Assert.Equal(0x56, actualRangeMapped.Position); + } + private void AssertLengthAndPosition(IRangeMapped expected, IRangeMapped actual) { Assert.Equal(expected.Length, actual.Length); diff --git a/Reemit.Common/RangeMapped.cs b/Reemit.Common/RangeMapped.cs index 0ddf78e..41549d1 100644 --- a/Reemit.Common/RangeMapped.cs +++ b/Reemit.Common/RangeMapped.cs @@ -14,6 +14,8 @@ public readonly struct RangeMapped(int position, int length, TValue valu public RangeMapped With(TResult otherValue) => new(Position, Length, otherValue); + public RangeMapped At(int offset) => new(Position + offset, Length, Value); + public RangeMapped Cast() { var v = Value; diff --git a/Reemit.Decompiler.Clr.UnitTests/Disassembler/StreamDisassemblerTests.cs b/Reemit.Decompiler.Clr.UnitTests/Disassembler/StreamDisassemblerTests.cs index d74065e..e7e0e4c 100644 --- a/Reemit.Decompiler.Clr.UnitTests/Disassembler/StreamDisassemblerTests.cs +++ b/Reemit.Decompiler.Clr.UnitTests/Disassembler/StreamDisassemblerTests.cs @@ -15,7 +15,7 @@ public void Disassemble_Buffer_InstructionsDisassembled( var disassembler = new StreamDisassembler(stream); // Act - var actualInstructions = disassembler.Disassemble(); + var actualInstructions = disassembler.Disassemble().Select(x => x.Value); // Assert Assert.Equal(expectedInstructions, actualInstructions, InstructionComparer.Instance); diff --git a/Reemit.Decompiler.Clr/Disassembler/InstructionEmitter.cs b/Reemit.Decompiler.Clr/Disassembler/InstructionEmitter.cs index b21cc68..f91add0 100644 --- a/Reemit.Decompiler.Clr/Disassembler/InstructionEmitter.cs +++ b/Reemit.Decompiler.Clr/Disassembler/InstructionEmitter.cs @@ -1,34 +1,35 @@ -using System.Text; +using Reemit.Common; +using System.Text; namespace Reemit.Decompiler.Clr.Disassembler; public class InstructionEmitter { - public string Emit(IReadOnlyCollection instructions) + public string Emit(IReadOnlyCollection> instructions) { var sb = new StringBuilder(); foreach (var inst in instructions) { - var mnemonic = MnemonicTable.GetMnemonic(inst.OpcodeInfo); + var mnemonic = MnemonicTable.GetMnemonic(inst.Value.OpcodeInfo); sb.Append(mnemonic); - if (inst.Operand.OperandValue.Any()) + if (inst.Value.Operand.OperandValue.Any()) { sb.Append(' '); - sb.Append(inst.Operand.OperandType); + sb.Append(inst.Value.Operand.OperandType); sb.Append(" {"); var operandHex = string.Join( " ", - inst.Operand.OperandValue.Select(x => string.Format("{0:x2}", x))); + inst.Value.Operand.OperandValue.Select(x => string.Format("{0:x2}", x))); sb.Append(operandHex); sb.Append('}'); } - if (!inst.OpcodeInfo.IsPrefix) + if (!inst.Value.OpcodeInfo.IsPrefix) { sb.Append(Environment.NewLine); } diff --git a/Reemit.Decompiler.Clr/Disassembler/StreamDisassembler.cs b/Reemit.Decompiler.Clr/Disassembler/StreamDisassembler.cs index 0120389..16499f8 100644 --- a/Reemit.Decompiler.Clr/Disassembler/StreamDisassembler.cs +++ b/Reemit.Decompiler.Clr/Disassembler/StreamDisassembler.cs @@ -1,4 +1,5 @@ -using System.Collections.Immutable; +using Reemit.Common; +using System.Collections.Immutable; namespace Reemit.Decompiler.Clr.Disassembler; @@ -6,13 +7,17 @@ public class StreamDisassembler(Stream stream) { private InstructionDecoder _decoder = new(stream); - public IReadOnlyCollection Disassemble() + public IReadOnlyCollection> Disassemble() { - var instructions = new List(); + var instructions = new List>(); while (stream.Position < stream.Length) { - instructions.Add(_decoder.Decode()); + var start = stream.Position; + var instruction = _decoder.Decode(); + var end = stream.Position; + var mapped = new RangeMapped((int)start, (int)(end - start), instruction); + instructions.Add(mapped); } return instructions.ToImmutableArray(); From c8c153b9bc7f8db3a8c8a56b1885a6fbbe6f6e8b Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Mon, 8 Jul 2024 00:13:05 -0400 Subject: [PATCH 14/20] Fixed mnemonic casing. --- .../Disassembler/ExtendedOpcode.g.cs | Bin 3342 -> 3342 bytes .../Disassembler/Opcodes.txt | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs b/Reemit.Decompiler.Clr/Disassembler/ExtendedOpcode.g.cs index 63a7adc0805f2107a11f2e25fb78fd40080ec365..f1d4a04a25f5345e5131b48e92e896fe3d3ebb4b 100644 GIT binary patch delta 19 ZcmeB^>XX{Q!NXWInUlvDNZRsT1OPS71%m(p delta 19 ZcmeB^>XX{Q!NV9dnUlvDNZRsT1OPM51w#M; diff --git a/Reemit.Decompiler.Clr/Disassembler/Opcodes.txt b/Reemit.Decompiler.Clr/Disassembler/Opcodes.txt index 90b26f5..09073be 100644 --- a/Reemit.Decompiler.Clr/Disassembler/Opcodes.txt +++ b/Reemit.Decompiler.Clr/Disassembler/Opcodes.txt @@ -215,5 +215,5 @@ 0xFE 0x19 no. 0xFE 0x1A rethrow 0xFE 0x1C sizeof -0xFE 0x1D Refanytype +0xFE 0x1D refanytype 0xFE 0x1E readonly. \ No newline at end of file From e11a6d1f6999462e591cb049736817c0769201db Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Fri, 19 Jul 2024 00:56:55 -0400 Subject: [PATCH 15/20] Made RangeMapped a readonly record struct. --- Reemit.Common/RangeMapped.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Reemit.Common/RangeMapped.cs b/Reemit.Common/RangeMapped.cs index 41549d1..af2db98 100644 --- a/Reemit.Common/RangeMapped.cs +++ b/Reemit.Common/RangeMapped.cs @@ -2,19 +2,15 @@ namespace Reemit.Common; -public readonly struct RangeMapped(int position, int length, TValue value) : IRangeMapped +public readonly record struct RangeMapped(int Position, int Length, TValue Value) : IRangeMapped { - public int Position { get; } = position; - public int Length { get; } = length; - public TValue Value { get; } = value; - public int End => Position + Length; public static implicit operator TValue(RangeMapped rangeMapped) => rangeMapped.Value; public RangeMapped With(TResult otherValue) => new(Position, Length, otherValue); - public RangeMapped At(int offset) => new(Position + offset, Length, Value); + public RangeMapped At(int offset) => this with { Position = Position + offset }; public RangeMapped Cast() { From 46f1642359d19a65706d8d53c8e5b6a3148a9322 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Fri, 19 Jul 2024 00:57:13 -0400 Subject: [PATCH 16/20] Simplified OpcodeOperandTable. --- Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs b/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs index 45fea1b..6cad30c 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OpcodeOperandTable.cs @@ -234,7 +234,5 @@ private static OperandType GetOperandType( TOpcode opcode) => // Assuming no entry means no operand for now. However, this behavior // may need to be changed to ensure we reject invalid opcodes. - table.TryGetValue(opcode, out var value) ? - value : - OperandType.None; + table.GetValueOrDefault(opcode); } From 472274d054fb8112a3bfd1c8982de8e290cc0381 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Fri, 19 Jul 2024 01:01:00 -0400 Subject: [PATCH 17/20] Removed DisallowNullAttribute. --- Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs | 2 +- Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs | 2 +- Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs b/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs index 0784c61..cf788ce 100644 --- a/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs +++ b/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs @@ -10,7 +10,7 @@ public bool Equals(Instruction? x, Instruction? y) => OpcodeInfoComparer.Instance.Equals(x?.OpcodeInfo, y?.OpcodeInfo) && OperandComparer.Instance.Equals(x?.Operand, y?.Operand); - public int GetHashCode([DisallowNull] Instruction obj) + public int GetHashCode(Instruction obj) { var hc = new HashCode(); hc.Add(obj.Operand); diff --git a/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs index 38cae4f..0557f28 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs @@ -9,7 +9,7 @@ public class OpcodeInfoComparer : IEqualityComparer public bool Equals(OpcodeInfo? x, OpcodeInfo? y) => x?.Opcode == y?.Opcode && x?.ExtendedOpcode == y?.ExtendedOpcode; - public int GetHashCode([DisallowNull] OpcodeInfo obj) + public int GetHashCode(OpcodeInfo obj) { var hc = new HashCode(); hc.Add(obj.Opcode); diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs b/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs index ca7a7cf..1b8e3e2 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs @@ -13,7 +13,7 @@ public bool Equals(Operand? x, Operand? y) => false : x.OperandValue.SequenceEqual(y.OperandValue); - public int GetHashCode([DisallowNull] Operand obj) + public int GetHashCode(Operand obj) { var hc = new HashCode(); hc.Add(obj.OperandType); From e9172fa0d138bfdc3a7997ebedc29cd63098d014 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Fri, 19 Jul 2024 01:03:38 -0400 Subject: [PATCH 18/20] Made OPerand into record. --- Reemit.Decompiler.Clr/Disassembler/Operand.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Reemit.Decompiler.Clr/Disassembler/Operand.cs b/Reemit.Decompiler.Clr/Disassembler/Operand.cs index 1f475dc..6cdffbf 100644 --- a/Reemit.Decompiler.Clr/Disassembler/Operand.cs +++ b/Reemit.Decompiler.Clr/Disassembler/Operand.cs @@ -1,10 +1,6 @@ namespace Reemit.Decompiler.Clr.Disassembler; -public class Operand(OperandType operandType, IReadOnlyCollection operandValue) +public record Operand(OperandType OperandType, IReadOnlyCollection OperandValue) { public static readonly Operand None = new(OperandType.None, Array.Empty()); - - public OperandType OperandType { get; } = operandType; - - public IReadOnlyCollection OperandValue { get; } = operandValue; } From eed2589f2aaff0f5a8cb31c6e7092478cc47aa5a Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Fri, 19 Jul 2024 01:05:17 -0400 Subject: [PATCH 19/20] Made static instance readonly on all comparers. --- Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs | 2 +- Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs | 2 +- Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs b/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs index cf788ce..a27f04c 100644 --- a/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs +++ b/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs @@ -4,7 +4,7 @@ namespace Reemit.Decompiler.Clr.Disassembler; public class InstructionComparer : IEqualityComparer { - public static InstructionComparer Instance = new InstructionComparer(); + public static readonly InstructionComparer Instance = new InstructionComparer(); public bool Equals(Instruction? x, Instruction? y) => OpcodeInfoComparer.Instance.Equals(x?.OpcodeInfo, y?.OpcodeInfo) && diff --git a/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs index 0557f28..88604c5 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs @@ -4,7 +4,7 @@ namespace Reemit.Decompiler.Clr.Disassembler; public class OpcodeInfoComparer : IEqualityComparer { - public static OpcodeInfoComparer Instance = new OpcodeInfoComparer(); + public static readonly OpcodeInfoComparer Instance = new OpcodeInfoComparer(); public bool Equals(OpcodeInfo? x, OpcodeInfo? y) => x?.Opcode == y?.Opcode && x?.ExtendedOpcode == y?.ExtendedOpcode; diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs b/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs index 1b8e3e2..005ce6e 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs @@ -4,7 +4,7 @@ namespace Reemit.Decompiler.Clr.Disassembler; public class OperandComparer : IEqualityComparer { - public static OperandComparer Instance = new OperandComparer(); + public static readonly OperandComparer Instance = new OperandComparer(); public bool Equals(Operand? x, Operand? y) => x == null && y == null ? From 68ac063fd34da607ccdbb1099bc839c157190b65 Mon Sep 17 00:00:00 2001 From: John Douglas Leitch Date: Fri, 19 Jul 2024 01:17:25 -0400 Subject: [PATCH 20/20] Made dasm types into readonly structs. --- Reemit.Decompiler.Clr/Disassembler/Instruction.cs | 2 +- .../Disassembler/InstructionComparer.cs | 6 +++--- Reemit.Decompiler.Clr/Disassembler/OpcodeInfo.cs | 4 +--- Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs | 4 ++-- Reemit.Decompiler.Clr/Disassembler/Operand.cs | 2 +- Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs | 9 +++------ 6 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Reemit.Decompiler.Clr/Disassembler/Instruction.cs b/Reemit.Decompiler.Clr/Disassembler/Instruction.cs index 962d4d1..cb5cd99 100644 --- a/Reemit.Decompiler.Clr/Disassembler/Instruction.cs +++ b/Reemit.Decompiler.Clr/Disassembler/Instruction.cs @@ -1,6 +1,6 @@ namespace Reemit.Decompiler.Clr.Disassembler; -public class Instruction +public readonly struct Instruction { public OpcodeInfo OpcodeInfo { get; } diff --git a/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs b/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs index a27f04c..9574f7d 100644 --- a/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs +++ b/Reemit.Decompiler.Clr/Disassembler/InstructionComparer.cs @@ -6,9 +6,9 @@ public class InstructionComparer : IEqualityComparer { public static readonly InstructionComparer Instance = new InstructionComparer(); - public bool Equals(Instruction? x, Instruction? y) => - OpcodeInfoComparer.Instance.Equals(x?.OpcodeInfo, y?.OpcodeInfo) && - OperandComparer.Instance.Equals(x?.Operand, y?.Operand); + public bool Equals(Instruction x, Instruction y) => + OpcodeInfoComparer.Instance.Equals(x.OpcodeInfo, y.OpcodeInfo) && + OperandComparer.Instance.Equals(x.Operand, y.Operand); public int GetHashCode(Instruction obj) { diff --git a/Reemit.Decompiler.Clr/Disassembler/OpcodeInfo.cs b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfo.cs index ddd94b5..cd19a99 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OpcodeInfo.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfo.cs @@ -1,8 +1,6 @@ namespace Reemit.Decompiler.Clr.Disassembler; -// Todo: -// User struct instead? -public class OpcodeInfo +public readonly struct OpcodeInfo { public Opcode Opcode { get; } diff --git a/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs index 88604c5..cf0f553 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OpcodeInfoComparer.cs @@ -6,8 +6,8 @@ public class OpcodeInfoComparer : IEqualityComparer { public static readonly OpcodeInfoComparer Instance = new OpcodeInfoComparer(); - public bool Equals(OpcodeInfo? x, OpcodeInfo? y) => - x?.Opcode == y?.Opcode && x?.ExtendedOpcode == y?.ExtendedOpcode; + public bool Equals(OpcodeInfo x, OpcodeInfo y) => + x.Opcode == y.Opcode && x.ExtendedOpcode == y.ExtendedOpcode; public int GetHashCode(OpcodeInfo obj) { diff --git a/Reemit.Decompiler.Clr/Disassembler/Operand.cs b/Reemit.Decompiler.Clr/Disassembler/Operand.cs index 6cdffbf..4a14519 100644 --- a/Reemit.Decompiler.Clr/Disassembler/Operand.cs +++ b/Reemit.Decompiler.Clr/Disassembler/Operand.cs @@ -1,6 +1,6 @@ namespace Reemit.Decompiler.Clr.Disassembler; -public record Operand(OperandType OperandType, IReadOnlyCollection OperandValue) +public readonly record struct Operand(OperandType OperandType, IReadOnlyCollection OperandValue) { public static readonly Operand None = new(OperandType.None, Array.Empty()); } diff --git a/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs b/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs index 005ce6e..c3e00bb 100644 --- a/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs +++ b/Reemit.Decompiler.Clr/Disassembler/OperandComparer.cs @@ -6,12 +6,9 @@ public class OperandComparer : IEqualityComparer { public static readonly OperandComparer Instance = new OperandComparer(); - public bool Equals(Operand? x, Operand? y) => - x == null && y == null ? - true : - x == null || y == null || x.OperandType != y.OperandType ? - false : - x.OperandValue.SequenceEqual(y.OperandValue); + public bool Equals(Operand x, Operand y) => + x.OperandType == y.OperandType && + x.OperandValue.SequenceEqual(y.OperandValue); public int GetHashCode(Operand obj) {