diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CheckForInvalidDesignerConfig.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CheckForInvalidDesignerConfig.cs index 9f0b63874f8..df9deb27ab2 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CheckForInvalidDesignerConfig.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CheckForInvalidDesignerConfig.cs @@ -39,6 +39,9 @@ static bool HasResourceDesignerAssemblyReference (ITaskItem assembly) return false; } using var pe = new PEReader (File.OpenRead (assembly.ItemSpec)); + if (!pe.HasMetadata) { + return false; + } var reader = pe.GetMetadataReader (); return HasResourceDesignerAssemblyReference (reader); } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/FilterAssemblies.cs b/src/Xamarin.Android.Build.Tasks/Tasks/FilterAssemblies.cs index a68ea1432a6..d16d82c4a81 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/FilterAssemblies.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/FilterAssemblies.cs @@ -57,6 +57,10 @@ public override bool RunTask () void ProcessAssembly(ITaskItem assemblyItem, List output) { using var pe = new PEReader (File.OpenRead (assemblyItem.ItemSpec)); + if (!pe.HasMetadata) { + Log.LogDebugMessage ($"Skipping non-.NET assembly: {assemblyItem.ItemSpec}"); + return; + } var reader = pe.GetMetadataReader (); // Check in-memory cache var module = reader.GetModuleDefinition (); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ResolveLibraryProjectImports.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ResolveLibraryProjectImports.cs index 0099895f3fe..ed94c350edb 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/ResolveLibraryProjectImports.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/ResolveLibraryProjectImports.cs @@ -256,6 +256,10 @@ void Extract ( Log.LogDebugMessage ($"Refreshing {fileName}"); using (var pe = new PEReader (File.OpenRead (assemblyPath))) { + if (!pe.HasMetadata) { + Log.LogDebugMessage ($"Skipping non-.NET assembly: {assemblyPath}"); + continue; + } var reader = pe.GetMetadataReader (); foreach (var handle in reader.ManifestResources) { var resource = reader.GetManifestResource (handle); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/FilterAssembliesTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/FilterAssembliesTests.cs index 7eeba328dab..718bc40c710 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/FilterAssembliesTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/FilterAssembliesTests.cs @@ -100,5 +100,78 @@ public async Task GuavaListenableFuture () var expected = new [] { "Xamarin.Google.Guava.ListenableFuture.dll" }; CollectionAssert.AreEqual (expected, actual); } + + [Test] + public void NativeDll_Skipped () + { + // Create a minimal valid PE file without CLI metadata (a native DLL) + var nativeDll = Path.Combine (tempDirectory, "native.dll"); + CreateNativePE (nativeDll); + var actual = Run (nativeDll); + CollectionAssert.IsEmpty (actual, "Native DLLs without CLI metadata should be skipped."); + } + + /// + /// Creates a minimal valid PE file without CLI metadata, simulating a native DLL. + /// + static void CreateNativePE (string path) + { + using var fs = File.Create (path); + using var writer = new BinaryWriter (fs); + + // DOS header: 'MZ' magic + padding to e_lfanew at offset 0x3C + writer.Write ((ushort) 0x5A4D); // e_magic = 'MZ' + writer.Write (new byte [58]); // pad to offset 0x3C + writer.Write ((uint) 0x80); // e_lfanew = 0x80 + + // Pad to PE signature at offset 0x80 + writer.Write (new byte [0x80 - 0x40]); + + // PE signature: 'PE\0\0' + writer.Write ((uint) 0x00004550); + + // COFF header (20 bytes) + writer.Write ((ushort) 0x14C); // Machine = IMAGE_FILE_MACHINE_I386 + writer.Write ((ushort) 0); // NumberOfSections = 0 + writer.Write ((uint) 0); // TimeDateStamp + writer.Write ((uint) 0); // PointerToSymbolTable + writer.Write ((uint) 0); // NumberOfSymbols + writer.Write ((ushort) 0xE0); // SizeOfOptionalHeader (PE32) + writer.Write ((ushort) 0x2102); // Characteristics = DLL | EXECUTABLE_IMAGE | LARGE_ADDRESS_AWARE + + // Optional header (PE32) — minimal, no CLI header directory entry + writer.Write ((ushort) 0x10B); // Magic = PE32 + writer.Write ((byte) 0); // MajorLinkerVersion + writer.Write ((byte) 0); // MinorLinkerVersion + writer.Write ((uint) 0); // SizeOfCode + writer.Write ((uint) 0); // SizeOfInitializedData + writer.Write ((uint) 0); // SizeOfUninitializedData + writer.Write ((uint) 0); // AddressOfEntryPoint + writer.Write ((uint) 0); // BaseOfCode + writer.Write ((uint) 0); // BaseOfData + writer.Write ((uint) 0x10000); // ImageBase + writer.Write ((uint) 0x1000); // SectionAlignment + writer.Write ((uint) 0x200); // FileAlignment + writer.Write ((ushort) 4); // MajorOperatingSystemVersion + writer.Write ((ushort) 0); // MinorOperatingSystemVersion + writer.Write ((ushort) 0); // MajorImageVersion + writer.Write ((ushort) 0); // MinorImageVersion + writer.Write ((ushort) 4); // MajorSubsystemVersion + writer.Write ((ushort) 0); // MinorSubsystemVersion + writer.Write ((uint) 0); // Win32VersionValue + writer.Write ((uint) 0x1000); // SizeOfImage + writer.Write ((uint) 0x200); // SizeOfHeaders + writer.Write ((uint) 0); // CheckSum + writer.Write ((ushort) 3); // Subsystem = WINDOWS_CUI + writer.Write ((ushort) 0); // DllCharacteristics + writer.Write ((uint) 0x100000); // SizeOfStackReserve + writer.Write ((uint) 0x1000); // SizeOfStackCommit + writer.Write ((uint) 0x100000); // SizeOfHeapReserve + writer.Write ((uint) 0x1000); // SizeOfHeapCommit + writer.Write ((uint) 0); // LoaderFlags + writer.Write ((uint) 16); // NumberOfRvaAndSizes + // Data directories (16 entries × 8 bytes = 128 bytes), all zeroed — no CLI header + writer.Write (new byte [128]); + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MetadataResolver.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MetadataResolver.cs index abfc8e9d568..68dc923be29 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MetadataResolver.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MetadataResolver.cs @@ -19,7 +19,12 @@ public MetadataReader GetAssemblyReader (string assemblyName) { var assemblyPath = Resolve (assemblyName); if (!cache.TryGetValue (assemblyPath, out PEReader reader)) { - cache.Add (assemblyPath, reader = new PEReader (File.OpenRead (assemblyPath))); + reader = new PEReader (File.OpenRead (assemblyPath)); + if (!reader.HasMetadata) { + reader.Dispose (); + throw new InvalidOperationException ($"Assembly '{assemblyPath}' is not a .NET assembly."); + } + cache.Add (assemblyPath, reader); } return reader.GetMetadataReader (); } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs index 4163bcb6276..488efd54bd3 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs @@ -353,6 +353,9 @@ public static bool HasMonoAndroidReference (ITaskItem assembly) return true; using var pe = new PEReader (File.OpenRead (assembly.ItemSpec)); + if (!pe.HasMetadata) { + return false; // not a .NET assembly (no CLI metadata) + } var reader = pe.GetMetadataReader (); return HasMonoAndroidReference (reader); } @@ -374,6 +377,10 @@ public static bool IsReferenceAssembly (string assembly, TaskLoggingHelper log) { using (var stream = File.OpenRead (assembly)) using (var pe = new PEReader (stream)) { + if (!pe.HasMetadata) { + log.LogDebugMessage ($"Skipping non-.NET assembly: {assembly}"); + return false; + } var reader = pe.GetMetadataReader (); var assemblyDefinition = reader.GetAssemblyDefinition (); foreach (var handle in assemblyDefinition.GetCustomAttributes ()) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ResourceDesignerImportGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ResourceDesignerImportGenerator.cs index 526bfed9e0e..9a5a0c1df29 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ResourceDesignerImportGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ResourceDesignerImportGenerator.cs @@ -44,6 +44,10 @@ public void CreateImportMethods (IEnumerable libraries) primary.Members.Add (method); foreach (var assemblyPath in libraries) { using (var pe = new PEReader (File.OpenRead (assemblyPath.ItemSpec))) { + if (!pe.HasMetadata) { + Log.LogDebugMessage ($"Skipping non-.NET assembly: {assemblyPath.ItemSpec}"); + continue; + } var reader = pe.GetMetadataReader (); var resourceDesignerName = GetResourceDesignerClass (reader); if (string.IsNullOrEmpty (resourceDesignerName)) {