From 0d34a434d50b793a441527c479a6572cf57db8d5 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Wed, 15 Jul 2026 17:56:41 -0700 Subject: [PATCH 1/3] Wrap memory-buffer reading surface on LLVMMemoryBufferRef Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Extensions/LLVMMemoryBufferRef.cs | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/sources/LLVMSharp.Interop/Extensions/LLVMMemoryBufferRef.cs b/sources/LLVMSharp.Interop/Extensions/LLVMMemoryBufferRef.cs index 4a88b4a6..f806e63a 100644 --- a/sources/LLVMSharp.Interop/Extensions/LLVMMemoryBufferRef.cs +++ b/sources/LLVMSharp.Interop/Extensions/LLVMMemoryBufferRef.cs @@ -1,13 +1,18 @@ // Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information. using System; +using System.Runtime.InteropServices; namespace LLVMSharp.Interop; -public unsafe partial struct LLVMMemoryBufferRef(IntPtr handle) : IEquatable +public unsafe partial struct LLVMMemoryBufferRef(IntPtr handle) : IDisposable, IEquatable { public IntPtr Handle = handle; + public readonly UIntPtr BufferSize => (Handle != IntPtr.Zero) ? LLVM.GetBufferSize(this) : default; + + public readonly IntPtr BufferStart => (Handle != IntPtr.Zero) ? (IntPtr)LLVM.GetBufferStart(this) : IntPtr.Zero; + public static implicit operator LLVMMemoryBufferRef(LLVMOpaqueMemoryBuffer* MemoryBuffer) => new LLVMMemoryBufferRef((IntPtr)MemoryBuffer); public static implicit operator LLVMOpaqueMemoryBuffer*(LLVMMemoryBufferRef MemoryBuffer) => (LLVMOpaqueMemoryBuffer*)MemoryBuffer.Handle; @@ -16,6 +21,57 @@ public unsafe partial struct LLVMMemoryBufferRef(IntPtr handle) : IEquatable !(left == right); + public static LLVMMemoryBufferRef CreateWithContentsOfFile(string Path) => CreateWithContentsOfFile(Path.AsSpan()); + + public static LLVMMemoryBufferRef CreateWithContentsOfFile(ReadOnlySpan Path) + { + if (!TryCreateWithContentsOfFile(Path, out LLVMMemoryBufferRef MemBuf, out string Message)) + { + throw new ExternalException(Message); + } + + return MemBuf; + } + + public static LLVMMemoryBufferRef CreateWithMemoryRange(ReadOnlySpan InputData, ReadOnlySpan BufferName, bool RequiresNullTerminator) + { + using var marshaledBufferName = new MarshaledString(BufferName); + + fixed (byte* pInputData = InputData) + { + return LLVM.CreateMemoryBufferWithMemoryRange((sbyte*)pInputData, (nuint)InputData.Length, marshaledBufferName, RequiresNullTerminator ? 1 : 0); + } + } + + public static LLVMMemoryBufferRef CreateWithMemoryRangeCopy(ReadOnlySpan InputData, ReadOnlySpan BufferName) + { + using var marshaledBufferName = new MarshaledString(BufferName); + + fixed (byte* pInputData = InputData) + { + return LLVM.CreateMemoryBufferWithMemoryRangeCopy((sbyte*)pInputData, (nuint)InputData.Length, marshaledBufferName); + } + } + + public static LLVMMemoryBufferRef CreateWithSTDIN() + { + if (!TryCreateWithSTDIN(out LLVMMemoryBufferRef MemBuf, out string Message)) + { + throw new ExternalException(Message); + } + + return MemBuf; + } + + public void Dispose() + { + if (Handle != IntPtr.Zero) + { + LLVM.DisposeMemoryBuffer(this); + Handle = IntPtr.Zero; + } + } + public override readonly bool Equals(object? obj) => (obj is LLVMMemoryBufferRef other) && Equals(other); public readonly bool Equals(LLVMMemoryBufferRef other) => this == other; @@ -23,4 +79,48 @@ public unsafe partial struct LLVMMemoryBufferRef(IntPtr handle) : IEquatable Handle.GetHashCode(); public override readonly string ToString() => $"{nameof(LLVMMemoryBufferRef)}: {Handle:X}"; + + public static bool TryCreateWithContentsOfFile(ReadOnlySpan Path, out LLVMMemoryBufferRef OutMemBuf, out string OutMessage) + { + using var marshaledPath = new MarshaledString(Path); + + fixed (LLVMMemoryBufferRef* pOutMemBuf = &OutMemBuf) + { + sbyte* pMessage = null; + var result = LLVM.CreateMemoryBufferWithContentsOfFile(marshaledPath, (LLVMOpaqueMemoryBuffer**)pOutMemBuf, &pMessage); + + if (pMessage == null) + { + OutMessage = string.Empty; + } + else + { + OutMessage = SpanExtensions.AsString(pMessage); + LLVM.DisposeMessage(pMessage); + } + + return result == 0; + } + } + + public static bool TryCreateWithSTDIN(out LLVMMemoryBufferRef OutMemBuf, out string OutMessage) + { + fixed (LLVMMemoryBufferRef* pOutMemBuf = &OutMemBuf) + { + sbyte* pMessage = null; + var result = LLVM.CreateMemoryBufferWithSTDIN((LLVMOpaqueMemoryBuffer**)pOutMemBuf, &pMessage); + + if (pMessage == null) + { + OutMessage = string.Empty; + } + else + { + OutMessage = SpanExtensions.AsString(pMessage); + LLVM.DisposeMessage(pMessage); + } + + return result == 0; + } + } } From 7efc68b1dcd4d52cebbd36e1d3a7f8a65f6bcacf Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Wed, 15 Jul 2026 17:56:41 -0700 Subject: [PATCH 2/3] Wrap object-file binary reading surface on LLVMBinaryRef Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Extensions/LLVMBinaryRef.cs | 82 ++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/sources/LLVMSharp.Interop/Extensions/LLVMBinaryRef.cs b/sources/LLVMSharp.Interop/Extensions/LLVMBinaryRef.cs index 7e2a3209..fc415cb2 100644 --- a/sources/LLVMSharp.Interop/Extensions/LLVMBinaryRef.cs +++ b/sources/LLVMSharp.Interop/Extensions/LLVMBinaryRef.cs @@ -1,13 +1,16 @@ // Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information. using System; +using System.Runtime.InteropServices; namespace LLVMSharp.Interop; -public unsafe partial struct LLVMBinaryRef(IntPtr handle) : IEquatable +public unsafe partial struct LLVMBinaryRef(IntPtr handle) : IDisposable, IEquatable { public IntPtr Handle = handle; + public readonly LLVMBinaryType BinaryType => (Handle != IntPtr.Zero) ? LLVM.BinaryGetType(this) : default; + public static implicit operator LLVMBinaryRef(LLVMOpaqueBinary* Comdat) => new LLVMBinaryRef((IntPtr)Comdat); public static implicit operator LLVMOpaqueBinary*(LLVMBinaryRef Comdat) => (LLVMOpaqueBinary*)Comdat.Handle; @@ -16,11 +19,88 @@ public unsafe partial struct LLVMBinaryRef(IntPtr handle) : IEquatable !(left == right); + public static LLVMBinaryRef Create(LLVMMemoryBufferRef MemBuf, LLVMContextRef Context) + { + if (!TryCreate(MemBuf, Context, out LLVMBinaryRef Binary, out string Message)) + { + throw new ExternalException(Message); + } + + return Binary; + } + + public readonly LLVMMemoryBufferRef CopyMemoryBuffer() => (Handle != IntPtr.Zero) ? LLVM.BinaryCopyMemoryBuffer(this) : default; + + public readonly LLVMSectionIteratorRef CopySectionIterator() => (Handle != IntPtr.Zero) ? LLVM.ObjectFileCopySectionIterator(this) : default; + + public readonly LLVMSymbolIteratorRef CopySymbolIterator() => (Handle != IntPtr.Zero) ? LLVM.ObjectFileCopySymbolIterator(this) : default; + + public void Dispose() + { + if (Handle != IntPtr.Zero) + { + LLVM.DisposeBinary(this); + Handle = IntPtr.Zero; + } + } + public override readonly bool Equals(object? obj) => (obj is LLVMBinaryRef other) && Equals(other); public readonly bool Equals(LLVMBinaryRef other) => this == other; public override readonly int GetHashCode() => Handle.GetHashCode(); + public readonly bool IsSectionIteratorAtEnd(LLVMSectionIteratorRef SI) => (Handle != IntPtr.Zero) && LLVM.ObjectFileIsSectionIteratorAtEnd(this, SI) != 0; + + public readonly bool IsSymbolIteratorAtEnd(LLVMSymbolIteratorRef SI) => (Handle != IntPtr.Zero) && LLVM.ObjectFileIsSymbolIteratorAtEnd(this, SI) != 0; + + public readonly LLVMBinaryRef MachOUniversalBinaryCopyObjectForArch(ReadOnlySpan Arch) + { + if (!TryMachOUniversalBinaryCopyObjectForArch(Arch, out LLVMBinaryRef Binary, out string Message)) + { + throw new ExternalException(Message); + } + + return Binary; + } + public override readonly string ToString() => $"{nameof(LLVMBinaryRef)}: {Handle:X}"; + + public static bool TryCreate(LLVMMemoryBufferRef MemBuf, LLVMContextRef Context, out LLVMBinaryRef OutBinary, out string OutMessage) + { + sbyte* pMessage = null; + OutBinary = LLVM.CreateBinary(MemBuf, Context, &pMessage); + + if (pMessage == null) + { + OutMessage = string.Empty; + } + else + { + OutMessage = SpanExtensions.AsString(pMessage); + LLVM.DisposeMessage(pMessage); + } + + return OutBinary.Handle != IntPtr.Zero; + } + + public readonly bool TryMachOUniversalBinaryCopyObjectForArch(ReadOnlySpan Arch, out LLVMBinaryRef OutBinary, out string OutMessage) + { + using var marshaledArch = new MarshaledString(Arch); + + sbyte* pMessage = null; + OutBinary = LLVM.MachOUniversalBinaryCopyObjectForArch(this, marshaledArch, (nuint)marshaledArch.Length, &pMessage); + + if (pMessage == null) + { + OutMessage = string.Empty; + } + else + { + OutMessage = SpanExtensions.AsString(pMessage); + LLVM.DisposeMessage(pMessage); + } + + return OutBinary.Handle != IntPtr.Zero; + } } From 0589c40a7b97bfdc9d9711bb36f29bcf45291ef1 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Wed, 15 Jul 2026 17:56:41 -0700 Subject: [PATCH 3/3] Wrap legacy object-file and section, symbol, and relocation iterators Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Extensions/LLVMObjectFileRef.cs | 19 ++++++- .../Extensions/LLVMRelocationIteratorRef.cs | 53 ++++++++++++++++++- .../Extensions/LLVMSectionIteratorRef.cs | 53 ++++++++++++++++++- .../Extensions/LLVMSymbolIteratorRef.cs | 37 ++++++++++++- 4 files changed, 158 insertions(+), 4 deletions(-) diff --git a/sources/LLVMSharp.Interop/Extensions/LLVMObjectFileRef.cs b/sources/LLVMSharp.Interop/Extensions/LLVMObjectFileRef.cs index 7e5ba418..771ce03d 100644 --- a/sources/LLVMSharp.Interop/Extensions/LLVMObjectFileRef.cs +++ b/sources/LLVMSharp.Interop/Extensions/LLVMObjectFileRef.cs @@ -4,7 +4,7 @@ namespace LLVMSharp.Interop; -public unsafe partial struct LLVMObjectFileRef(IntPtr handle) : IEquatable +public unsafe partial struct LLVMObjectFileRef(IntPtr handle) : IDisposable, IEquatable { public IntPtr Handle = handle; @@ -16,11 +16,28 @@ public unsafe partial struct LLVMObjectFileRef(IntPtr handle) : IEquatable !(left == right); + public void Dispose() + { + if (Handle != IntPtr.Zero) + { + LLVM.DisposeObjectFile(this); + Handle = IntPtr.Zero; + } + } + public override readonly bool Equals(object? obj) => (obj is LLVMObjectFileRef other) && Equals(other); public readonly bool Equals(LLVMObjectFileRef other) => this == other; public override readonly int GetHashCode() => Handle.GetHashCode(); + public readonly LLVMSectionIteratorRef GetSections() => (Handle != IntPtr.Zero) ? LLVM.GetSections(this) : default; + + public readonly LLVMSymbolIteratorRef GetSymbols() => (Handle != IntPtr.Zero) ? LLVM.GetSymbols(this) : default; + + public readonly bool IsSectionIteratorAtEnd(LLVMSectionIteratorRef SI) => (Handle != IntPtr.Zero) && LLVM.IsSectionIteratorAtEnd(this, SI) != 0; + + public readonly bool IsSymbolIteratorAtEnd(LLVMSymbolIteratorRef SI) => (Handle != IntPtr.Zero) && LLVM.IsSymbolIteratorAtEnd(this, SI) != 0; + public override readonly string ToString() => $"{nameof(LLVMObjectFileRef)}: {Handle:X}"; } diff --git a/sources/LLVMSharp.Interop/Extensions/LLVMRelocationIteratorRef.cs b/sources/LLVMSharp.Interop/Extensions/LLVMRelocationIteratorRef.cs index e86a8266..f99a7ed1 100644 --- a/sources/LLVMSharp.Interop/Extensions/LLVMRelocationIteratorRef.cs +++ b/sources/LLVMSharp.Interop/Extensions/LLVMRelocationIteratorRef.cs @@ -4,10 +4,44 @@ namespace LLVMSharp.Interop; -public unsafe partial struct LLVMRelocationIteratorRef(IntPtr handle) : IEquatable +public unsafe partial struct LLVMRelocationIteratorRef(IntPtr handle) : IDisposable, IEquatable { public IntPtr Handle = handle; + public readonly ulong Offset => (Handle != IntPtr.Zero) ? LLVM.GetRelocationOffset(this) : default; + + public readonly LLVMSymbolIteratorRef Symbol => (Handle != IntPtr.Zero) ? LLVM.GetRelocationSymbol(this) : default; + + public readonly ulong Type => (Handle != IntPtr.Zero) ? LLVM.GetRelocationType(this) : default; + + public readonly string TypeName + { + get + { + if (Handle == IntPtr.Zero) + { + return string.Empty; + } + + var pTypeName = LLVM.GetRelocationTypeName(this); + return (pTypeName != null) ? SpanExtensions.AsString(pTypeName) : string.Empty; + } + } + + public readonly string ValueString + { + get + { + if (Handle == IntPtr.Zero) + { + return string.Empty; + } + + var pValueString = LLVM.GetRelocationValueString(this); + return (pValueString != null) ? SpanExtensions.AsString(pValueString) : string.Empty; + } + } + public static implicit operator LLVMRelocationIteratorRef(LLVMOpaqueRelocationIterator* value) => new LLVMRelocationIteratorRef((IntPtr)value); public static implicit operator LLVMOpaqueRelocationIterator*(LLVMRelocationIteratorRef value) => (LLVMOpaqueRelocationIterator*)value.Handle; @@ -16,11 +50,28 @@ public unsafe partial struct LLVMRelocationIteratorRef(IntPtr handle) : IEquatab public static bool operator !=(LLVMRelocationIteratorRef left, LLVMRelocationIteratorRef right) => !(left == right); + public void Dispose() + { + if (Handle != IntPtr.Zero) + { + LLVM.DisposeRelocationIterator(this); + Handle = IntPtr.Zero; + } + } + public override readonly bool Equals(object? obj) => (obj is LLVMRelocationIteratorRef other) && Equals(other); public readonly bool Equals(LLVMRelocationIteratorRef other) => this == other; public override readonly int GetHashCode() => Handle.GetHashCode(); + public readonly void MoveToNextRelocation() + { + if (Handle != IntPtr.Zero) + { + LLVM.MoveToNextRelocation(this); + } + } + public override readonly string ToString() => $"{nameof(LLVMRelocationIteratorRef)}: {Handle:X}"; } diff --git a/sources/LLVMSharp.Interop/Extensions/LLVMSectionIteratorRef.cs b/sources/LLVMSharp.Interop/Extensions/LLVMSectionIteratorRef.cs index 5e05623e..72fc8d75 100644 --- a/sources/LLVMSharp.Interop/Extensions/LLVMSectionIteratorRef.cs +++ b/sources/LLVMSharp.Interop/Extensions/LLVMSectionIteratorRef.cs @@ -4,10 +4,30 @@ namespace LLVMSharp.Interop; -public unsafe partial struct LLVMSectionIteratorRef(IntPtr handle) : IEquatable +public unsafe partial struct LLVMSectionIteratorRef(IntPtr handle) : IDisposable, IEquatable { public IntPtr Handle = handle; + public readonly ulong Address => (Handle != IntPtr.Zero) ? LLVM.GetSectionAddress(this) : default; + + public readonly IntPtr Contents => (Handle != IntPtr.Zero) ? (IntPtr)LLVM.GetSectionContents(this) : IntPtr.Zero; + + public readonly string Name + { + get + { + if (Handle == IntPtr.Zero) + { + return string.Empty; + } + + var pName = LLVM.GetSectionName(this); + return (pName != null) ? SpanExtensions.AsString(pName) : string.Empty; + } + } + + public readonly ulong Size => (Handle != IntPtr.Zero) ? LLVM.GetSectionSize(this) : default; + public static implicit operator LLVMSectionIteratorRef(LLVMOpaqueSectionIterator* value) => new LLVMSectionIteratorRef((IntPtr)value); public static implicit operator LLVMOpaqueSectionIterator*(LLVMSectionIteratorRef value) => (LLVMOpaqueSectionIterator*)value.Handle; @@ -16,11 +36,42 @@ public unsafe partial struct LLVMSectionIteratorRef(IntPtr handle) : IEquatable< public static bool operator !=(LLVMSectionIteratorRef left, LLVMSectionIteratorRef right) => !(left == right); + public readonly bool ContainsSymbol(LLVMSymbolIteratorRef Sym) => (Handle != IntPtr.Zero) && LLVM.GetSectionContainsSymbol(this, Sym) != 0; + + public void Dispose() + { + if (Handle != IntPtr.Zero) + { + LLVM.DisposeSectionIterator(this); + Handle = IntPtr.Zero; + } + } + public override readonly bool Equals(object? obj) => (obj is LLVMSectionIteratorRef other) && Equals(other); public readonly bool Equals(LLVMSectionIteratorRef other) => this == other; + public readonly LLVMRelocationIteratorRef GetRelocations() => (Handle != IntPtr.Zero) ? LLVM.GetRelocations(this) : default; + public override readonly int GetHashCode() => Handle.GetHashCode(); + public readonly bool IsRelocationIteratorAtEnd(LLVMRelocationIteratorRef RI) => (Handle != IntPtr.Zero) && LLVM.IsRelocationIteratorAtEnd(this, RI) != 0; + + public readonly void MoveToContainingSection(LLVMSymbolIteratorRef Sym) + { + if (Handle != IntPtr.Zero) + { + LLVM.MoveToContainingSection(this, Sym); + } + } + + public readonly void MoveToNextSection() + { + if (Handle != IntPtr.Zero) + { + LLVM.MoveToNextSection(this); + } + } + public override readonly string ToString() => $"{nameof(LLVMSectionIteratorRef)}: {Handle:X}"; } diff --git a/sources/LLVMSharp.Interop/Extensions/LLVMSymbolIteratorRef.cs b/sources/LLVMSharp.Interop/Extensions/LLVMSymbolIteratorRef.cs index d8f7e508..8eb41bf6 100644 --- a/sources/LLVMSharp.Interop/Extensions/LLVMSymbolIteratorRef.cs +++ b/sources/LLVMSharp.Interop/Extensions/LLVMSymbolIteratorRef.cs @@ -4,10 +4,28 @@ namespace LLVMSharp.Interop; -public unsafe partial struct LLVMSymbolIteratorRef(IntPtr handle) : IEquatable +public unsafe partial struct LLVMSymbolIteratorRef(IntPtr handle) : IDisposable, IEquatable { public IntPtr Handle = handle; + public readonly ulong Address => (Handle != IntPtr.Zero) ? LLVM.GetSymbolAddress(this) : default; + + public readonly string Name + { + get + { + if (Handle == IntPtr.Zero) + { + return string.Empty; + } + + var pName = LLVM.GetSymbolName(this); + return (pName != null) ? SpanExtensions.AsString(pName) : string.Empty; + } + } + + public readonly ulong Size => (Handle != IntPtr.Zero) ? LLVM.GetSymbolSize(this) : default; + public static implicit operator LLVMSymbolIteratorRef(LLVMOpaqueSymbolIterator* value) => new LLVMSymbolIteratorRef((IntPtr)value); public static implicit operator LLVMOpaqueSymbolIterator*(LLVMSymbolIteratorRef value) => (LLVMOpaqueSymbolIterator*)value.Handle; @@ -16,11 +34,28 @@ public unsafe partial struct LLVMSymbolIteratorRef(IntPtr handle) : IEquatable !(left == right); + public void Dispose() + { + if (Handle != IntPtr.Zero) + { + LLVM.DisposeSymbolIterator(this); + Handle = IntPtr.Zero; + } + } + public override readonly bool Equals(object? obj) => (obj is LLVMSymbolIteratorRef other) && Equals(other); public readonly bool Equals(LLVMSymbolIteratorRef other) => this == other; public override readonly int GetHashCode() => Handle.GetHashCode(); + public readonly void MoveToNextSymbol() + { + if (Handle != IntPtr.Zero) + { + LLVM.MoveToNextSymbol(this); + } + } + public override readonly string ToString() => $"{nameof(LLVMSymbolIteratorRef)}: {Handle:X}"; }