Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions tests/MaxRev.Gdal.Core.Tests.XUnit/BinaryLoadingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
using OSGeo.OGR;
using OSGeo.OSR;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
Expand Down Expand Up @@ -137,6 +141,61 @@ public async Task ConfigureAll_IsThreadSafe()
_outputHelper.WriteLine($"Concurrent ConfigureAll completed across {threadCount} threads; {driverCount} drivers registered");
}

// DLLs the Windows runtime ships under their original names (mirrors
// $BundledDllKeepPatterns in win/partials.psm1): gdal.dll and the SWIG
// *_wrap.dll are loaded by name from managed code, the VC++ runtime and the
// ODBC driver are Microsoft-signed redistributables, OpenCL.dll is the OS ICD loader.
private static readonly Regex KeptWindowsDllName = new Regex(
@"^(gdal|.+_wrap|vcruntime\d.*|msvcp\d.*|concrt\d.*|vccorlib\d.*|vcomp\d.*|msodbcsql\d.*|opencl)\.dll$",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);

[Fact]
public void Windows_LoadsUniquelyNamedBundledDlls()
{
if (!OperatingSystem.IsWindows())
{
_outputHelper.WriteLine("Skipped: Windows-only check");
return;
}

// The Windows runtime ships every bundled third-party DLL as
// "<name>-maxrev.dll" so a foreign same-named DLL can't satisfy our
// imports (see #253).
GdalBase.ConfigureAll();
var modules = Process.GetCurrentProcess().Modules.Cast<ProcessModule>().ToList();
var gdal = modules.FirstOrDefault(m => string.Equals(m.ModuleName, "gdal.dll", StringComparison.OrdinalIgnoreCase));
Assert.NotNull(gdal);
var nativeDir = Path.GetDirectoryName(gdal.FileName);

var fromNativeDir = modules
.Where(m => string.Equals(Path.GetDirectoryName(m.FileName), nativeDir, StringComparison.OrdinalIgnoreCase))
.Where(m => IsNativeDll(m.FileName))
.Select(m => m.ModuleName)
.ToList();
var notUnique = fromNativeDir
.Where(n => !KeptWindowsDllName.IsMatch(n) && !n.EndsWith("-maxrev.dll", StringComparison.OrdinalIgnoreCase))
.ToList();
Assert.True(notUnique.Count == 0, $"Bundled DLLs loaded under their original names: {string.Join(", ", notUnique)}");
var renamed = fromNativeDir.Where(n => n.EndsWith("-maxrev.dll", StringComparison.OrdinalIgnoreCase)).ToList();
Assert.NotEmpty(renamed);
_outputHelper.WriteLine($"Loaded {renamed.Count} uniquely named DLLs from {nativeDir}: {string.Join(", ", renamed)}");

// No copy of a renamed DLL may also ship under its original name.
var generic = Directory.GetFiles(nativeDir, "*-maxrev.dll")
.Select(f => Path.GetFileName(f).Replace("-maxrev.dll", ".dll", StringComparison.OrdinalIgnoreCase))
.Where(n => File.Exists(Path.Combine(nativeDir, n)))
.ToList();
Assert.True(generic.Count == 0, $"Bundled DLLs are also packaged under their original names: {string.Join(", ", generic)}");
}

// Managed assemblies can show up in the module list too; only native DLLs
// (no CLI metadata) are bundled natives.
private static bool IsNativeDll(string path)
{
using var reader = new PEReader(File.OpenRead(path));
return !reader.HasMetadata;
}

[Fact]
public void RuntimePackage_IsAvailable()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,13 @@
<ProjectReference Include="..\MaxRev.Gdal.Core.Tests.AzureFunctions\MaxRev.Gdal.Core.Tests.AzureFunctions.csproj" />
<ProjectReference Include="..\MaxRev.Gdal.Core.Tests\MaxRev.Gdal.Core.Tests.csproj" />
</ItemGroup>
<!-- Compiled directly (not Add-Type'd like in the PowerShell build) so the PE
patching logic itself gets unit-test coverage; see PeImportRenamerTests.
The Linux test container copies only tests/, so skip both when win/ is absent. -->
<ItemGroup Condition="Exists('..\..\win\PeImportRenamer.cs')">
<Compile Include="..\..\win\PeImportRenamer.cs" Link="PeImportRenamer.cs" />
</ItemGroup>
<ItemGroup Condition="!Exists('..\..\win\PeImportRenamer.cs')">
<Compile Remove="PeImportRenamerTests.cs" />
</ItemGroup>
</Project>
257 changes: 257 additions & 0 deletions tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
using GdalNetcore.Build;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection.PortableExecutable;
using System.Text;
using Xunit;

namespace GdalCore_XUnit
{
// Exercises win/PeImportRenamer.cs directly (compiled into this project, see the
// csproj's linked Compile item) instead of only through the Windows-only build
// pipeline. Every real rename appends "-maxrev" and therefore lengthens the DLL
// name, so the names never fit in place: they go into an appended section
// (PeImportRenamer.AppendNameSection) or, when the PE header has no room for
// another section header, into the padding of existing read-only data sections
// (PeImportRenamer.TryPlaceInSectionPadding). Both paths are covered here.
//
// The input is a minimal, hand-crafted PE32 image with one section and a single
// import descriptor for mscoree.dll, with deliberate header slack, mirroring the
// real DLLs this tool patches in CI closely enough to validate both paths end to
// end on any OS (no Windows runtime is needed to parse or write a PE). Managed
// .NET PEs are not used because the CLR tools pack the PE header tightly.
public class PeImportRenamerTests
{
private const string DllName = "mscoree.dll";
private const string RenamedDllName = "mscoree-maxrev.dll";
private const uint CodeSection = 0x60000020; // CNT_CODE | MEM_EXECUTE | MEM_READ
private const uint ReadOnlyDataSection = 0x40000040; // CNT_INITIALIZED_DATA | MEM_READ

[Fact]
public void RewriteDllNames_AppendsSectionAndIsReadableAndIdempotent()
{
var dir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "PeImportRenamerTests-" + Guid.NewGuid().ToString("N")));
var path = Path.Combine(dir.FullName, "synthetic.dll");
try
{
File.WriteAllBytes(path, BuildSyntheticPe32WithImport(DllName));

var before = PeImportRenamer.GetImportedDllNames(path);

Check warning on line 40 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-14)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 40 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-15-intel)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 40 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / WindowsBuild / TestPackages-Windows (windows-2022)

The type 'PeImportRenamer' in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs'.
Assert.Contains(DllName, before, StringComparer.OrdinalIgnoreCase);

int originalSectionCount;
using (var stream = File.OpenRead(path))
using (var reader = new PEReader(stream))
{
originalSectionCount = reader.PEHeaders.SectionHeaders.Length;
}

var renames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
[DllName] = "mscoree-maxrev.dll"
};

var changed = PeImportRenamer.RewriteDllNames(path, renames, null);

Check warning on line 55 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-14)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 55 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-15-intel)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 55 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / WindowsBuild / TestPackages-Windows (windows-2022)

The type 'PeImportRenamer' in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs'.
Assert.True(changed > 0, "Expected at least one import name to be rewritten");

var after = PeImportRenamer.GetImportedDllNames(path);

Check warning on line 58 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-14)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 58 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-15-intel)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 58 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / WindowsBuild / TestPackages-Windows (windows-2022)

The type 'PeImportRenamer' in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs'.
Assert.Contains("mscoree-maxrev.dll", after, StringComparer.OrdinalIgnoreCase);
Assert.DoesNotContain(DllName, after, StringComparer.OrdinalIgnoreCase);

byte[] patchedBytes;
using (var stream = File.OpenRead(path))
using (var reader = new PEReader(stream))
{
var headers = reader.PEHeaders;
// The new name is longer than "mscoree.dll", so it does not fit
// in place and must go into an appended ".dllname" section.
Assert.Equal(originalSectionCount + 1, headers.SectionHeaders.Length);

var lastSection = headers.SectionHeaders[headers.SectionHeaders.Length - 1];
var expectedMinSizeOfImage = lastSection.VirtualAddress + lastSection.VirtualSize;
Assert.True(headers.PEHeader.SizeOfImage >= expectedMinSizeOfImage,
$"SizeOfImage ({headers.PEHeader.SizeOfImage}) must cover the appended section (ends at {expectedMinSizeOfImage})");
}
patchedBytes = File.ReadAllBytes(path);

// A second run renaming the same (already renamed) import is a no-op:
// there is nothing left named "mscoree.dll" to rewrite, so the file
// must come out byte-identical.
var secondRunChanged = PeImportRenamer.RewriteDllNames(path, renames, null);

Check warning on line 81 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-14)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 81 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-15-intel)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 81 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / WindowsBuild / TestPackages-Windows (windows-2022)

The type 'PeImportRenamer' in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs'.
Assert.Equal(0, secondRunChanged);
var afterSecondRun = File.ReadAllBytes(path);
Assert.Equal(patchedBytes, afterSecondRun);
}
finally
{
try { Directory.Delete(dir.FullName, true); } catch { /* best-effort cleanup */ }
}
}

[Fact]
public void RewriteDllNames_UsesSectionPaddingWhenHeaderIsFull()
{
WithTempFile(path =>
{
File.WriteAllBytes(path, BuildSyntheticPe32WithImport(DllName, ReadOnlyDataSection, fillHeaderSlack: true));
uint originalVirtualSize;
using (var reader = new PEReader(File.OpenRead(path)))
{
originalVirtualSize = (uint)reader.PEHeaders.SectionHeaders[0].VirtualSize;
}

Assert.True(PeImportRenamer.RewriteDllNames(path, Renames(), null) > 0);

Check warning on line 104 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-14)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 104 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-15-intel)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 104 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / WindowsBuild / TestPackages-Windows (windows-2022)

The type 'PeImportRenamer' in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs'.
Assert.Equal(new[] { RenamedDllName }, PeImportRenamer.GetImportedDllNames(path));

Check warning on line 105 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-14)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 105 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-15-intel)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 105 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / WindowsBuild / TestPackages-Windows (windows-2022)

The type 'PeImportRenamer' in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs'.

using (var reader = new PEReader(File.OpenRead(path)))
{
var headers = reader.PEHeaders;
// No section was added; the existing one grew over its padding
// by exactly the new NUL-terminated name.
Assert.Single(headers.SectionHeaders);
Assert.Equal(originalVirtualSize + RenamedDllName.Length + 1, (uint)headers.SectionHeaders[0].VirtualSize);
}

var patched = File.ReadAllBytes(path);
Assert.Equal(0, PeImportRenamer.RewriteDllNames(path, Renames(), null));

Check warning on line 117 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-14)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 117 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-15-intel)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 117 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / WindowsBuild / TestPackages-Windows (windows-2022)

The type 'PeImportRenamer' in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs'.
Assert.Equal(patched, File.ReadAllBytes(path));
});
}

[Fact]
public void RewriteDllNames_ThrowsWhenNoSpaceForNewNames()
{
WithTempFile(path =>
{
// Header slack is taken and the only section is executable code,
// which the padding fallback must not touch.
var original = BuildSyntheticPe32WithImport(DllName, CodeSection, fillHeaderSlack: true);
File.WriteAllBytes(path, original);

var ex = Assert.Throws<InvalidOperationException>(() => PeImportRenamer.RewriteDllNames(path, Renames(), null));

Check warning on line 132 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-14)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 132 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-15-intel)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 132 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / WindowsBuild / TestPackages-Windows (windows-2022)

The type 'PeImportRenamer' in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs'.
Assert.Contains("PE header slack after the section table is not empty", ex.Message);
Assert.Equal(original, File.ReadAllBytes(path));
});
}

[Fact]
public void RewriteDllNames_RefusesSignedFiles()
{
WithTempFile(path =>
{
File.WriteAllBytes(path, BuildSyntheticPe32WithImport(DllName, CodeSection));
Assert.False(PeImportRenamer.IsAuthenticodeSigned(path));

Check warning on line 144 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-14)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 144 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-15-intel)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 144 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / WindowsBuild / TestPackages-Windows (windows-2022)

The type 'PeImportRenamer' in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs'.

var signed = BuildSyntheticPe32WithImport(DllName, CodeSection, certificateTableSize: 8);
File.WriteAllBytes(path, signed);
Assert.True(PeImportRenamer.IsAuthenticodeSigned(path));

Check warning on line 148 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-14)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 148 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / MacOSBuild / TestPackages-MacOS (macos-15-intel)

The type 'PeImportRenamer' in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in '/Users/runner/work/gdal.netcore/gdal.netcore/tests/MaxRev.Gdal.Core.Tests.XUnit/../../win/PeImportRenamer.cs'.

Check warning on line 148 in tests/MaxRev.Gdal.Core.Tests.XUnit/PeImportRenamerTests.cs

View workflow job for this annotation

GitHub Actions / WindowsBuild / TestPackages-Windows (windows-2022)

The type 'PeImportRenamer' in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs' conflicts with the imported type 'PeImportRenamer' in 'MaxRev.Gdal.Core, Version=3.13.3.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\gdal.netcore\gdal.netcore\tests\MaxRev.Gdal.Core.Tests.XUnit\..\..\win\PeImportRenamer.cs'.
Assert.Throws<InvalidOperationException>(() => PeImportRenamer.RewriteDllNames(path, Renames(), null));
Assert.Equal(signed, File.ReadAllBytes(path));
});
}

private static Dictionary<string, string> Renames()
{
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { [DllName] = RenamedDllName };
}

private static void WithTempFile(Action<string> test)
{
var dir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "PeImportRenamerTests-" + Guid.NewGuid().ToString("N")));
try
{
test(Path.Combine(dir.FullName, "synthetic.dll"));
}
finally
{
try { Directory.Delete(dir.FullName, true); } catch { /* best-effort cleanup */ }
}
}

/// <summary>
/// Builds a minimal valid PE32 image: DOS header/stub, COFF + optional
/// headers (16 data directories), one ".text" section containing a single
/// import descriptor (plus its null terminator) for <paramref name="dllName"/>,
/// and deliberate zero-filled slack between the section table and
/// SizeOfHeaders so a new section header can be appended later.
/// <paramref name="fillHeaderSlack"/> puts non-zero bytes into that slack,
/// and <paramref name="certificateTableSize"/> sets the certificate table
/// directory as if the file were Authenticode-signed.
/// </summary>
private static byte[] BuildSyntheticPe32WithImport(string dllName, uint sectionCharacteristics = CodeSection, bool fillHeaderSlack = false, uint certificateTableSize = 0)
{
const int peHeaderOffset = 0x80;
const int coffOffset = peHeaderOffset + 4;
const int optOffset = coffOffset + 20;
const int optionalHeaderSize = 224; // PE32, 16 data directories
const int sectionTableOffset = optOffset + optionalHeaderSize;
const uint sizeOfHeaders = 0x200;
const uint fileAlignment = 0x200;
const uint sectionAlignment = 0x1000;
const uint sectionVirtualAddress = 0x1000;

var nameBytes = Encoding.ASCII.GetBytes(dllName + "\0");
uint nameRva = sectionVirtualAddress + 40; // right after the 2 import descriptors
uint sectionVirtualSize = (uint)(40 + nameBytes.Length);
uint sectionRawSize = Align(sectionVirtualSize, fileAlignment);
uint sectionRawPointer = sizeOfHeaders;

var data = new byte[sectionRawPointer + sectionRawSize];

// DOS header
data[0] = (byte)'M';
data[1] = (byte)'Z';
WriteUInt32(data, 0x3C, peHeaderOffset);

// PE signature + COFF header
WriteUInt32(data, peHeaderOffset, 0x00004550); // "PE\0\0"
WriteUInt16(data, coffOffset, 0x014C); // Machine = IMAGE_FILE_MACHINE_I386
WriteUInt16(data, coffOffset + 2, 1); // NumberOfSections
WriteUInt16(data, coffOffset + 16, optionalHeaderSize); // SizeOfOptionalHeader
WriteUInt16(data, coffOffset + 18, 0x0102); // Characteristics: EXECUTABLE_IMAGE | 32BIT_MACHINE

// Optional header (PE32)
WriteUInt16(data, optOffset, 0x10B); // Magic = PE32
WriteUInt32(data, optOffset + 28, 0x00400000); // ImageBase
WriteUInt32(data, optOffset + 32, sectionAlignment); // SectionAlignment
WriteUInt32(data, optOffset + 36, fileAlignment); // FileAlignment
WriteUInt32(data, optOffset + 56, Align(sectionVirtualAddress + sectionVirtualSize, sectionAlignment)); // SizeOfImage
WriteUInt32(data, optOffset + 60, sizeOfHeaders); // SizeOfHeaders
WriteUInt32(data, optOffset + 92, 16); // NumberOfRvaAndSizes
// Data directory 1 (Import Table): RVA + Size, covering the descriptor + terminator
WriteUInt32(data, optOffset + 96 + 1 * 8, sectionVirtualAddress);
WriteUInt32(data, optOffset + 96 + 1 * 8 + 4, 40);

// Section header: ".text"
var sectionName = Encoding.ASCII.GetBytes(".text");
Buffer.BlockCopy(sectionName, 0, data, sectionTableOffset, sectionName.Length);
WriteUInt32(data, sectionTableOffset + 8, sectionVirtualSize);
WriteUInt32(data, sectionTableOffset + 12, sectionVirtualAddress);
WriteUInt32(data, sectionTableOffset + 16, sectionRawSize);
WriteUInt32(data, sectionTableOffset + 20, sectionRawPointer);
WriteUInt32(data, sectionTableOffset + 36, sectionCharacteristics);
if (fillHeaderSlack)
for (int i = sectionTableOffset + 40; i < sizeOfHeaders; i++) data[i] = 0xCC;
if (certificateTableSize != 0)
{
// Data directory 4 (Certificate Table): a file offset past the image, plus a size
WriteUInt32(data, optOffset + 96 + 4 * 8, (uint)data.Length);
WriteUInt32(data, optOffset + 96 + 4 * 8 + 4, certificateTableSize);
}

// Section content: one import descriptor (20 bytes) + null terminator
// (20 bytes), then the DLL name string.
int descriptor = (int)sectionRawPointer;
WriteUInt32(data, descriptor + 12, nameRva); // Name RVA (4th DWORD field)
WriteUInt32(data, descriptor + 16, nameRva); // FirstThunk (non-zero so this isn't read as the terminator)
Buffer.BlockCopy(nameBytes, 0, data, (int)sectionRawPointer + 40, nameBytes.Length);

return data;
}

private static uint Align(uint value, uint alignment) => (value + alignment - 1) / alignment * alignment;
private static void WriteUInt16(byte[] b, int o, ushort v) { b[o] = (byte)v; b[o + 1] = (byte)(v >> 8); }
private static void WriteUInt32(byte[] b, int o, uint v) { b[o] = (byte)v; b[o + 1] = (byte)(v >> 8); b[o + 2] = (byte)(v >> 16); b[o + 3] = (byte)(v >> 24); }
}
}
Loading
Loading