diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs index 3afdb5702..8871cbe7d 100644 --- a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs @@ -148,6 +148,92 @@ namespace Ryujinx.HLE.HOS.Kernel } } + public long MapProcessCodeMemory(long Dst, long Src, long Size) + { + lock (Blocks) + { + long PagesCount = Size / PageSize; + + bool Success = IsUnmapped(Dst, Size); + + Success &= CheckRange( + Src, + Size, + MemoryState.Mask, + MemoryState.Heap, + MemoryPermission.Mask, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (Success) + { + long PA = CpuMemory.GetPhysicalAddress(Src); + + InsertBlock(Dst, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute); + InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.None); + + CpuMemory.Map(Dst, PA, Size); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long UnmapProcessCodeMemory(long Dst, long Src, long Size) + { + lock (Blocks) + { + long PagesCount = Size / PageSize; + + bool Success = CheckRange( + Dst, + Size, + MemoryState.Mask, + MemoryState.CodeStatic, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + Success &= CheckRange( + Src, + Size, + MemoryState.Mask, + MemoryState.Heap, + MemoryPermission.Mask, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (Success) + { + InsertBlock(Dst, PagesCount, MemoryState.Unmapped); + InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite); + + CpuMemory.Unmap(Dst, Size); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission) { long PagesCount = Size / PageSize; @@ -755,6 +841,18 @@ namespace Ryujinx.HLE.HOS.Kernel } } + public bool HleIsUnmapped(long Position, long Size) + { + bool Result = false; + + lock (Blocks) + { + Result = IsUnmapped(Position, Size); + } + + return Result; + } + private bool IsUnmapped(long Position, long Size) { return CheckRange( diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs index 3817f5619..ab0ab18ba 100644 --- a/Ryujinx.HLE/HOS/Process.cs +++ b/Ryujinx.HLE/HOS/Process.cs @@ -106,13 +106,37 @@ namespace Ryujinx.HLE.HOS throw new ObjectDisposedException(nameof(Process)); } - Device.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ImageBase:x16}."); + long ImageEnd = LoadProgram(Program, ImageBase); - Executable Executable = new Executable(Program, MemoryManager, Memory, ImageBase); + ImageBase = IntUtils.AlignUp(ImageEnd, KMemoryManager.PageSize); + } + + public long LoadProgram(IExecutable Program, long ExecutableBase) + { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + + Device.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ExecutableBase:x16}."); + + Executable Executable = new Executable(Program, MemoryManager, Memory, ExecutableBase); Executables.Add(Executable); - ImageBase = IntUtils.AlignUp(Executable.ImageEnd, KMemoryManager.PageSize); + return Executable.ImageEnd; + } + + public void RemoveProgram(long ExecutableBase) + { + foreach (Executable Executable in Executables) + { + if (Executable.ImageBase == ExecutableBase) + { + Executables.Remove(Executable); + break; + } + } } public void SetEmptyArgs() diff --git a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs new file mode 100644 index 000000000..4d595fde2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs @@ -0,0 +1,457 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.HLE.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +using static Ryujinx.HLE.HOS.ErrorCode; + +namespace Ryujinx.HLE.HOS.Services.Ldr +{ + [StructLayout(LayoutKind.Explicit, Size = 0x350)] + unsafe struct NrrHeader + { + [FieldOffset(0)] + public uint Magic; + + [FieldOffset(0x10)] + public ulong TitleIdMask; + + [FieldOffset(0x18)] + public ulong TitleIdPattern; + + [FieldOffset(0x30)] + public fixed byte Modulus[0x100]; + + [FieldOffset(0x130)] + public fixed byte FixedKeySignature[0x100]; + + [FieldOffset(0x230)] + public fixed byte NrrSignature[0x100]; + + [FieldOffset(0x330)] + public ulong TitleIdMin; + + [FieldOffset(0x338)] + public uint NrrSize; + + [FieldOffset(0x340)] + public uint HashOffset; + + [FieldOffset(0x344)] + public uint HashCount; + } + + class NrrInfo + { + public NrrHeader Header { get; private set; } + public List Hashes { get; private set; } + public long NrrAddress { get; private set; } + + public NrrInfo(long NrrAddress, NrrHeader Header, List Hashes) + { + this.NrrAddress = NrrAddress; + this.Header = Header; + this.Hashes = Hashes; + } + } + + class NroInfo + { + public Nro Executable { get; private set; } + public byte[] Hash { get; private set; } + public long NroAddress { get; private set; } + public long TotalSize { get; private set; } + public long NroMappedAddress { get; set; } + + public NroInfo(Nro Executable, byte[] Hash, long TotalSize) + { + this.Executable = Executable; + this.Hash = Hash; + this.TotalSize = TotalSize; + } + } + + class IRoInterface : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + private const int MaxNrr = 0x40; + private const int MaxNro = 0x40; + + private const uint NrrMagic = 0x3052524E; + private const uint NroMagic = 0x304F524E; + + private List NrrInfos; + private List NroInfos; + + private bool IsInitialized; + + public IRoInterface() + { + m_Commands = new Dictionary() + { + { 0, LoadNro }, + { 1, UnloadNro }, + { 2, LoadNrr }, + { 3, UnloadNrr }, + { 4, Initialize }, + }; + + NrrInfos = new List(MaxNrr); + NroInfos = new List(MaxNro); + } + + private long ParseNrr(out NrrInfo NrrInfo, ServiceCtx Context, long NrrAddress, long NrrSize) + { + NrrInfo = null; + + if (NrrSize == 0 || NrrAddress + NrrSize <= NrrAddress || (NrrSize & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + else if ((NrrAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + StructReader Reader = new StructReader(Context.Memory, NrrAddress); + NrrHeader Header = Reader.Read(); + + if (Header.Magic != NrrMagic) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNrr); + } + else if (Header.NrrSize != NrrSize) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + + List Hashes = new List(); + + for (int i = 0; i < Header.HashCount; i++) + { + Hashes.Add(Context.Memory.ReadBytes(NrrAddress + Header.HashOffset + (i * 0x20), 0x20)); + } + + NrrInfo = new NrrInfo(NrrAddress, Header, Hashes); + + return 0; + } + + public bool IsNroHashPresent(byte[] NroHash) + { + foreach (NrrInfo Info in NrrInfos) + { + foreach (byte[] Hash in Info.Hashes) + { + if (Hash.SequenceEqual(NroHash)) + { + return true; + } + } + } + + return false; + } + + public bool IsNroLoaded(byte[] NroHash) + { + foreach (NroInfo Info in NroInfos) + { + if (Info.Hash.SequenceEqual(NroHash)) + { + return true; + } + } + + return false; + } + + public long ParseNro(out NroInfo Res, ServiceCtx Context, long NroHeapAddress, long NroSize, long BssHeapAddress, long BssSize) + { + Res = null; + + if (NroInfos.Count >= MaxNro) + { + return MakeError(ErrorModule.Loader, LoaderErr.MaxNro); + } + else if (NroSize == 0 || NroHeapAddress + NroSize <= NroHeapAddress || (NroSize & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + else if (BssSize != 0 && (BssHeapAddress + BssSize) <= BssHeapAddress) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + else if ((NroHeapAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + uint Magic = Context.Memory.ReadUInt32(NroHeapAddress + 0x10); + uint NroFileSize = Context.Memory.ReadUInt32(NroHeapAddress + 0x18); + + if (Magic != NroMagic || NroSize != NroFileSize) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + byte[] NroData = Context.Memory.ReadBytes(NroHeapAddress, NroSize); + byte[] NroHash = null; + + MemoryStream Stream = new MemoryStream(NroData); + + using (SHA256 Hasher = SHA256.Create()) + { + NroHash = Hasher.ComputeHash(Stream); + } + + if (!IsNroHashPresent(NroHash)) + { + return MakeError(ErrorModule.Loader, LoaderErr.NroHashNotPresent); + } + + if (IsNroLoaded(NroHash)) + { + return MakeError(ErrorModule.Loader, LoaderErr.NroAlreadyLoaded); + } + + Stream.Position = 0; + + Nro Executable = new Nro(Stream, "memory", NroHeapAddress, BssHeapAddress); + + // check if everything is page align. + if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0 + || (Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + // check if everything is contiguous. + if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length + || Executable.DataOffset != Executable.ROOffset + Executable.RO.Length + || NroFileSize != Executable.DataOffset + Executable.Data.Length) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + // finally check the bss size match. + if (Executable.BssSize != BssSize) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + Res = new NroInfo(Executable, NroHash, Executable.Text.Length + Executable.RO.Length + Executable.Data.Length + Executable.BssSize); + + return 0; + } + + private long MapNro(ServiceCtx Context, NroInfo Info, out long NroMappedAddress) + { + NroMappedAddress = 0; + long TargetAddress = Context.Process.MemoryManager.AddrSpaceStart; + + long HeapRegionStart = Context.Process.MemoryManager.HeapRegionStart; + long HeapRegionEnd = Context.Process.MemoryManager.HeapRegionEnd; + + long MapRegionStart = Context.Process.MemoryManager.MapRegionStart; + long MapRegionEnd = Context.Process.MemoryManager.MapRegionEnd; + + while (true) + { + if (TargetAddress + Info.TotalSize >= Context.Process.MemoryManager.AddrSpaceEnd) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState); + } + + bool IsValidAddress = !(HeapRegionStart > 0 && HeapRegionStart <= TargetAddress + Info.TotalSize - 1 + && TargetAddress <= HeapRegionEnd - 1) + && !(MapRegionStart > 0 + && MapRegionStart <= TargetAddress + Info.TotalSize - 1 + && TargetAddress <= MapRegionEnd - 1); + + if (IsValidAddress && Context.Process.MemoryManager.HleIsUnmapped(TargetAddress, Info.TotalSize)) + { + break; + } + + TargetAddress += 0x1000; + } + + Context.Process.LoadProgram(Info.Executable, TargetAddress); + + Info.NroMappedAddress = TargetAddress; + NroMappedAddress = TargetAddress; + + return 0; + } + + private long RemoveNrrInfo(long NrrAddress) + { + foreach (NrrInfo Info in NrrInfos) + { + if (Info.NrrAddress == NrrAddress) + { + NrrInfos.Remove(Info); + + return 0; + } + } + + return MakeError(ErrorModule.Loader, LoaderErr.BadNrrAddress); + } + + private long RemoveNroInfo(ServiceCtx Context, long NroMappedAddress, long NroHeapAddress) + { + foreach (NroInfo Info in NroInfos) + { + if (Info.NroMappedAddress == NroMappedAddress && Info.Executable.SourceAddress == NroHeapAddress) + { + NroInfos.Remove(Info); + + Context.Process.RemoveProgram(Info.NroMappedAddress); + + long Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress, Info.Executable.SourceAddress, Info.TotalSize - Info.Executable.BssSize); + + if (Result == 0 && Info.Executable.BssSize != 0) + { + Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress + Info.TotalSize - Info.Executable.BssSize, Info.Executable.BssAddress, Info.Executable.BssSize); + } + + return Result; + } + } + + return MakeError(ErrorModule.Loader, LoaderErr.BadNroAddress); + } + + // LoadNro(u64, u64, u64, u64, u64, pid) -> u64 + public long LoadNro(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + // Zero + Context.RequestData.ReadUInt64(); + + long NroHeapAddress = Context.RequestData.ReadInt64(); + long NroSize = Context.RequestData.ReadInt64(); + long BssHeapAddress = Context.RequestData.ReadInt64(); + long BssSize = Context.RequestData.ReadInt64(); + + long NroMappedAddress = 0; + + if (IsInitialized) + { + NroInfo Info; + + Result = ParseNro(out Info, Context, NroHeapAddress, NroSize, BssHeapAddress, BssSize); + + if (Result == 0) + { + Result = MapNro(Context, Info, out NroMappedAddress); + + if (Result == 0) + { + NroInfos.Add(Info); + } + } + } + + Context.ResponseData.Write(NroMappedAddress); + + return Result; + } + + // UnloadNro(u64, u64, pid) + public long UnloadNro(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + long NroMappedAddress = Context.RequestData.ReadInt64(); + long NroHeapAddress = Context.RequestData.ReadInt64(); + + if (IsInitialized) + { + if ((NroMappedAddress & 0xFFF) != 0 || (NroHeapAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + Result = RemoveNroInfo(Context, NroMappedAddress, NroHeapAddress); + } + + return Result; + } + + // LoadNrr(u64, u64, u64, pid) + public long LoadNrr(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + // Zero + Context.RequestData.ReadUInt64(); + + long NrrAddress = Context.RequestData.ReadInt64(); + long NrrSize = Context.RequestData.ReadInt64(); + + if (IsInitialized) + { + NrrInfo Info; + Result = ParseNrr(out Info, Context, NrrAddress, NrrSize); + + if(Result == 0) + { + if (NrrInfos.Count >= MaxNrr) + { + Result = MakeError(ErrorModule.Loader, LoaderErr.MaxNrr); + } + else + { + NrrInfos.Add(Info); + } + } + } + + return Result; + } + + // UnloadNrr(u64, u64, pid) + public long UnloadNrr(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + // Zero + Context.RequestData.ReadUInt64(); + + long NrrHeapAddress = Context.RequestData.ReadInt64(); + + if (IsInitialized) + { + if ((NrrHeapAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + Result = RemoveNrrInfo(NrrHeapAddress); + } + + return Result; + } + + // Initialize(u64, pid, KObject) + public long Initialize(ServiceCtx Context) + { + // TODO: we actually ignore the pid and process handle receive, we will need to use them when we will have multi process support. + IsInitialized = true; + + return 0; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs b/Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs new file mode 100644 index 000000000..ba77a5cc1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.HLE.HOS.Services.Ldr +{ + static class LoaderErr + { + public const int InvalidMemoryState = 51; + public const int InvalidNro = 52; + public const int InvalidNrr = 53; + public const int MaxNro = 55; + public const int MaxNrr = 56; + public const int NroAlreadyLoaded = 57; + public const int NroHashNotPresent = 54; + public const int UnalignedAddress = 81; + public const int BadSize = 82; + public const int BadNroAddress = 84; + public const int BadNrrAddress = 85; + public const int BadInitialization = 87; + } +} diff --git a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs index fd5a06e6d..f701dd053 100644 --- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs +++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs @@ -7,6 +7,7 @@ using Ryujinx.HLE.HOS.Services.Caps; using Ryujinx.HLE.HOS.Services.FspSrv; using Ryujinx.HLE.HOS.Services.Hid; using Ryujinx.HLE.HOS.Services.Irs; +using Ryujinx.HLE.HOS.Services.Ldr; using Ryujinx.HLE.HOS.Services.Lm; using Ryujinx.HLE.HOS.Services.Mm; using Ryujinx.HLE.HOS.Services.Nfp; @@ -100,6 +101,9 @@ namespace Ryujinx.HLE.HOS.Services case "irs": return new IIrSensorServer(); + case "ldr:ro": + return new IRoInterface(); + case "lm": return new ILogService(); diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs index a9850e4af..3c63af141 100644 --- a/Ryujinx.HLE/Loaders/Executable.cs +++ b/Ryujinx.HLE/Loaders/Executable.cs @@ -49,21 +49,49 @@ namespace Ryujinx.HLE.Loaders long DataPosition = ImageBase + (uint)Exe.DataOffset; long TextSize = (uint)IntUtils.AlignUp(Exe.Text.Length, KMemoryManager.PageSize); - long ROSize = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize); + long ROSize = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize); long DataSize = (uint)IntUtils.AlignUp(Exe.Data.Length, KMemoryManager.PageSize); + long BssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize); - long DataAndBssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize) + DataSize; + long DataAndBssSize = BssSize + DataSize; ImageEnd = DataPosition + DataAndBssSize; - MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize); + if (Exe.SourceAddress == 0) + { + MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize); - MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); - MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite); + MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); + MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite); - Memory.WriteBytes(TextPosition, Exe.Text); - Memory.WriteBytes(ROPosition, Exe.RO); - Memory.WriteBytes(DataPosition, Exe.Data); + Memory.WriteBytes(TextPosition, Exe.Text); + Memory.WriteBytes(ROPosition, Exe.RO); + Memory.WriteBytes(DataPosition, Exe.Data); + } + else + { + long Result = MemoryManager.MapProcessCodeMemory(TextPosition, Exe.SourceAddress, TextSize + ROSize + DataSize); + + if (Result != 0) + { + throw new InvalidOperationException(); + } + + MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); + MemoryManager.SetProcessMemoryPermission(DataPosition, DataSize, MemoryPermission.ReadAndWrite); + + if (Exe.BssAddress != 0 && Exe.BssSize != 0) + { + Result = MemoryManager.MapProcessCodeMemory(DataPosition + DataSize, Exe.BssAddress, BssSize); + + if (Result != 0) + { + throw new InvalidOperationException(); + } + + MemoryManager.SetProcessMemoryPermission(DataPosition + DataSize, BssSize, MemoryPermission.ReadAndWrite); + } + } if (Exe.Mod0Offset == 0) { diff --git a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs index 44bad6149..6f0952abd 100644 --- a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs +++ b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs @@ -8,6 +8,9 @@ namespace Ryujinx.HLE.Loaders.Executables byte[] RO { get; } byte[] Data { get; } + long SourceAddress { get; } + long BssAddress { get; } + int Mod0Offset { get; } int TextOffset { get; } int ROOffset { get; } diff --git a/Ryujinx.HLE/Loaders/Executables/Nro.cs b/Ryujinx.HLE/Loaders/Executables/Nro.cs index 0b5068d7b..6015da213 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nro.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nro.cs @@ -16,9 +16,14 @@ namespace Ryujinx.HLE.Loaders.Executables public int DataOffset { get; private set; } public int BssSize { get; private set; } - public Nro(Stream Input, string FilePath) + public long SourceAddress { get; private set; } + public long BssAddress { get; private set; } + + public Nro(Stream Input, string FilePath, long SourceAddress = 0, long BssAddress = 0) { - this.FilePath = FilePath; + this.FilePath = FilePath; + this.SourceAddress = SourceAddress; + this.BssAddress = BssAddress; BinaryReader Reader = new BinaryReader(Input); diff --git a/Ryujinx.HLE/Loaders/Executables/Nso.cs b/Ryujinx.HLE/Loaders/Executables/Nso.cs index fef9c4b85..c7b48a5f3 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nso.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nso.cs @@ -18,6 +18,9 @@ namespace Ryujinx.HLE.Loaders.Executables public int DataOffset { get; private set; } public int BssSize { get; private set; } + public long SourceAddress { get; private set; } + public long BssAddress { get; private set; } + [Flags] private enum NsoFlags { @@ -33,6 +36,9 @@ namespace Ryujinx.HLE.Loaders.Executables { this.FilePath = FilePath; + SourceAddress = 0; + BssAddress = 0; + BinaryReader Reader = new BinaryReader(Input); Input.Seek(0, SeekOrigin.Begin); diff --git a/Ryujinx.HLE/Logging/LogClass.cs b/Ryujinx.HLE/Logging/LogClass.cs index 490501341..0458c75f8 100644 --- a/Ryujinx.HLE/Logging/LogClass.cs +++ b/Ryujinx.HLE/Logging/LogClass.cs @@ -23,6 +23,7 @@ namespace Ryujinx.HLE.Logging ServiceFs, ServiceHid, ServiceIrs, + ServiceLdr, ServiceLm, ServiceMm, ServiceNfp,