Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
kekyo committed Feb 9, 2024
1 parent 211bd0e commit f9e1bc5
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 18 deletions.
58 changes: 43 additions & 15 deletions chibias.core/Assembler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace chibias;
Expand Down Expand Up @@ -265,14 +266,11 @@ private void WriteRuntimeConfiguration(
}

private void WriteAppHost(
string outputFullPath,
string outputAssemblyFullPath,
string outputAssemblyPath,
string appHostTemplateFullPath,
AssemblerOptions options)
{
this.logger.Information(
$"Writing AppHost: {Path.GetFileName(outputFullPath)}");

using var ms = new MemoryStream();
using (var fs = new FileStream(
appHostTemplateFullPath, FileMode.Open, FileAccess.Read, FileShare.Read))
Expand All @@ -284,15 +282,50 @@ private void WriteAppHost(
var outputAssemblyName = Path.GetFileName(outputAssemblyPath);
var outputAssemblyNameBytes = Encoding.UTF8.GetBytes(outputAssemblyName);

var isPEImage = PEUtils.IsPEImage(ms);
var outputFullPath = Path.Combine(
Utilities.GetDirectoryPath(outputAssemblyFullPath),
Path.GetFileNameWithoutExtension(outputAssemblyFullPath) + (isPEImage ? ".exe" : ""));

this.logger.Information(
$"Writing AppHost: {Path.GetFileName(outputFullPath)}{(isPEImage ? " (PE format)" : "")}");

if (Utilities.UpdateBytes(
ms.GetBuffer(), (int)ms.Length,
ms,
appBinaryPathPlaceholderSearchValue,
outputAssemblyNameBytes))
{
using var fs = new FileStream(
outputFullPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
ms.CopyTo(fs);
fs.Flush();
if (isPEImage && options.AssemblyType == AssemblyTypes.WinExe)
{
PEUtils.SetWindowsGraphicalUserInterfaceBit(ms);
}

using (var fs = new FileStream(
outputFullPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
{
ms.CopyTo(fs);
fs.Flush();
}

if (!Utilities.IsInWindows)
{
while (true)
{
var r = Utilities.chmod(outputFullPath,
chmodFlags.S_IXOTH | chmodFlags.S_IROTH |
chmodFlags.S_IXGRP | chmodFlags.S_IRGRP |
chmodFlags.S_IXUSR | chmodFlags.S_IWUSR | chmodFlags.S_IRUSR);
if (r != -1)
{
break;
}
var errno = Marshal.GetLastWin32Error();
if (errno != Utilities.EINTR)
{
Marshal.ThrowExceptionForHR(errno);
}
}
}
}
else
{
Expand Down Expand Up @@ -321,11 +354,6 @@ options.AppHostTemplatePath is { } appHostTemplatePath &&
Utilities.GetDirectoryPath(outputAssemblyFullPath),
Path.GetFileNameWithoutExtension(outputAssemblyFullPath) + ".dll") :
outputAssemblyFullPath;
var outputFullPath = requireAppHost ?
Path.Combine(
Utilities.GetDirectoryPath(outputAssemblyFullPath),
Path.GetFileNameWithoutExtension(outputAssemblyFullPath) + (Utilities.IsInWindows ? ".exe" : "")) :
outputAssemblyFullPath;

//////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -465,7 +493,7 @@ options.AppHostTemplatePath is { } appHostTemplatePath &&
!string.IsNullOrWhiteSpace(appHostTemplatePath3))
{
this.WriteAppHost(
outputFullPath,
outputAssemblyFullPath,
outputAssemblyCandidateFullPath,
Path.GetFullPath(appHostTemplatePath3),
options);
Expand Down
135 changes: 135 additions & 0 deletions chibias.core/Internal/PEUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/////////////////////////////////////////////////////////////////////////////////////
//
// chibias-cil - The specialized backend CIL assembler for chibicc-cil
// Copyright (c) Kouji Matsui(@kozy_kekyo, @kekyo @mastodon.cloud)
//
// Licensed under MIT: https://opensource.org/licenses/MIT
//
/////////////////////////////////////////////////////////////////////////////////////

// Imported from:
// https://github.com/dotnet/runtime/blob/release/5.0/src/installer/managed/Microsoft.NET.HostModel/AppHost/PEUtils.cs

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.IO;

namespace chibias.Internal;

internal static class PEUtils
{
/// <summary>
/// The first two bytes of a PE file are a constant signature.
/// </summary>
private const UInt16 PEFileSignature = 0x5A4D;

/// <summary>
/// The offset of the PE header pointer in the DOS header.
/// </summary>
private const int PEHeaderPointerOffset = 0x3C;

/// <summary>
/// The offset of the Subsystem field in the PE header.
/// </summary>
private const int SubsystemOffset = 0x5C;

/// <summary>
/// The value of the sybsystem field which indicates Windows GUI (Graphical UI)
/// </summary>
private const UInt16 WindowsGUISubsystem = 0x2;

/// <summary>
/// The value of the subsystem field which indicates Windows CUI (Console)
/// </summary>
private const UInt16 WindowsCUISubsystem = 0x3;

/// <summary>
/// Check whether the apphost file is a windows PE image by looking at the first few bytes.
/// </summary>
/// <param name="ms">The memory accessor which has the apphost file opened.</param>
/// <returns>true if the accessor represents a PE image, false otherwise.</returns>
internal static unsafe bool IsPEImage(MemoryStream ms)
{
var buffer = ms.GetBuffer();
fixed (byte* bytes = &buffer[0])
{
// https://en.wikipedia.org/wiki/Portable_Executable
// Validate that we're looking at Windows PE file
if (((UInt16*)bytes)[0] != PEFileSignature || ms.Length < PEHeaderPointerOffset + sizeof(UInt32))
{
return false;
}
return true;
}
}

public static bool IsPEImage(string filePath)
{
using (BinaryReader reader = new BinaryReader(File.OpenRead(filePath)))
{
if (reader.BaseStream.Length < PEHeaderPointerOffset + sizeof(UInt32))
{
return false;
}

ushort signature = reader.ReadUInt16();
return signature == PEFileSignature;
}
}

/// <summary>
/// This method will attempt to set the subsystem to GUI. The apphost file should be a windows PE file.
/// </summary>
/// <param name="ms">The memory accessor which has the apphost file opened.</param>
internal static unsafe void SetWindowsGraphicalUserInterfaceBit(MemoryStream ms)
{
var buffer = ms.GetBuffer();
fixed (byte* bytes = &buffer[0])
{
// https://en.wikipedia.org/wiki/Portable_Executable
UInt32 peHeaderOffset = ((UInt32*)(bytes + PEHeaderPointerOffset))[0];

if (ms.Length < peHeaderOffset + SubsystemOffset + sizeof(UInt16))
{
throw new FormatException("AppHost is not PE format.");
}

UInt16* subsystem = ((UInt16*)(bytes + peHeaderOffset + SubsystemOffset));

// https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format#windows-subsystem
// The subsystem of the prebuilt apphost should be set to CUI
if (subsystem[0] != WindowsCUISubsystem)
{
throw new FormatException("AppHost is not CUI.");
}

// Set the subsystem to GUI
subsystem[0] = WindowsGUISubsystem;
}
}

/// <summary>
/// This method will return the subsystem CUI/GUI value. The apphost file should be a windows PE file.
/// </summary>
/// <param name="ms">The memory accessor which has the apphost file opened.</param>
internal static unsafe UInt16 GetWindowsGraphicalUserInterfaceBit(MemoryStream ms)
{
var buffer = ms.GetBuffer();
fixed (byte* bytes = &buffer[0])
{
// https://en.wikipedia.org/wiki/Portable_Executable
UInt32 peHeaderOffset = ((UInt32*)(bytes + PEHeaderPointerOffset))[0];

if (ms.Length < peHeaderOffset + SubsystemOffset + sizeof(UInt16))
{
throw new FormatException("AppHost is not PE format.");
}

UInt16* subsystem = ((UInt16*)(bytes + peHeaderOffset + SubsystemOffset));

return subsystem[0];
}
}
}
31 changes: 28 additions & 3 deletions chibias.core/Internal/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,35 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace chibias.Internal;

[Flags]
internal enum chmodFlags
{
S_IRUSR = 0x100,
S_IWUSR = 0x80,
S_IXUSR = 0x40,
S_IRGRP = 0x20,
S_IWGRP = 0x10,
S_IXGRP = 0x8,
S_IROTH = 0x4,
S_IWOTH = 0x2,
S_IXOTH = 0x1,
}

internal static class Utilities
{
public static readonly bool IsInWindows =
Environment.OSVersion.Platform == PlatformID.Win32NT;

public const int EINTR = 4;

[DllImport("libc", SetLastError = true)]
public static extern int chmod(string path, chmodFlags mode);

#if NET40 || NET45
private static class ArrayEmpty<T>
{
Expand Down Expand Up @@ -214,13 +234,14 @@ public static bool TryParseFloat64(string word, out double value) =>
public static bool TryParseEnum<TEnum>(string word, out TEnum value)
where TEnum : struct, Enum =>
Enum.TryParse(word, true, out value);

public static bool UpdateBytes(
byte[] data, int length,
MemoryStream ms,
byte[] targetBytes, byte[] replaceBytes)
{
var data = ms.GetBuffer();
var index = 0;
while (index < length)
while (index < ms.Length)
{
var targetIndex = 0;
while (targetIndex < targetBytes.Length)
Expand All @@ -234,6 +255,10 @@ public static bool UpdateBytes(
if (targetIndex >= targetBytes.Length)
{
Array.Copy(replaceBytes, 0, data, index, replaceBytes.Length);
for (var index2 = replaceBytes.Length; index2 < targetBytes.Length; index2++)
{
data[index + index2] = 0x00;
}
return true;
}
index++;
Expand Down
1 change: 1 addition & 0 deletions chibias.core/chibias.core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<AssemblyName>chibias.core</AssemblyName>
<IsPackable>true</IsPackable>
<PackageId>chibias.core</PackageId>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit f9e1bc5

Please sign in to comment.