Skip to content

Commit

Permalink
Crossplatform ResourceUpdater (#89303)
Browse files Browse the repository at this point in the history
This enables cross-builds of windows applications on non-windows to have updated win32 resources. It also removes the need to open/write the app host multiple times during build.
  • Loading branch information
anatawa12 authored Aug 8, 2023
1 parent c83324d commit a3e38ff
Show file tree
Hide file tree
Showing 18 changed files with 912 additions and 413 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

namespace ILCompiler.DependencyAnalysis
{
// There is small set of ObjectDataBuilder in at src/installer/managed/Microsoft.NET.HostModel/ObjectDataBuilder.cs
// only for ResourceData.WriteResources
public struct ObjectDataBuilder
#if !READYTORUN
: Internal.Runtime.ITargetBinaryWriter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;

#if HOST_MODEL
namespace Microsoft.NET.HostModel.Win32Resources
#else
namespace ILCompiler.Win32Resources
#endif
{
public unsafe partial class ResourceData
{
Expand Down Expand Up @@ -51,7 +55,7 @@ void ProcessLanguageList(object languageName, uint offsetOfLanguageListEntry, bo
if (!resourceFilter(typeName, name, (ushort)languageName))
return;
}
AddResource(typeName, name, (ushort)languageName, data);
AddResourceInternal(name, typeName, (ushort)languageName, data);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
using System;
using System.Collections.Generic;

#if HOST_MODEL
namespace Microsoft.NET.HostModel.Win32Resources
#else
namespace ILCompiler.Win32Resources
#endif
{
public unsafe partial class ResourceData
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
using System;
using System.Collections;

#if HOST_MODEL
namespace Microsoft.NET.HostModel.Win32Resources
#else
namespace ILCompiler.Win32Resources
#endif
{
public unsafe partial class ResourceData
{
private void AddResource(object type, object name, ushort language, byte[] data)
private void AddResourceInternal(object name, object type, ushort language, byte[] data)
{
ResType resType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@
using System.Runtime.InteropServices;
using System.Reflection.Metadata;

#if !HOST_MODEL
using ILCompiler.DependencyAnalysis;
#endif

#if HOST_MODEL
namespace Microsoft.NET.HostModel.Win32Resources
#else
namespace ILCompiler.Win32Resources
#endif
{
public unsafe partial class ResourceData
{
Expand Down Expand Up @@ -81,16 +87,23 @@ public IMAGE_RESOURCE_DATA_ENTRY(ref BlobReader blobReader)
CodePage = blobReader.ReadUInt32();
Reserved = blobReader.ReadUInt32();
}

#if HOST_MODEL
public static void Write(ref ObjectDataBuilder dataBuilder, int sectionBase, int offsetFromSymbol, int sizeOfData)
#else
public static void Write(ref ObjectDataBuilder dataBuilder, ISymbolNode node, int offsetFromSymbol, int sizeOfData)
#endif
{
#if HOST_MODEL
dataBuilder.EmitInt(sectionBase + offsetFromSymbol);
#else
dataBuilder.EmitReloc(node,
#if READYTORUN
RelocType.IMAGE_REL_BASED_ADDR32NB,
#else
RelocType.IMAGE_REL_BASED_ABSOLUTE,
#endif
offsetFromSymbol);
#endif
dataBuilder.EmitInt(sizeOfData);
dataBuilder.EmitInt(1252); // CODEPAGE = DEFAULT_CODEPAGE
dataBuilder.EmitInt(0); // RESERVED
Expand Down
88 changes: 86 additions & 2 deletions src/coreclr/tools/Common/Compiler/Win32Resources/ResourceData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,46 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;

#if !HOST_MODEL
using ILCompiler.DependencyAnalysis;
using Internal.TypeSystem.Ecma;
#endif

#if HOST_MODEL
namespace Microsoft.NET.HostModel.Win32Resources
#else
namespace ILCompiler.Win32Resources
#endif
{
/// <summary>
/// Resource abstraction to allow examination
/// of a PE file that contains resources.
/// </summary>
public unsafe partial class ResourceData
{
#if HOST_MODEL
/// <summary>
/// Initialize a ResourceData instance from a PE file
/// </summary>
/// <param name="peFile"></param>
public ResourceData(PEReader peFile)
{
DirectoryEntry resourceDirectory = peFile.PEHeaders.PEHeader!.ResourceTableDirectory;
if (resourceDirectory.Size != 0)
{
BlobReader resourceDataBlob = peFile.GetSectionData(resourceDirectory.RelativeVirtualAddress).GetReader(0, resourceDirectory.Size);
ReadResourceData(resourceDataBlob, peFile, null);
}
}
#else
/// <summary>
/// Initialize a ResourceData instance from a PE file
/// </summary>
/// <param name="ecmaModule"></param>
public ResourceData(EcmaModule ecmaModule, Func<object, object, ushort, bool> resourceFilter = null)
public ResourceData(Internal.TypeSystem.Ecma.EcmaModule ecmaModule, Func<object, object, ushort, bool> resourceFilter = null)
{
System.Collections.Immutable.ImmutableArray<byte> ecmaData = ecmaModule.PEReader.GetEntireImage().GetContent();
PEReader peFile = ecmaModule.PEReader;
Expand All @@ -34,6 +55,7 @@ public ResourceData(EcmaModule ecmaModule, Func<object, object, ushort, bool> re
ReadResourceData(resourceDataBlob, peFile, resourceFilter);
}
}
#endif

/// <summary>
/// Find a resource in the resource data
Expand Down Expand Up @@ -67,6 +89,44 @@ public byte[] FindResource(ushort name, ushort type, ushort language)
return FindResourceInternal(name, type, language);
}

/// <summary>
/// Add or update resource
/// </summary>
public void AddResource(string name, string type, ushort language, byte[] data) => AddResourceInternal(name, type, language, data);

/// <summary>
/// Add or update resource
/// </summary>
public void AddResource(string name, ushort type, ushort language, byte[] data) => AddResourceInternal(name, type, language, data);

/// <summary>
/// Add or update resource
/// </summary>
public void AddResource(ushort name, string type, ushort language, byte[] data) => AddResourceInternal(name, type, language, data);

/// <summary>
/// Add or update resource
/// </summary>
public void AddResource(ushort name, ushort type, ushort language, byte[] data) => AddResourceInternal(name, type, language, data);

public IEnumerable<(object name, object type, ushort language, byte[] data)> GetAllResources()
{
return _resTypeHeadID.SelectMany(typeIdPair => SelectResType(typeIdPair.Key, typeIdPair.Value))
.Concat(_resTypeHeadName.SelectMany(typeNamePair => SelectResType(typeNamePair.Key, typeNamePair.Value)));

IEnumerable<(object name, object type, ushort language, byte[] data)> SelectResType(object type, ResType resType)
{
return resType.NameHeadID.SelectMany(nameIdPair => SelectResName(type, nameIdPair.Key, nameIdPair.Value))
.Concat(resType.NameHeadName.SelectMany(nameNamePair =>
SelectResName(type, nameNamePair.Key, nameNamePair.Value)));
}

IEnumerable<(object name, object type, ushort language, byte[] data)> SelectResName(object type, object name, ResName resType)
{
return resType.Languages.Select((lang) => (name, type, lang.Key, lang.Value.DataEntry));
}
}

public bool IsEmpty
{
get
Expand All @@ -81,12 +141,32 @@ public bool IsEmpty
}
}

/// <summary>
/// Add all resources in the specified ResourceData struct.
/// </summary>
public void CopyResourcesFrom(ResourceData moduleResources)
{
foreach ((object name, object type, ushort language, byte[] data) in moduleResources.GetAllResources())
AddResourceInternal(name, type, language, data);
}

#if HOST_MODEL
public void WriteResources(int sectionBase, ref ObjectDataBuilder dataBuilder)
{
WriteResources(sectionBase, ref dataBuilder, ref dataBuilder);
}
#else
public void WriteResources(ISymbolNode nodeAssociatedWithDataBuilder, ref ObjectDataBuilder dataBuilder)
{
WriteResources(nodeAssociatedWithDataBuilder, ref dataBuilder, ref dataBuilder);
}
#endif

#if HOST_MODEL
public void WriteResources(int sectionBase, ref ObjectDataBuilder dataBuilder, ref ObjectDataBuilder contentBuilder)
#else
public void WriteResources(ISymbolNode nodeAssociatedWithDataBuilder, ref ObjectDataBuilder dataBuilder, ref ObjectDataBuilder contentBuilder)
#endif
{
Debug.Assert(dataBuilder.CountBytes == 0);

Expand Down Expand Up @@ -159,7 +239,11 @@ public void WriteResources(ISymbolNode nodeAssociatedWithDataBuilder, ref Object
foreach (Tuple<ResLanguage, ObjectDataBuilder.Reservation> language in resLanguages)
{
dataBuilder.EmitInt(language.Item2, dataBuilder.CountBytes);
#if HOST_MODEL
IMAGE_RESOURCE_DATA_ENTRY.Write(ref dataBuilder, sectionBase, dataEntryTable[language.Item1], language.Item1.DataEntry.Length);
#else
IMAGE_RESOURCE_DATA_ENTRY.Write(ref dataBuilder, nodeAssociatedWithDataBuilder, dataEntryTable[language.Item1], language.Item1.DataEntry.Length);
#endif
}
dataBuilder.PadAlignment(4); // resource data entries are 4 byte aligned
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,24 +66,6 @@ void RewriteAppHost(MemoryMappedViewAccessor accessor)
}
}

void UpdateResources()
{
if (assemblyToCopyResourcesFrom != null && appHostIsPEImage)
{
if (ResourceUpdater.IsSupportedOS())
{
// Copy resources from managed dll to the apphost
new ResourceUpdater(appHostDestinationFilePath)
.AddResourcesFromPEImage(assemblyToCopyResourcesFrom)
.Update();
}
else
{
throw new AppHostCustomizationUnsupportedOSException();
}
}
}

try
{
RetryUtil.RetryOnIOError(() =>
Expand Down Expand Up @@ -115,6 +97,13 @@ void UpdateResources()
{
MachOUtils.RemoveSignature(fileStream);
}

if (assemblyToCopyResourcesFrom != null && appHostIsPEImage)
{
using var updater = new ResourceUpdater(fileStream, true);
updater.AddResourcesFromPEImage(assemblyToCopyResourcesFrom);
updater.Update();
}
}
}
finally
Expand All @@ -125,8 +114,6 @@ void UpdateResources()
}
});

RetryUtil.RetryOnWin32Error(UpdateResources);

if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var filePermissionOctal = Convert.ToInt32("755", 8); // -rwxr-xr-x
Expand Down
Loading

0 comments on commit a3e38ff

Please sign in to comment.