From 125d6523ffb6f577021c0832b516838d52239f66 Mon Sep 17 00:00:00 2001 From: Jeremy Pritts <49847914+ds5678@users.noreply.github.com> Date: Thu, 14 Dec 2023 15:01:03 -0500 Subject: [PATCH 1/3] Add support for ReadOnlySpan --- Gee.External.Capstone/CapstoneDisassembler.cs | 69 ++++++++----------- .../Gee.External.Capstone.csproj | 4 ++ Gee.External.Capstone/NativeCapstone.cs | 16 ++--- 3 files changed, 38 insertions(+), 51 deletions(-) diff --git a/Gee.External.Capstone/CapstoneDisassembler.cs b/Gee.External.Capstone/CapstoneDisassembler.cs index b905ba7..05a7274 100644 --- a/Gee.External.Capstone/CapstoneDisassembler.cs +++ b/Gee.External.Capstone/CapstoneDisassembler.cs @@ -767,13 +767,10 @@ NativeDisassembleMode CreateNativeDisassembleMode(CapstoneDisassembler /// An array of disassembled instructions. /// - /// - /// Thrown if the binary code array is a null reference. - /// /// /// Thrown if the disassembler is disposed. /// - public TInstruction[] Disassemble(byte[] binaryCode) { + public TInstruction[] Disassemble(ReadOnlySpan binaryCode) { // ... // // Throws an exception if the operation fails. @@ -793,13 +790,10 @@ public TInstruction[] Disassemble(byte[] binaryCode) { /// /// An array of disassembled instructions. /// - /// - /// Thrown if the binary code array is a null reference. - /// /// /// Thrown if the disassembler is disposed. /// - public TInstruction[] Disassemble(byte[] binaryCode, long startingAddress) { + public TInstruction[] Disassemble(ReadOnlySpan binaryCode, long startingAddress) { // ... // // Throws an exception if the operation fails. @@ -822,13 +816,10 @@ public TInstruction[] Disassemble(byte[] binaryCode, long startingAddress) { /// /// An array of disassembled instructions. /// - /// - /// Thrown if the binary code array is a null reference. - /// /// /// Thrown if the disassembler is disposed. /// - public TInstruction[] Disassemble(byte[] binaryCode, long startingAddress, int count) { + public TInstruction[] Disassemble(ReadOnlySpan binaryCode, long startingAddress, int count) { // ... // // Throws an exception if the operation fails. @@ -954,13 +945,10 @@ public string GetRegisterName(TRegisterId registerId) { /// /// A deferred collection of disassembled instructions. /// - /// - /// Thrown if the binary code array is a null reference. - /// /// /// Thrown if the disassembler is disposed. /// - public IEnumerable Iterate(byte[] binaryCode) { + public IEnumerable Iterate(ReadOnlySpan binaryCode) { // ... // // Throws an exception if the operation fails. @@ -979,15 +967,10 @@ public IEnumerable Iterate(byte[] binaryCode) { /// /// A deferred collection of disassembled instructions. /// - /// - /// Thrown if the binary code array is a null reference. - /// /// /// Thrown if the disassembler is disposed. /// - public IEnumerable Iterate(byte[] binaryCode, long startingAddress) { - CapstoneDisassembler.ThrowIfValueIsNullReference(nameof(binaryCode), binaryCode); - + public IEnumerable Iterate(ReadOnlySpan binaryCode, long startingAddress) { var binaryCodeOffset = 0; // ... @@ -1021,7 +1004,7 @@ public IEnumerable Iterate(byte[] binaryCode, long startingAddress // // To make this work though, we MUST define a local variable for the delegate! if (this._skipDataCallback != null) { - callback = OnNativeSkipDataCallback; + callback = CreateCallback(binaryCode.ToArray()); } // ... @@ -1067,24 +1050,28 @@ public IEnumerable Iterate(byte[] binaryCode, long startingAddress } } - // - // Native Skip Data Mode Callback. - // - IntPtr OnNativeSkipDataCallback(IntPtr cPBinaryCode, IntPtr cBinaryCodeSize, IntPtr cDataOffset, IntPtr pState) { - // ... - // - // Normally, a closure enclosing over a variable modified from a loop, such as this method, is a - // problem because the value of the variable is resolved at the time the closure is invoked and not - // when the variable is captured. This can lead to unexpected behavior if the closure is invoked - // outside the loop since the value of the captured variable will always be resolved to the last value - // it was set to inside the loop. - // - // However, because this closure will always be invoked from inside a disassemble loop, and never from - // outside of one, the variable value resolution behavior is exactly what we are looking for. We want - // the Capstone API to invoke this callback with an updated value for the captured variable every - // time! - var cBytesToSkip = this.SkipDataCallback(binaryCode, binaryCodeOffset); - return new IntPtr(cBytesToSkip); + NativeCapstone.SkipDataCallback CreateCallback(byte[] binaryCode) { + return OnNativeSkipDataCallback; + + // + // Native Skip Data Mode Callback. + // + IntPtr OnNativeSkipDataCallback(IntPtr cPBinaryCode, IntPtr cBinaryCodeSize, IntPtr cDataOffset, IntPtr pState) { + // ... + // + // Normally, a closure enclosing over a variable modified from a loop, such as this method, is a + // problem because the value of the variable is resolved at the time the closure is invoked and not + // when the variable is captured. This can lead to unexpected behavior if the closure is invoked + // outside the loop since the value of the captured variable will always be resolved to the last value + // it was set to inside the loop. + // + // However, because this closure will always be invoked from inside a disassemble loop, and never from + // outside of one, the variable value resolution behavior is exactly what we are looking for. We want + // the Capstone API to invoke this callback with an updated value for the captured variable every + // time! + var cBytesToSkip = this.SkipDataCallback(binaryCode, binaryCodeOffset); + return new IntPtr(cBytesToSkip); + } } } diff --git a/Gee.External.Capstone/Gee.External.Capstone.csproj b/Gee.External.Capstone/Gee.External.Capstone.csproj index 23fdb4c..4ee3bea 100644 --- a/Gee.External.Capstone/Gee.External.Capstone.csproj +++ b/Gee.External.Capstone/Gee.External.Capstone.csproj @@ -27,4 +27,8 @@ + + + + \ No newline at end of file diff --git a/Gee.External.Capstone/NativeCapstone.cs b/Gee.External.Capstone/NativeCapstone.cs index c1bae9e..55a5a6d 100644 --- a/Gee.External.Capstone/NativeCapstone.cs +++ b/Gee.External.Capstone/NativeCapstone.cs @@ -472,14 +472,14 @@ internal static Version GetVersion() { /// /// Thrown if the disassembler handle is disposed, or if the instruction handle is disposed. /// - internal static bool Iterate(NativeDisassemblerHandle hDisassembler, byte[] binaryCode, ref int binaryCodeOffset, ref long address, NativeInstructionHandle hInstruction) { - var hBinaryCode = GCHandle.Alloc(binaryCode, GCHandleType.Pinned); - try { + internal static unsafe bool Iterate(NativeDisassemblerHandle hDisassembler, ReadOnlySpan binaryCode, ref int binaryCodeOffset, ref long address, NativeInstructionHandle hInstruction) { + fixed (byte* fixedPointer = binaryCode) + { // ... // // First, we increment the pointer to the binary code buffer to the point to the address of the // instruction we want to disassemble. - var pBinaryCode = hBinaryCode.AddrOfPinnedObject() + binaryCodeOffset; + var pBinaryCode = (IntPtr)fixedPointer + binaryCodeOffset; // ... // @@ -498,7 +498,8 @@ internal static bool Iterate(NativeDisassemblerHandle hDisassembler, byte[] bina // Throws an exception if the operation fails. var initialPBinaryCode = pBinaryCode; var isDisassembled = NativeCapstoneImport.Iterate(hDisassembler, ref pBinaryCode, ref binaryCodeSize, ref address, hInstruction); - if (isDisassembled) { + if (isDisassembled) + { // ... // // Fourth, we compute a new offset to indicate to the caller the next instruction to disassemble @@ -508,11 +509,6 @@ internal static bool Iterate(NativeDisassemblerHandle hDisassembler, byte[] bina return isDisassembled; } - finally { - if (hBinaryCode.IsAllocated) { - hBinaryCode.Free(); - } - } } /// From 1171bdc5ec472e3a89f7b79b65d17cebe39d2529 Mon Sep 17 00:00:00 2001 From: Jeremy Pritts <49847914+ds5678@users.noreply.github.com> Date: Thu, 14 Dec 2023 15:02:52 -0500 Subject: [PATCH 2/3] Formatting --- Gee.External.Capstone/NativeCapstone.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Gee.External.Capstone/NativeCapstone.cs b/Gee.External.Capstone/NativeCapstone.cs index 55a5a6d..e0bdf00 100644 --- a/Gee.External.Capstone/NativeCapstone.cs +++ b/Gee.External.Capstone/NativeCapstone.cs @@ -498,8 +498,7 @@ internal static unsafe bool Iterate(NativeDisassemblerHandle hDisassembler, Read // Throws an exception if the operation fails. var initialPBinaryCode = pBinaryCode; var isDisassembled = NativeCapstoneImport.Iterate(hDisassembler, ref pBinaryCode, ref binaryCodeSize, ref address, hInstruction); - if (isDisassembled) - { + if (isDisassembled) { // ... // // Fourth, we compute a new offset to indicate to the caller the next instruction to disassemble From 20817733e3e790f4ff9c8132de1dd6a328da134c Mon Sep 17 00:00:00 2001 From: Jeremy Pritts <49847914+ds5678@users.noreply.github.com> Date: Thu, 14 Dec 2023 15:07:40 -0500 Subject: [PATCH 3/3] Java style brackets --- Gee.External.Capstone/NativeCapstone.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Gee.External.Capstone/NativeCapstone.cs b/Gee.External.Capstone/NativeCapstone.cs index e0bdf00..3aaf449 100644 --- a/Gee.External.Capstone/NativeCapstone.cs +++ b/Gee.External.Capstone/NativeCapstone.cs @@ -473,8 +473,7 @@ internal static Version GetVersion() { /// Thrown if the disassembler handle is disposed, or if the instruction handle is disposed. /// internal static unsafe bool Iterate(NativeDisassemblerHandle hDisassembler, ReadOnlySpan binaryCode, ref int binaryCodeOffset, ref long address, NativeInstructionHandle hInstruction) { - fixed (byte* fixedPointer = binaryCode) - { + fixed (byte* fixedPointer = binaryCode) { // ... // // First, we increment the pointer to the binary code buffer to the point to the address of the