mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Remove use of code that involves reflection
This allows over 40% of the CoreRT native binary size to be removed by removing reflection capabilities. The "--noreflection" option for the build script can be used to build hactoolnet with no reflection. The Linux build won't always work because creating a new thread for the progress bar runs into some issue with EventSource being removed.
This commit is contained in:
parent
d3c95d14d3
commit
88983d39e5
6 changed files with 196 additions and 29 deletions
|
@ -34,6 +34,9 @@ namespace LibHacBuild
|
|||
[Parameter("Don't enable any size-reducing settings on native builds.")]
|
||||
public readonly bool Untrimmed;
|
||||
|
||||
[Parameter("Disable reflection in native builds.")]
|
||||
public readonly bool NoReflection;
|
||||
|
||||
[Solution("LibHac.sln")] readonly Solution _solution;
|
||||
|
||||
AbsolutePath SourceDirectory => RootDirectory / "src";
|
||||
|
@ -358,6 +361,11 @@ namespace LibHacBuild
|
|||
{
|
||||
string buildType = Untrimmed ? "native-untrimmed" : "native";
|
||||
|
||||
if (NoReflection)
|
||||
{
|
||||
buildType = "native-noreflection";
|
||||
}
|
||||
|
||||
DotNetPublishSettings publishSettings = new DotNetPublishSettings()
|
||||
.SetConfiguration(Configuration)
|
||||
.SetProject(HactoolnetProject)
|
||||
|
@ -368,7 +376,7 @@ namespace LibHacBuild
|
|||
|
||||
DotNetPublish(publishSettings);
|
||||
|
||||
if (EnvironmentInfo.IsUnix && !Untrimmed)
|
||||
if (EnvironmentInfo.IsUnix && !Untrimmed && !NoReflection)
|
||||
{
|
||||
File.Copy(CliNativeExe, CliNativeExe + "_unstripped", true);
|
||||
ProcessTasks.StartProcess("strip", CliNativeExe).AssertZeroExitCode();
|
||||
|
|
|
@ -20,19 +20,19 @@ namespace LibHac
|
|||
|
||||
private const int SdCardKeyIdCount = 3;
|
||||
|
||||
public byte[][] KeyblobKeys { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||
public byte[][] KeyblobMacKeys { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||
public byte[][] EncryptedKeyblobs { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0xB0);
|
||||
public byte[][] Keyblobs { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x90);
|
||||
public byte[][] KeyblobKeySources { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||
public byte[][] KeyblobKeys { get; } = Util.CreateJaggedByteArray(0x20, 0x10);
|
||||
public byte[][] KeyblobMacKeys { get; } = Util.CreateJaggedByteArray(0x20, 0x10);
|
||||
public byte[][] EncryptedKeyblobs { get; } = Util.CreateJaggedByteArray(0x20, 0xB0);
|
||||
public byte[][] Keyblobs { get; } = Util.CreateJaggedByteArray(0x20, 0x90);
|
||||
public byte[][] KeyblobKeySources { get; } = Util.CreateJaggedByteArray(0x20, 0x10);
|
||||
public byte[] KeyblobMacKeySource { get; } = new byte[0x10];
|
||||
public byte[][] TsecRootKeys { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||
public byte[][] MasterKekSources { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||
public byte[][] MasterKeks { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||
public byte[][] TsecRootKeys { get; } = Util.CreateJaggedByteArray(0x20, 0x10);
|
||||
public byte[][] MasterKekSources { get; } = Util.CreateJaggedByteArray(0x20, 0x10);
|
||||
public byte[][] MasterKeks { get; } = Util.CreateJaggedByteArray(0x20, 0x10);
|
||||
public byte[] MasterKeySource { get; } = new byte[0x10];
|
||||
public byte[][] MasterKeys { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||
public byte[][] Package1Keys { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||
public byte[][] Package2Keys { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||
public byte[][] MasterKeys { get; } = Util.CreateJaggedByteArray(0x20, 0x10);
|
||||
public byte[][] Package1Keys { get; } = Util.CreateJaggedByteArray(0x20, 0x10);
|
||||
public byte[][] Package2Keys { get; } = Util.CreateJaggedByteArray(0x20, 0x10);
|
||||
public byte[] Package2KeySource { get; } = new byte[0x10];
|
||||
public byte[] AesKekGenerationSource { get; } = new byte[0x10];
|
||||
public byte[] AesKeyGenerationSource { get; } = new byte[0x10];
|
||||
|
@ -46,29 +46,29 @@ namespace LibHac
|
|||
public byte[] TitleKekSource { get; } = new byte[0x10];
|
||||
public byte[] HeaderKekSource { get; } = new byte[0x10];
|
||||
public byte[] SdCardKekSource { get; } = new byte[0x10];
|
||||
public byte[][] SdCardKeySources { get; } = Util.CreateJaggedArray<byte[][]>(SdCardKeyIdCount, 0x20);
|
||||
public byte[][] SdCardKeySources { get; } = Util.CreateJaggedByteArray(SdCardKeyIdCount, 0x20);
|
||||
public byte[] HeaderKeySource { get; } = new byte[0x20];
|
||||
public byte[] HeaderKey { get; } = new byte[0x20];
|
||||
public byte[] XciHeaderKey { get; } = new byte[0x10];
|
||||
public byte[][] TitleKeks { get; } = Util.CreateJaggedArray<byte[][]>(0x20, 0x10);
|
||||
public byte[][][] KeyAreaKeys { get; } = Util.CreateJaggedArray<byte[][][]>(0x20, 3, 0x10);
|
||||
public byte[][] TitleKeks { get; } = Util.CreateJaggedByteArray(0x20, 0x10);
|
||||
public byte[][][] KeyAreaKeys { get; } = Util.CreateJaggedByteArray(0x20, 3, 0x10);
|
||||
public byte[] EticketRsaKek { get; } = new byte[0x10];
|
||||
public byte[] RetailSpecificAesKeySource { get; } = new byte[0x10];
|
||||
public byte[] PerConsoleKeySource { get; } = new byte[0x10];
|
||||
public byte[] BisKekSource { get; } = new byte[0x10];
|
||||
public byte[][] BisKeySource { get; } = Util.CreateJaggedArray<byte[][]>(4, 0x20);
|
||||
public byte[][] BisKeySource { get; } = Util.CreateJaggedByteArray(4, 0x20);
|
||||
public byte[] SslRsaKek { get; } = new byte[0x10];
|
||||
|
||||
// Device-specific keys
|
||||
public byte[] SecureBootKey { get; } = new byte[0x10];
|
||||
public byte[] TsecKey { get; } = new byte[0x10];
|
||||
public byte[] DeviceKey { get; } = new byte[0x10];
|
||||
public byte[][] BisKeys { get; } = Util.CreateJaggedArray<byte[][]>(4, 0x20);
|
||||
public byte[][] BisKeys { get; } = Util.CreateJaggedByteArray(4, 0x20);
|
||||
public byte[] SaveMacKey { get; } = new byte[0x10];
|
||||
public byte[] SaveMacSdCardKey { get; } = new byte[0x10];
|
||||
public byte[] SdSeed { get; } = new byte[0x10];
|
||||
public byte[][] SdCardKeySourcesSpecific { get; } = Util.CreateJaggedArray<byte[][]>(SdCardKeyIdCount, 0x20);
|
||||
public byte[][] SdCardKeys { get; } = Util.CreateJaggedArray<byte[][]>(SdCardKeyIdCount, 0x20);
|
||||
public byte[][] SdCardKeySourcesSpecific { get; } = Util.CreateJaggedByteArray(SdCardKeyIdCount, 0x20);
|
||||
public byte[][] SdCardKeys { get; } = Util.CreateJaggedByteArray(SdCardKeyIdCount, 0x20);
|
||||
|
||||
public RSAParameters EticketExtKeyRsa { get; set; }
|
||||
|
||||
|
|
|
@ -11,21 +11,30 @@ namespace LibHac
|
|||
{
|
||||
private const int MediaSize = 0x200;
|
||||
|
||||
public static T CreateJaggedArray<T>(params int[] lengths)
|
||||
public static byte[][] CreateJaggedByteArray(int len1, int len2)
|
||||
{
|
||||
return (T)InitializeJaggedArray(typeof(T).GetElementType(), 0, lengths);
|
||||
var array = new byte[len1][];
|
||||
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
array[i] = new byte[len2];
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private static object InitializeJaggedArray(Type type, int index, int[] lengths)
|
||||
public static byte[][][] CreateJaggedByteArray(int len1, int len2, int len3)
|
||||
{
|
||||
var array = Array.CreateInstance(type, lengths[index]);
|
||||
var array = new byte[len1][][];
|
||||
|
||||
Type elementType = type.GetElementType();
|
||||
if (elementType == null) return array;
|
||||
|
||||
for (int i = 0; i < lengths[index]; i++)
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
array.SetValue(InitializeJaggedArray(elementType, index + 1, lengths), i);
|
||||
array[i] = new byte[len2][];
|
||||
|
||||
for (int j = 0; j < array[i].Length; j++)
|
||||
{
|
||||
array[i][j] = new byte[len3];
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
|
|
142
src/hactoolnet/HomeFolder.cs
Normal file
142
src/hactoolnet/HomeFolder.cs
Normal file
|
@ -0,0 +1,142 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// Environment.GetFolderPath calls Enum.IsDefined which currently doesn't work under CoreRT (2020-06-27)
|
||||
// This code is copied from the .NET runtime with modifications to avoid that.
|
||||
// The downside is that it won't work in Linux unless the HOME environmental variable is set.
|
||||
|
||||
#if CORERT_NO_REFLECTION
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace hactoolnet
|
||||
{
|
||||
internal static class HomeFolder
|
||||
{
|
||||
public static string GetFolderPath(Environment.SpecialFolder folder) =>
|
||||
GetFolderPath(folder, Environment.SpecialFolderOption.None);
|
||||
|
||||
public static string GetFolderPath(Environment.SpecialFolder folder, Environment.SpecialFolderOption option)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return GetFolderPathCoreWin(folder, option);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetFolderPathCoreWithoutValidation(folder);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (CSIDL_PROFILE) The root users profile folder "%USERPROFILE%"
|
||||
/// ("%SystemDrive%\Users\%USERNAME%")
|
||||
/// </summary>
|
||||
internal const string Profile = "{5E6C858F-0E22-4760-9AFE-EA3317B67173}";
|
||||
|
||||
private static string GetFolderPathCoreWin(Environment.SpecialFolder folder,
|
||||
Environment.SpecialFolderOption option)
|
||||
{
|
||||
// We're using SHGetKnownFolderPath instead of SHGetFolderPath as SHGetFolderPath is
|
||||
// capped at MAX_PATH.
|
||||
//
|
||||
// Because we validate both of the input enums we shouldn't have to care about CSIDL and flag
|
||||
// definitions we haven't mapped. If we remove or loosen the checks we'd have to account
|
||||
// for mapping here (this includes tweaking as SHGetFolderPath would do).
|
||||
//
|
||||
// The only SpecialFolderOption defines we have are equivalent to KnownFolderFlags.
|
||||
|
||||
string folderGuid;
|
||||
|
||||
switch (folder)
|
||||
{
|
||||
case Environment.SpecialFolder.UserProfile:
|
||||
folderGuid = Profile;
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
return GetKnownFolderPath(folderGuid, option);
|
||||
}
|
||||
|
||||
private static string GetKnownFolderPath(string folderGuid, Environment.SpecialFolderOption option)
|
||||
{
|
||||
var folderId = new Guid(folderGuid);
|
||||
|
||||
int hr = Shell32.SHGetKnownFolderPath(folderId, (uint)option, IntPtr.Zero, out string path);
|
||||
if (hr != 0) // Not S_OK
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private static string GetFolderPathCoreWithoutValidation(Environment.SpecialFolder folder)
|
||||
{
|
||||
// All other paths are based on the XDG Base Directory Specification:
|
||||
// https://specifications.freedesktop.org/basedir-spec/latest/
|
||||
string home = null;
|
||||
try
|
||||
{
|
||||
home = GetHomeDirectory();
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Debug.Fail($"Unable to get home directory: {exc}");
|
||||
}
|
||||
|
||||
// Fall back to '/' when we can't determine the home directory.
|
||||
// This location isn't writable by non-root users which provides some safeguard
|
||||
// that the application doesn't write data which is meant to be private.
|
||||
if (string.IsNullOrEmpty(home))
|
||||
{
|
||||
home = "/";
|
||||
}
|
||||
|
||||
// TODO: Consider caching (or precomputing and caching) all subsequent results.
|
||||
// This would significantly improve performance for repeated access, at the expense
|
||||
// of not being responsive to changes in the underlying environment variables,
|
||||
// configuration files, etc.
|
||||
|
||||
switch (folder)
|
||||
{
|
||||
case Environment.SpecialFolder.UserProfile:
|
||||
case Environment.SpecialFolder.MyDocuments: // same value as Personal
|
||||
return home;
|
||||
}
|
||||
|
||||
// No known path for the SpecialFolder
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>Gets the current user's home directory.</summary>
|
||||
/// <returns>The path to the home directory, or null if it could not be determined.</returns>
|
||||
internal static string GetHomeDirectory()
|
||||
{
|
||||
// First try to get the user's home directory from the HOME environment variable.
|
||||
// This should work in most cases.
|
||||
string userHomeDirectory = Environment.GetEnvironmentVariable("HOME");
|
||||
if (!string.IsNullOrEmpty(userHomeDirectory))
|
||||
return userHomeDirectory;
|
||||
|
||||
throw new NotSupportedException(
|
||||
"Unable to get your home directory. Please report this on the LibHac GitHub repository." +
|
||||
"You can use the netcore build in the GitHub repo's releases for now.");
|
||||
}
|
||||
}
|
||||
|
||||
internal class Shell32
|
||||
{
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Unicode, BestFitMapping = false)]
|
||||
internal static extern int SHGetKnownFolderPath(
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid rfid,
|
||||
uint dwFlags,
|
||||
IntPtr hToken,
|
||||
out string ppszPath);
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -37,7 +37,10 @@ namespace hactoolnet
|
|||
Console.Error.WriteLine($"\nERROR: {ex.Message}\n");
|
||||
|
||||
Console.Error.WriteLine("Additional information:");
|
||||
#if !CORERT_NO_REFLECTION
|
||||
Console.Error.WriteLine(ex.GetType().FullName);
|
||||
#endif
|
||||
|
||||
Console.Error.WriteLine(ex.StackTrace);
|
||||
}
|
||||
|
||||
|
@ -167,7 +170,11 @@ namespace hactoolnet
|
|||
{
|
||||
string keyFileName = ctx.Options.UseDevKeys ? "dev.keys" : "prod.keys";
|
||||
|
||||
#if CORERT_NO_REFLECTION
|
||||
string home = HomeFolder.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
#else
|
||||
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
#endif
|
||||
string homeKeyFile = Path.Combine(home, ".switch", keyFileName);
|
||||
string homeTitleKeyFile = Path.Combine(home, ".switch", "title.keys");
|
||||
string homeConsoleKeyFile = Path.Combine(home, ".switch", "console.keys");
|
||||
|
|
|
@ -36,9 +36,10 @@
|
|||
<IlcArg Include="--removefeature:EventSource" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- No-reflection mode doesn't work yet -->
|
||||
<!-- No-reflection mode might not work on Linux if the HOME environment variable is not set -->
|
||||
<PropertyGroup Condition=" '$(BuildType)' == 'native-noreflection' ">
|
||||
<IlcDisableReflection>true</IlcDisableReflection>
|
||||
<DefineConstants>CORERT_NO_REFLECTION;$(DefineConstants)</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
Loading…
Reference in a new issue