From 0f112d068594ea46049cb4149de6cc71e6291087 Mon Sep 17 00:00:00 2001 From: louis-michel Date: Fri, 10 Jun 2022 13:29:42 -0400 Subject: [PATCH] BCJ executable filter (only for decoding), used by XZ. --- .../Compressors/Filters/BranchExecFilter.cs | 297 ++++++++++++++++++ .../Compressors/Xz/Filters/ArmFilter.cs | 68 ++++ .../Compressors/Xz/Filters/ArmThumbFilter.cs | 69 ++++ .../Compressors/Xz/Filters/BlockFilter.cs | 8 +- .../Compressors/Xz/Filters/IA64Filter.cs | 67 ++++ .../Compressors/Xz/Filters/PowerPCFilter.cs | 66 ++++ .../Compressors/Xz/Filters/SparcFilter.cs | 65 ++++ .../Compressors/Xz/Filters/X86Filter.cs | 70 +++++ src/SharpCompress/Utility.cs | 41 +++ .../Filters/BranchExecTests.cs | 157 +++++++++ .../SharpCompress.Test/Xz/Filters/BCJTests.cs | 88 ++++++ 11 files changed, 995 insertions(+), 1 deletion(-) create mode 100644 src/SharpCompress/Compressors/Filters/BranchExecFilter.cs create mode 100644 src/SharpCompress/Compressors/Xz/Filters/ArmFilter.cs create mode 100644 src/SharpCompress/Compressors/Xz/Filters/ArmThumbFilter.cs create mode 100644 src/SharpCompress/Compressors/Xz/Filters/IA64Filter.cs create mode 100644 src/SharpCompress/Compressors/Xz/Filters/PowerPCFilter.cs create mode 100644 src/SharpCompress/Compressors/Xz/Filters/SparcFilter.cs create mode 100644 src/SharpCompress/Compressors/Xz/Filters/X86Filter.cs create mode 100644 tests/SharpCompress.Test/Filters/BranchExecTests.cs create mode 100644 tests/SharpCompress.Test/Xz/Filters/BCJTests.cs diff --git a/src/SharpCompress/Compressors/Filters/BranchExecFilter.cs b/src/SharpCompress/Compressors/Filters/BranchExecFilter.cs new file mode 100644 index 00000000..02f92ac5 --- /dev/null +++ b/src/SharpCompress/Compressors/Filters/BranchExecFilter.cs @@ -0,0 +1,297 @@ +/* + * BranchExecFilter.cs -- Converters for executable + * + * @TODO Encoding + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharpCompress.Compressors.Filters +{ + [CLSCompliant(false)] + public sealed class BranchExecFilter + { + public enum Alignment : int + { + ARCH_x86_ALIGNMENT = 1, + ARCH_PowerPC_ALIGNMENT = 4, + ARCH_IA64_ALIGNMENT = 16, + ARCH_ARM_ALIGNMENT = 4, + ARCH_ARMTHUMB_ALIGNMENT = 2, + ARCH_SPARC_ALIGNMENT = 4, + } + + public static void X86Converter(byte[] data, UInt32 ip, ref UInt32 state) { + + long i = 0; + long size = data.Length; + UInt32 pos = 0; + UInt32 mask = state & 7; + if (size < 5) + return; + size -= 4; + ip += 5; + + for (;;) + { + i = pos; + + for (; i < size; i++) + { + if ((data[i] & 0xFE) == 0xE8) + { + break; + } + } + + UInt32 d = (UInt32)(i) - pos; + pos = (UInt32)i; + if (i >= size) + { + state = (d > 2 ? 0 : mask >> (int)d); + return; + } + if (d > 2) + { + mask = 0; + } + else + { + mask >>= (int)d; + if (mask != 0 && (mask > 4 || mask == 3 || (((((data[(UInt32)(mask >> 1) + 1])) + 1) & 0xFE) == 0) )) + { + mask = (mask >> 1) | 4; + pos++; + continue; + } + } + + if ((((data[i + 4]) + 1) & 0xFE) == 0) + { + UInt32 inst = ((UInt32)data[i + 4] << 24) | ((UInt32)data[i + 3] << 16) | ((UInt32)data[i + 2] << 8) | ((UInt32)data[i + 1]); + UInt32 cur = ip + (UInt32)pos; + pos += 5; + + inst -= cur; + if (mask != 0) + { + UInt32 sh = (mask & 6) << 2; + if (((((((Byte)(inst >> (int)sh))) + 1) & 0xFE) == 0)) + { + inst ^= (((UInt32)0x100 << (int)sh) - 1); + inst -= cur; + } + mask = 0; + } + data[i + 1] = (Byte)inst; + data[i + 2] = (Byte)(inst >> 8); + data[i + 3] = (Byte)(inst >> 16); + data[i + 4] = (Byte)(0 - ((inst >> 24) & 1)); + } + else + { + mask = (mask >> 1) | 4; + pos++; + } + } + } + + public static void PowerPCConverter(byte[] data, UInt32 ip) + { + long i = 0; + long size = data.Length; + size &= ~(UInt32)3; + ip -= 4; + + for (;; ) // infinite loop + { + for (;; ) // infinite loop + { + if (i >= size) + return; + i += 4; + + if ((data[i - 4] & 0xFC) == 0x48 && (data[i - 1] & 3) == 1) + break; + } + { + UInt32 inst = BitConverter.ToUInt32(data, (int)i - 4); + + if (BitConverter.IsLittleEndian) + { + inst = Utility.SwapUINT32(inst); + } + + inst -= (UInt32)(ip + i); + inst &= 0x03FFFFFF; + inst |= 0x48000000; + + Utility.SetBigUInt32(ref data, inst, (i - 4)); + } + } + } + + public static void ARMConverter(byte[] data, UInt32 ip) + { + long i = 0; + long size = data.Length; + size &= ~(UInt32)3; + ip += 4; + + for (;;) // infinite loop + { + for (;;) // infinite loop + { + if (i >= size) + { + return; + } + + i += 4; + if (data[i - 1] == 0xEB) + break; + } + + UInt32 inst = BitConverter.ToUInt32(data, (int)i - 4); + inst <<= 2; + inst -= (UInt32)(ip + i); + inst >>= 2; + inst &= 0x00FFFFFF; + inst |= 0xEB000000; + + Utility.SetLittleUInt32(ref data, inst, i - 4); + } + } + + public static void ARMTConverter(byte[] data, UInt32 ip) + { + long i = 0; + long size = data.Length; + size &= ~(UInt32)1; + long lim = size - 4; + + for (;;) + { + UInt32 b1; + for (;;) + { + UInt32 b3; + if (i > lim) + return; + b1 = data[i + 1]; + b3 = data[i + 3]; + i += 2; + b1 ^= 8; + if ((b3 & b1) >= 0xF8) + break; + } + + UInt32 inst = ((UInt32)b1 << 19) + + (((UInt32)data[i + 1] & 0x7) << 8) + + (((UInt32)data[i - 2] << 11)) + + (data[i]); + + i += 2; + + UInt32 cur = ((UInt32)(ip + i)) >> 1; + inst -= cur; + + + data[i - 4] = (Byte)(inst >> 11); + data[i - 3] = (Byte)(0xF0 | ((inst >> 19) & 0x7)); + data[i - 2] = (Byte)inst; + data[i - 1] = (Byte)(0xF8 | (inst >> 8)); + } + } + + public static void IA64Converter(byte[] data, UInt32 ip) + { + UInt32 i = 0; + long size = data.Length; + if (size < 16) + throw new InvalidDataException("Unexpected data size"); + size -= 16; + + do + { + UInt32 m = ((UInt32)0x334B0000 >> (data[i] & 0x1E)) & 3; + if (m != 0) + { + m++; + do + { + UInt32 iterator = (UInt32)( (i + (m * 5) - 8)); + if (((data[iterator + 3] >> (int)m) & 15) == 5 + && (((data[iterator - 1] | ((UInt32)data[iterator] << 8)) >> (int)m) & 0x70) == 0) + { + UInt32 raw = BitConverter.ToUInt32(data, (int)iterator); + UInt32 inst = raw >> (int)m; + inst = (inst & 0xFFFFF) | ((inst & (1 << 23)) >> 3); + + inst <<= 4; + inst -= (ip + (UInt32)i); + inst >>= 4; + + inst &= 0x1FFFFF; + inst += 0x700000; + inst &= 0x8FFFFF; + raw &= ~((UInt32)0x8FFFFF << (int)m); + raw |= (inst << (int)m); + + Utility.SetLittleUInt32(ref data, raw, iterator); + } + } + while (++m <= 4); + } + i += 16; + } + while (i <= size); + return; + } + + public static void SPARCConverter(byte[] data, UInt32 ip) + { + long i = 0; + long size = data.Length; + size &= ~(UInt32)3; + ip -= 4; + + for (;;) // infinite loop + { + for (;;) // infinite loop + { + if (i >= size) + return; + + i += 4; + if ((data[i - 4] == 0x40 && (data[i - 3] & 0xC0) == 0) || + (data[i - 4] == 0x7F && (data[i - 3] >= 0xC0))) + break; + } + + UInt32 inst = BitConverter.ToUInt32(data, (int)i - 4); + + if (BitConverter.IsLittleEndian) + { + inst = Utility.SwapUINT32(inst); + } + + inst <<= 2; + inst -= (UInt32)(ip + i); + + inst &= 0x01FFFFFF; + inst -= (UInt32)1 << 24; + inst ^= 0xFF000000; + inst >>= 2; + inst |= 0x40000000; + + Utility.SetBigUInt32(ref data, inst, (i - 4)); + } + } + } +} diff --git a/src/SharpCompress/Compressors/Xz/Filters/ArmFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/ArmFilter.cs new file mode 100644 index 00000000..9f8b5ad7 --- /dev/null +++ b/src/SharpCompress/Compressors/Xz/Filters/ArmFilter.cs @@ -0,0 +1,68 @@ +/* + * ArmFilter.cs -- XZ converter ARM executable + * + * @TODO Properties offset + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SharpCompress.Compressors.Filters; + +namespace SharpCompress.Compressors.Xz.Filters +{ + internal class ArmFilter : BlockFilter + { + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private UInt32 _ip = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) + { + if (properties.Length != 0 && properties.Length != 4) + { + throw new InvalidDataException("ARM properties unexpected length"); + } + + if (properties.Length == 4) + { + // Even XZ doesn't support it. + throw new InvalidDataException("ARM properties offset is not supported"); + + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_ARM_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} + } + } + + public override void ValidateFilter() + { + + } + + public override int Read(byte[] buffer, int offset, int count) + { + int bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.ARMConverter(buffer, _ip); + _ip += (UInt32)bytesRead; + return bytesRead; + } + + public override void SetBaseStream(Stream stream) + { + BaseStream = stream; + } + } +} diff --git a/src/SharpCompress/Compressors/Xz/Filters/ArmThumbFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/ArmThumbFilter.cs new file mode 100644 index 00000000..db79e5e9 --- /dev/null +++ b/src/SharpCompress/Compressors/Xz/Filters/ArmThumbFilter.cs @@ -0,0 +1,69 @@ +/* + * ArmFThumbFilter.cs -- XZ converter ARMThumb executable + * + * @TODO Properties offset + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SharpCompress.Compressors.Filters; + +namespace SharpCompress.Compressors.Xz.Filters +{ + internal class ArmThumbFilter : BlockFilter + { + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private UInt32 _ip = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) + { + + if (properties.Length != 0 && properties.Length != 4) + { + throw new InvalidDataException("ARM Thumb properties unexpected length"); + } + + if (properties.Length == 4) + { + // Even XZ doesn't support it. + throw new InvalidDataException("ARM Thumb properties offset is not supported"); + + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_ARMTHUMB_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} + } + } + + public override void ValidateFilter() + { + + } + + public override int Read(byte[] buffer, int offset, int count) + { + int bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.ARMTConverter(buffer, _ip); + _ip += (UInt32)bytesRead; + return bytesRead; + } + + public override void SetBaseStream(Stream stream) + { + BaseStream = stream; + } + } +} diff --git a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs index 875d1b44..2f7e2769 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; @@ -20,6 +20,12 @@ namespace SharpCompress.Compressors.Xz.Filters private static readonly Dictionary FilterMap = new Dictionary { + {FilterTypes.ARCH_x86_FILTER, typeof(X86Filter) }, + {FilterTypes.ARCH_PowerPC_FILTER, typeof(PowerPCFilter) }, + {FilterTypes.ARCH_IA64_FILTER, typeof(IA64Filter) }, + {FilterTypes.ARCH_ARM_FILTER, typeof(ArmFilter) }, + {FilterTypes.ARCH_ARMTHUMB_FILTER, typeof(ArmThumbFilter) }, + {FilterTypes.ARCH_SPARC_FILTER, typeof(SparcFilter) }, {FilterTypes.LZMA2, typeof(Lzma2Filter) } }; diff --git a/src/SharpCompress/Compressors/Xz/Filters/IA64Filter.cs b/src/SharpCompress/Compressors/Xz/Filters/IA64Filter.cs new file mode 100644 index 00000000..a4fb9f1f --- /dev/null +++ b/src/SharpCompress/Compressors/Xz/Filters/IA64Filter.cs @@ -0,0 +1,67 @@ +/* + * IA64Filter.cs -- XZ converter IA64 executable + * + * @TODO Properties offset + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SharpCompress.Compressors.Filters; + +namespace SharpCompress.Compressors.Xz.Filters +{ + internal class IA64Filter : BlockFilter + { + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private UInt32 _ip = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) + { + if (properties.Length != 0 && properties.Length != 4) + { + throw new InvalidDataException("IA64 properties unexpected length"); + } + + if (properties.Length == 4) + { + // Even XZ doesn't support it. + throw new InvalidDataException("IA64 properties offset is not supported"); + + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_IA64_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} + } + } + + public override void ValidateFilter() + { + + } + + public override int Read(byte[] buffer, int offset, int count) + { + int bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.IA64Converter(buffer, _ip); + _ip += (UInt32)bytesRead; + return bytesRead; + } + public override void SetBaseStream(Stream stream) + { + BaseStream = stream; + } + } +} diff --git a/src/SharpCompress/Compressors/Xz/Filters/PowerPCFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/PowerPCFilter.cs new file mode 100644 index 00000000..9c072521 --- /dev/null +++ b/src/SharpCompress/Compressors/Xz/Filters/PowerPCFilter.cs @@ -0,0 +1,66 @@ +/* + * PowerPCFilter.cs -- XZ converter PowerPC executable + * + * @TODO Properties offset + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SharpCompress.Compressors.Filters; + +namespace SharpCompress.Compressors.Xz.Filters +{ + internal class PowerPCFilter : BlockFilter + { + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private UInt32 _ip = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) + { + + if (properties.Length != 0 && properties.Length != 4) + { + throw new InvalidDataException("PPC properties unexpected length"); + } + + if (properties.Length == 4) + { + // Even XZ doesn't support it. + throw new InvalidDataException("PPC properties offset is not supported"); + + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_PowerPC_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} + } + } + + public override void ValidateFilter() { } + + public override int Read(byte[] buffer, int offset, int count) + { + int bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.PowerPCConverter(buffer, _ip); + _ip += (UInt32)bytesRead; + return bytesRead; + } + + public override void SetBaseStream(Stream stream) + { + BaseStream = stream; + } + } +} diff --git a/src/SharpCompress/Compressors/Xz/Filters/SparcFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/SparcFilter.cs new file mode 100644 index 00000000..eb031e97 --- /dev/null +++ b/src/SharpCompress/Compressors/Xz/Filters/SparcFilter.cs @@ -0,0 +1,65 @@ +/* + * SparcFilter.cs -- XZ converter SPARC executable + * + * @TODO Properties offset + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SharpCompress.Compressors.Filters; + +namespace SharpCompress.Compressors.Xz.Filters +{ + internal class SparcFilter : BlockFilter + { + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private UInt32 _ip = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) { + + if (properties.Length != 0 && properties.Length != 4) + { + throw new InvalidDataException("SPARC properties unexpected length"); + } + + if (properties.Length == 4) + { + // Even XZ doesn't support it. + throw new InvalidDataException("SPARC properties offset is not supported"); + + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_SPARC_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} + } + } + + public override void ValidateFilter() { + + } + + public override int Read(byte[] buffer, int offset, int count){ + int bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.SPARCConverter(buffer, _ip); + _ip += (UInt32)bytesRead; + return bytesRead; + } + + public override void SetBaseStream(Stream stream) { + BaseStream = stream; + } + } +} diff --git a/src/SharpCompress/Compressors/Xz/Filters/X86Filter.cs b/src/SharpCompress/Compressors/Xz/Filters/X86Filter.cs new file mode 100644 index 00000000..43115e42 --- /dev/null +++ b/src/SharpCompress/Compressors/Xz/Filters/X86Filter.cs @@ -0,0 +1,70 @@ +/* + * X86Filter.cs -- XZ converter x86 executable + * + * @TODO Properties offset + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SharpCompress.Compressors.Filters; + +namespace SharpCompress.Compressors.Xz.Filters +{ + internal class X86Filter : BlockFilter + { + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private UInt32 _ip = 0; + + private UInt32 _state = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) + { + if (properties.Length != 0 && properties.Length != 4) + { + throw new InvalidDataException("X86 properties unexpected length"); + } + + if (properties.Length == 4) + { + // Even XZ doesn't support it. + throw new InvalidDataException("X86 properties offset is not supported"); + + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_x86_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} + } + } + + public override void ValidateFilter() + { + + } + + public override int Read(byte[] buffer, int offset, int count) + { + int bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.X86Converter(buffer, _ip, ref _state); + _ip += (UInt32)bytesRead; + return bytesRead; + } + + public override void SetBaseStream(Stream stream) + { + BaseStream = stream; + } + } +} diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index cbc5b9ae..ee732e8d 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -315,5 +315,46 @@ namespace SharpCompress { return source.Replace('\0', ' ').Trim(); } + + /// + /// Swap the endianness of a UINT32 + /// + /// The UINT32 you want to swap his endianness + /// Return the new UINT32 in the other endianness format + public static UInt32 SwapUINT32(UInt32 number) + { + return (number >> 24) | + ((number << 8) & 0x00FF0000) | + ((number >> 8) & 0x0000FF00) | + (number << 24); + } + + /// + /// Insert a little endian UINT32 into a byte array + /// + /// The buffer to insert into + /// The UINT32 to insert + /// Offset of the buffer to insert into + public static void SetLittleUInt32(ref byte[] buffer, UInt32 number, long offset) + { + buffer[offset] = (byte)(number); + buffer[offset + 1] = (byte)(number >> 8); + buffer[offset + 2] = (byte)(number >> 16); + buffer[offset + 3] = (byte)(number >> 24); + } + + /// + /// Insert a big endian UINT32 into a byte array + /// + /// The buffer to insert into + /// The UINT32 to insert + /// Offset of the buffer to insert into + public static void SetBigUInt32(ref byte[] buffer, UInt32 number, long offset) + { + buffer[offset] = (byte)(number >> 24); + buffer[offset + 1] = (byte)(number >> 16); + buffer[offset + 2] = (byte)(number >> 8); + buffer[offset + 3] = (byte)number; + } } } diff --git a/tests/SharpCompress.Test/Filters/BranchExecTests.cs b/tests/SharpCompress.Test/Filters/BranchExecTests.cs new file mode 100644 index 00000000..41dea9fd --- /dev/null +++ b/tests/SharpCompress.Test/Filters/BranchExecTests.cs @@ -0,0 +1,157 @@ +/* + * BranchExecTests.cs -- Test for converters + * + * + * All data is extracted from busybox, where "ip" is the offset within the busybox file. + */ + +using SharpCompress.Compressors.Filters; +using Xunit; + +namespace SharpCompress.Test.Filters +{ + public class BranchExecTests + { + private static byte[] x86resultData { get; } = new byte[] { + 0x12, 0x00, 0x00, 0x00, 0x02, 0x0B, 0x00, 0x00, 0xE8, 0xBD, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x6D, 0x01, 0x00, 0x00, 0xF0, 0xCA, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0xBC, 0x09, 0x00, 0x00, 0x14, 0xC2, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x98, 0x0B, 0x00, 0x00, 0x60, 0x75, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xF1, 0xFF, 0x42, 0x01, 0x00, 0x00, 0x08, 0xC8, 0x00, 0x00, 0x1C, 0x00, + }; + + private static byte[] x86Data { get; } = new byte[] { + 0x12, 0x00, 0x00, 0x00, 0x02, 0x0B, 0x00, 0x00, 0xE8, 0xCA, 0x20, 0x00, 0x00, 0x07, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x6D, 0x01, 0x00, 0x00, 0xF0, 0xCA, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0xBC, 0x09, 0x00, 0x00, 0x14, 0xC2, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x98, 0x0B, 0x00, 0x00, 0x60, 0x75, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xF1, 0xFF, 0x42, 0x01, 0x00, 0x00, 0x08, 0xC8, 0x00, 0x00, 0x1C, 0x00, + }; + + private static byte[] ppcResultData { get; } = new byte[] { + 0xF8, 0x6B, 0x2E, 0x8C, 0x95, 0xC5, 0x4B, 0x1B, 0x94, 0x78, 0x9E, 0x7C, 0xBD, 0x8B, 0xA8, 0xAF, 0x31, 0x20, 0xFE, 0x0F, 0xB3, 0x15, 0x9A, 0x7C, 0xD5, 0x5C, + 0xC2, 0xC0, 0xEC, 0xE9, 0x43, 0x2B, 0xD0, 0x9F, 0x2C, 0xFC, 0xB8, 0x2B, 0x6B, 0x15, 0xCD, 0x3F, 0x0C, 0xAF, 0x8F, 0x68, 0xB0, 0x6E, 0x6B, 0x30, 0x2E, 0x8C, + 0x3F, 0x7E, 0x96, 0x7C, 0x93, 0xB2, 0xA4, 0x0E, 0x43, 0xEA, 0x20, 0x10, 0x38, 0x6D, 0x37, 0xF8, 0x87, 0xFE, 0xA9, 0x63, 0x75, 0xF5, 0x56, 0x34, 0x4A, 0xE3, + 0xCF, 0x89, 0x18, 0x08, 0xC2, 0x76, 0x74, 0x12, 0xEC, 0xA7, 0x6D, 0xC2, 0xB7, 0x1B, 0x7A, 0xB2, 0xD4, 0xED + }; + + private static byte[] ppcData { get; } = new byte[] { + 0xF8, 0x6B, 0x2E, 0x8C, 0x95, 0xC5, 0x4B, 0x1B, 0x94, 0x78, 0x9E, 0x7C, 0xBD, 0x8B, 0xA8, 0xAF, 0x31, 0x20, 0xFE, 0x0F, 0xB3, 0x15, 0x9A, 0x7C, 0xD5, 0x5C, + 0xC2, 0xC0, 0xEC, 0xE9, 0x43, 0x2B, 0xD0, 0x9F, 0x2C, 0xFC, 0xB8, 0x2B, 0x6B, 0x15, 0xCD, 0x3F, 0x0C, 0xAF, 0x8F, 0x68, 0xB0, 0x6E, 0x6B, 0x30, 0x2E, 0x8C, + 0x3F, 0x7E, 0x96, 0x7C, 0x93, 0xB2, 0xA4, 0x0E, 0x43, 0xEA, 0x20, 0x10, 0x38, 0x6D, 0x37, 0xF8, 0x87, 0xFE, 0xA9, 0x63, 0x75, 0xF5, 0x56, 0x34, 0x4A, 0xE3, + 0xD6, 0x75, 0x18, 0x08, 0xC2, 0x76, 0x74, 0x12, 0xEC, 0xA7, 0x6D, 0xC2, 0xB7, 0x1B, 0x7A, 0xB2, 0xD4, 0xED + }; + + private static byte[] armResultData { get; } = new byte[] { + 0x7C, 0xFC, 0x0A, 0x00, 0x16, 0x42, 0x01, 0x00, 0x80, 0xFC, 0x0A, 0x00, 0x16, 0xB8, 0x00, 0x00, 0x84, 0xFC, 0x0A, 0x00, 0x16, 0x3A, 0x00, 0x00, 0x04, 0xE0, + 0x2D, 0xE5, 0x04, 0xD0, 0x4D, 0xE2, 0x0E, 0x04, 0x00, 0xEB, 0x04, 0xD0, 0x8D, 0xE2, 0x04, 0xE0, 0x9D, 0xE4, 0x1E, 0xFF, 0x2F, 0xE1, 0x04, 0xE0, 0x2D, 0xE5, + 0x04, 0xE0, 0x9F, 0xE5, 0x0E, 0xE0, 0x8F, 0xE0, 0x08, 0xF0, 0xBE, 0xE5, 0xF0, 0x3A, 0x0A, 0x00, 0x00, 0xC6, 0x8F, 0xE2, 0xA3, 0xCA, 0x8C, 0xE2, 0xF0, 0xFA, + 0xBC, 0xE5, 0x00, 0xC6, 0x8F, 0xE2, 0xA3, 0xCA, 0x8C, 0xE2, 0xE8, 0xFA, 0xBC, 0xE5, 0x00, 0xC6, 0x8F, 0xE2 + }; + + private static byte[] armData { get; } = new byte[] { + 0x7C, 0xFC, 0x0A, 0x00, 0x16, 0x42, 0x01, 0x00, 0x80, 0xFC, 0x0A, 0x00, 0x16, 0xB8, 0x00, 0x00, 0x84, 0xFC, 0x0A, 0x00, 0x16, 0x3A, 0x00, 0x00, 0x04, 0xE0, + 0x2D, 0xE5, 0x04, 0xD0, 0x4D, 0xE2, 0x18, 0x13, 0x00, 0xEB, 0x04, 0xD0, 0x8D, 0xE2, 0x04, 0xE0, 0x9D, 0xE4, 0x1E, 0xFF, 0x2F, 0xE1, 0x04, 0xE0, 0x2D, 0xE5, + 0x04, 0xE0, 0x9F, 0xE5, 0x0E, 0xE0, 0x8F, 0xE0, 0x08, 0xF0, 0xBE, 0xE5, 0xF0, 0x3A, 0x0A, 0x00, 0x00, 0xC6, 0x8F, 0xE2, 0xA3, 0xCA, 0x8C, 0xE2, 0xF0, 0xFA, + 0xBC, 0xE5, 0x00, 0xC6, 0x8F, 0xE2, 0xA3, 0xCA, 0x8C, 0xE2, 0xE8, 0xFA, 0xBC, 0xE5, 0x00, 0xC6, 0x8F, 0xE2 + }; + + private static byte[] armtResultData { get; } = new byte[] { + 0x95, 0x23, 0xB6, 0xB1, 0xBE, 0x60, 0x79, 0xF0, 0xF6, 0x01, 0xD9, 0x7F, 0x2E, 0x03, 0x31, 0x1C, 0xFD, 0xD3, 0x40, 0x0F, 0x21, 0x3C, 0x06, 0x97, 0xE5, 0xC3, + 0x57, 0x11, 0x76, 0x6F, 0xE3, 0x70, 0xED, 0x49, 0xCB, 0xB5, 0xC9, 0x42, 0x59, 0x10, 0x2F, 0xBD, 0xAE, 0xB1, 0x40, 0x4D, 0x9D, 0x7C, 0xE9, 0xFC, 0x48, 0x3E, + 0xBC, 0x7F, 0x0B, 0x23, 0xB0, 0x8A, 0x4D, 0x02, 0x39, 0xC4, 0xFB, 0x66, 0x83, 0x7F, 0xA7, 0xBD, 0x12, 0xAC, 0xED, 0x31, 0x34, 0x93, 0x4D, 0x8D, 0xD7, 0x94, + 0x93, 0x1C, 0x0A, 0x50, 0x54, 0x4B, 0x03, 0x55, 0x27, 0xFE, 0xCE, 0x29, 0x66, 0x52, 0x81, 0xAE, 0x69, 0xA0, 0x69, 0xF2, 0x3D, 0xFF, 0xA1, 0x8A, 0x5D, 0x61, + 0x7D, 0xC5, 0x94, 0x0A, 0x7D, 0xED, 0x11, 0x0F + }; + + private static byte[] armtData { get; } = new byte[] { + 0x95, 0x23, 0xB6, 0xB1, 0xBE, 0x60, 0x79, 0xF0, 0xF6, 0x01, 0xD9, 0x7F, 0x2E, 0x03, 0x31, 0x1C, 0xFD, 0xD3, 0x40, 0x0F, 0x21, 0x3C, 0x06, 0x97, 0xE5, 0xC3, + 0x57, 0x11, 0x76, 0x6F, 0xE3, 0x70, 0xED, 0x49, 0xCB, 0xB5, 0xC9, 0x42, 0x59, 0x10, 0x2F, 0xBD, 0xAE, 0xB1, 0x40, 0x4D, 0x9D, 0x7C, 0xE9, 0xFC, 0x48, 0x3E, + 0xBC, 0x7F, 0x0B, 0x23, 0xB0, 0x8A, 0x4D, 0x02, 0x39, 0xC4, 0xFB, 0x66, 0x83, 0x7F, 0xA7, 0xBD, 0x12, 0xAC, 0xED, 0x31, 0x34, 0x93, 0x4D, 0x8D, 0xD7, 0x94, + 0x93, 0x1C, 0x0A, 0x50, 0x54, 0x4B, 0x03, 0x55, 0x27, 0xFE, 0xCE, 0x29, 0x66, 0x52, 0x81, 0xAE, 0x69, 0xA0, 0x6A, 0xF2, 0x6F, 0xFC, 0xA1, 0x8A, 0x5D, 0x61, + 0x7D, 0xC5, 0x94, 0x0A, 0x7D, 0xED, 0x11, 0x0F + }; + + private static byte[] ia64ResultData { get; } = new byte[] { + 0x4D, 0xF8, 0xF2, 0x0D, 0x06, 0x2F, 0x74, 0x0F, 0xF0, 0x91, 0x06, 0x0B, 0x19, 0x22, 0x91, 0x5A, 0x66, 0x56, 0xA7, 0x15, 0x77, 0x1E, 0x2F, 0xA3, 0xE4, 0xDE, + 0x93, 0x1C, 0xD5, 0xCE, 0x6E, 0x45, 0x36, 0x15, 0x15, 0x65, 0x4E, 0xC5, 0xA3, 0x8C, 0x5A, 0x8B, 0x8A, 0x1C, 0x12, 0x5B, 0x39, 0x1F, 0xA0, 0xF2, 0x93, 0x7C, + 0x7F, 0x5D, 0xD9, 0x30, 0x1F, 0xF6, 0x5C, 0x10, 0x62, 0x3E, 0xB4, 0x64, 0x56, 0x48, 0xB2, 0x20, 0x39, 0xE8, 0x44, 0x10, 0x87, 0x9E, 0x2C, 0xFC, 0x29, 0x0E, + 0x20, 0x76, 0xCE, 0xDA, 0x93, 0x1C, 0xED, 0x54, 0x0D, 0xAF, 0xEC, 0xDE, 0x93, 0x1C, 0x2B, 0x72, 0xD5, 0x0D + }; + + private static byte[] ia64Data { get; } = new byte[] { + 0x4D, 0xF8, 0xF2, 0x0D, 0x06, 0x2F, 0x74, 0x0F, 0xF0, 0x91, 0x06, 0x0B, 0x19, 0x22, 0x91, 0x5A, 0x66, 0x56, 0xA7, 0x15, 0x77, 0x1E, 0x2F, 0xA3, 0xE4, 0xDE, + 0x93, 0x1C, 0xD5, 0xCE, 0x6E, 0x45, 0x36, 0x15, 0x15, 0x65, 0x4E, 0xC5, 0xA3, 0x8C, 0x5A, 0x8B, 0x8A, 0x1C, 0x12, 0x5B, 0x39, 0x1F, 0xA0, 0xF2, 0x93, 0x7C, + 0x7F, 0x5D, 0xD9, 0x30, 0x1F, 0xF6, 0x5C, 0x10, 0x62, 0x3E, 0xB4, 0x64, 0x56, 0x48, 0xB2, 0x20, 0x39, 0xE8, 0x44, 0x80, 0x8C, 0x9E, 0x2C, 0xFC, 0x29, 0x0E, + 0x20, 0x76, 0xCE, 0xDA, 0x93, 0x1C, 0xED, 0x54, 0x0D, 0xAF, 0xEC, 0xDE, 0x93, 0x1C, 0x2B, 0x72, 0xD5, 0x0D + }; + + private static byte[] sparcResultData { get; } = new byte[] { + 0x78, 0x2E, 0x73, 0x6F, 0x2E, 0x33, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xA2, 0x0D, + 0x10, 0x12, 0x30, 0x03, 0xC9, 0x37, 0x40, 0x1A, 0x85, 0xD9, 0x44, 0x34, 0x40, 0x32, 0x85, 0xA0, 0x30, 0x40, 0x00, 0x70, 0x00, 0x40, 0x84, 0x80, 0x04, 0x00, + 0xE4, 0xAC, 0x07, 0x04, 0x21, 0x44, 0x02, 0x20, 0x10, 0x00, 0x40, 0xC2, 0x89, 0x98, 0x85, 0x00, 0x58, 0x6A, 0x41, 0x8E, 0x18, 0xA1, 0x91, 0x00, 0x10, 0x00, + }; + + private static byte[] sparcData { get; } = new byte[] { + 0x78, 0x2E, 0x73, 0x6F, 0x2E, 0x33, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x44, 0x0B, 0x00, 0x00, 0x00, 0xA2, 0x0D, + 0x10, 0x12, 0x30, 0x03, 0xC9, 0x37, 0x40, 0x1A, 0x86, 0x21, 0x44, 0x34, 0x40, 0x32, 0x85, 0xA0, 0x30, 0x40, 0x00, 0x70, 0x00, 0x40, 0x84, 0x80, 0x04, 0x00, + 0xE4, 0xAC, 0x07, 0x04, 0x21, 0x44, 0x02, 0x20, 0x10, 0x00, 0x40, 0xC2, 0x89, 0x98, 0x85, 0x00, 0x58, 0x6A, 0x41, 0x8E, 0x18, 0xA1, 0x91, 0x00, 0x10, 0x00, + }; + + private void CompareBuffer(byte[] testBuffer, byte[] targetBuffer) + { + Assert.Equal(testBuffer, targetBuffer); + } + + [Fact] + public void X86ConverterDecodeTest() + { + uint state = 0; + uint ip = 0x2000; + var testData = x86Data; + BranchExecFilter.X86Converter(testData, ip, ref state); + CompareBuffer(testData, x86resultData); + } + + [Fact] + public void PowerPCConverterDecodeTest() + { + uint ip = 0x6A0; + var testData = ppcData; + BranchExecFilter.PowerPCConverter(testData, ip); + CompareBuffer(testData, ppcResultData); + } + + [Fact] + public void ARMConverteDecoderTest() + { + uint ip = 0x3C00; + var testData = armData; + BranchExecFilter.ARMConverter(testData, ip); + CompareBuffer(testData, armResultData); + } + + [Fact] + public void ARMTConverterDecodeTest() + { + uint ip = 0xA00; + var testData = armtData; + BranchExecFilter.ARMTConverter(testData, ip); + CompareBuffer(testData, armtResultData); + } + + [Fact] + public void IA64ConverterDecodeTest() + { + uint ip = 0xAA0; + var testData = ia64Data; + BranchExecFilter.IA64Converter(testData, ip); + CompareBuffer(testData, ia64ResultData); + } + + [Fact] + public void SPARCConverterDecodeTest() + { + uint ip = 0x100; + var testData = sparcData; + BranchExecFilter.SPARCConverter(testData, ip); + CompareBuffer(testData, sparcResultData); + } + } +} diff --git a/tests/SharpCompress.Test/Xz/Filters/BCJTests.cs b/tests/SharpCompress.Test/Xz/Filters/BCJTests.cs new file mode 100644 index 00000000..c562a43b --- /dev/null +++ b/tests/SharpCompress.Test/Xz/Filters/BCJTests.cs @@ -0,0 +1,88 @@ +/* + * BCJTests.cs -- XZ converter test class + * + */ + +using System.IO; +using SharpCompress.Compressors.Xz.Filters; +using Xunit; + +namespace SharpCompress.Test.Xz.Filters +{ + public class BCJTests : XZTestsBase + { + private readonly ArmFilter armFilter; + private readonly ArmThumbFilter armtFilter; + private readonly IA64Filter ia64Filter; + private readonly PowerPCFilter ppcFilter; + private readonly SparcFilter sparcFilter; + private readonly X86Filter x86Filter; + public BCJTests() + { + armFilter = new ArmFilter(); + armtFilter = new ArmThumbFilter(); + ia64Filter = new IA64Filter(); + ppcFilter = new PowerPCFilter(); + sparcFilter = new SparcFilter(); + x86Filter = new X86Filter(); + } + + [Fact] + public void IsOnlyAllowedLast() + { + Assert.False(armFilter.AllowAsLast); + Assert.True(armFilter.AllowAsNonLast); + + Assert.False(armtFilter.AllowAsLast); + Assert.True(armtFilter.AllowAsNonLast); + + Assert.False(ia64Filter.AllowAsLast); + Assert.True(ia64Filter.AllowAsNonLast); + + Assert.False(ppcFilter.AllowAsLast); + Assert.True(ppcFilter.AllowAsNonLast); + + Assert.False(sparcFilter.AllowAsLast); + Assert.True(sparcFilter.AllowAsNonLast); + + Assert.False(x86Filter.AllowAsLast); + Assert.True(x86Filter.AllowAsNonLast); + } + + [Fact] + public void ChangesStreamSize() + { + Assert.False(armFilter.ChangesDataSize); + Assert.False(armtFilter.ChangesDataSize); + Assert.False(ia64Filter.ChangesDataSize); + Assert.False(ppcFilter.ChangesDataSize); + Assert.False(sparcFilter.ChangesDataSize); + Assert.False(x86Filter.ChangesDataSize); + } + + [Theory] + [InlineData(new byte[] { 0 })] + [InlineData(new byte[] { 0, 0, 0, 0, 0 })] + public void OnlyAcceptsOneByte(byte[] bytes) + { + InvalidDataException ex; + ex = Assert.Throws(() => armFilter.Init(bytes)); + Assert.Equal("ARM properties unexpected length", ex.Message); + + ex = Assert.Throws(() => armtFilter.Init(bytes)); + Assert.Equal("ARM Thumb properties unexpected length", ex.Message); + + ex = Assert.Throws(() => ia64Filter.Init(bytes)); + Assert.Equal("IA64 properties unexpected length", ex.Message); + + ex = Assert.Throws(() => ppcFilter.Init(bytes)); + Assert.Equal("PPC properties unexpected length", ex.Message); + + ex = Assert.Throws(() => sparcFilter.Init(bytes)); + Assert.Equal("SPARC properties unexpected length", ex.Message); + + ex = Assert.Throws(() => x86Filter.Init(bytes)); + Assert.Equal("X86 properties unexpected length", ex.Message); + } + } +}