diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 09c54396ea..80395c59d2 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -6,15 +6,10 @@ - - - + + diff --git a/BizHawk.Emulation.Cores/Waterbox/ElfRunner.cs b/BizHawk.Emulation.Cores/Waterbox/ElfRunner.cs deleted file mode 100644 index 79f277ff43..0000000000 --- a/BizHawk.Emulation.Cores/Waterbox/ElfRunner.cs +++ /dev/null @@ -1,472 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using ELFSharp.ELF; -using ELFSharp.ELF.Sections; -using ELFSharp.ELF.Segments; -using System.Reflection; -using BizHawk.Common; -using System.Security.Cryptography; -using System.IO; -using BizHawk.Emulation.Common; -using BizHawk.BizInvoke; - -namespace BizHawk.Emulation.Cores.Waterbox -{ - public sealed class ElfRunner : Swappable, IImportResolver, IBinaryStateable - { - // TODO: a lot of things only work with our elves and aren't fully generalized - - private ELF _elf; - private byte[] _elfhash; - - /// - /// executable is loaded here - /// - private MemoryBlockBase _base; - /// - /// standard malloc() heap - /// - private Heap _heap; - - /// - /// sealed heap (writable only during init) - /// - private Heap _sealedheap; - - /// - /// invisible heap (not savestated, use with care) - /// - private Heap _invisibleheap; - - private long _loadoffset; - private Dictionary> _symdict; - private List> _symlist; - - /// - /// everything to clean up at dispose time - /// - private List _disposeList = new List(); - - private ulong GetHeapStart(ulong prevend) - { - // if relocatable, we won't have constant pointers, so put the heap anywhere - // otherwise, put the heap at a canonical location aligned 1MB from the end of the elf, then incremented 16MB - ulong heapstart = HasRelocations() ? 0 : ((prevend - 1) | 0xfffff) + 0x1000001; - return heapstart; - } - - public ElfRunner(string filename, long heapsize, long sealedheapsize, long invisibleheapsize) - { - using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) - { - _elfhash = WaterboxUtils.Hash(fs); - } - - // todo: hack up this baby to take Streams - _elf = ELFReader.Load(filename); - - var loadsegs = _elf.Segments.Where(s => s.Type == SegmentType.Load); - - long orig_start = loadsegs.Min(s => s.Address); - orig_start &= ~(Environment.SystemPageSize - 1); - long orig_end = loadsegs.Max(s => s.Address + s.Size); - if (HasRelocations()) - { - _base = MemoryBlockBase.CallPlatformCtor((ulong)(orig_end - orig_start)); - _loadoffset = (long) _base.AddressRange.Start - orig_start; - Initialize(0); - } - else - { - Initialize((ulong)orig_start); - _base = MemoryBlockBase.CallPlatformCtor((ulong)orig_start, (ulong)(orig_end - orig_start)); - _loadoffset = 0; - Enter(); - } - - try - { - _disposeList.Add(_base); - AddMemoryBlock(_base, "elf"); - _base.Activate(); - _base.Protect(_base.AddressRange.Start, _base.Size, MemoryBlockBase.Protection.RW); - - foreach (var seg in loadsegs) - { - var data = seg.GetContents(); - Marshal.Copy(data, 0, Z.SS(seg.Address + _loadoffset), data.Length); - } - RegisterSymbols(); - ProcessRelocations(); - - _base.Protect(_base.AddressRange.Start, _base.Size, MemoryBlockBase.Protection.R); - - foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Allocatable) != 0)) - { - if ((sec.Flags & SectionFlags.Executable) != 0) - _base.Protect((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, MemoryBlockBase.Protection.RX); - else if ((sec.Flags & SectionFlags.Writable) != 0) - _base.Protect((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, MemoryBlockBase.Protection.RW); - } - - ulong end = _base.AddressRange.EndInclusive + 1; - - if (heapsize > 0) - { - _heap = new Heap(GetHeapStart(end), (ulong)heapsize, "sbrk-heap"); - _heap.Memory.Activate(); - end = _heap.Memory.AddressRange.EndInclusive + 1; - _disposeList.Add(_heap); - AddMemoryBlock(_heap.Memory, "sbrk - heap"); - } - - if (sealedheapsize > 0) - { - _sealedheap = new Heap(GetHeapStart(end), (ulong)sealedheapsize, "sealed-heap"); - _sealedheap.Memory.Activate(); - end = _sealedheap.Memory.AddressRange.EndInclusive + 1; - _disposeList.Add(_sealedheap); - AddMemoryBlock(_sealedheap.Memory, "sealed-heap"); - } - - if (invisibleheapsize > 0) - { - _invisibleheap = new Heap(GetHeapStart(end), (ulong)invisibleheapsize, "invisible-heap"); - _invisibleheap.Memory.Activate(); - end = _invisibleheap.Memory.AddressRange.EndInclusive + 1; - _disposeList.Add(_invisibleheap); - AddMemoryBlock(_invisibleheap.Memory, "invisible-heap"); - } - - ConnectAllClibPatches(); - Console.WriteLine($"Loaded {filename}@{_base.AddressRange.Start:X16}"); - foreach (var sec in _elf.Sections.Where(s => s.LoadAddress != 0)) - { - Console.WriteLine(" {0}@{1:X16}, size {2}", sec.Name.PadLeft(20), sec.LoadAddress + _loadoffset, sec.Size.ToString().PadLeft(12)); - } - - PrintTopSavableSymbols(); - } - catch - { - Dispose(); - throw; - } - finally - { - Exit(); - } - } - - private void PrintTopSavableSymbols() - { - Console.WriteLine("Top savestate symbols:"); - foreach (var text in _symlist - .Where(s => s.PointedSection != null && (s.PointedSection.Flags & SectionFlags.Writable) != 0) - .OrderByDescending(s => s.Size) - .Take(30) - .Select(s => $"{s.Name} size {s.Size}")) - { - Console.WriteLine(text); - } - } - - private class Elf32_Rel - { - public long Address; - public byte Type; - public int SymbolIdx; - public long Addend; - - public Elf32_Rel(byte[] data, int start, int len) - { - if (len == 8 || len == 12) - { - Address = BitConverter.ToInt32(data, start); - Type = data[start + 4]; - SymbolIdx = (int)(BitConverter.ToUInt32(data, start + 4) >> 8); - Addend = data.Length == 12 ? BitConverter.ToInt32(data, start + 8) : 0; - } - else - { - throw new InvalidOperationException(); - } - } - } - - private bool HasRelocations() - { - return _elf.Sections.Any(s => s.Name.StartsWith(".rel")); - } - - // elfsharp does not read relocation tables, so there - private void ProcessRelocations() - { - // todo: amd64 - foreach (var rel in _elf.Sections.Where(s => s.Name.StartsWith(".rel"))) - { - byte[] data = rel.GetContents(); - var symbols = Enumerable.Range(0, data.Length / 8) - .Select(i => new Elf32_Rel(data, i * 8, 8)); - foreach (var symbol in symbols) - { - ApplyRelocation(symbol); - } - } - } - private void ApplyRelocation(Elf32_Rel rel) - { - // http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf - // this is probably mostly wrong - - long val = 0; - long A = rel.Addend; - // since all symbols were moved by the same amount, just add _loadoffset here - long S = _symlist[rel.SymbolIdx].Value + _loadoffset; - long B = _loadoffset; - switch (rel.Type) - { - case 0: val = 0; break; - case 1: val = S + A; break; - case 2: throw new NotImplementedException(); - case 3: throw new NotImplementedException(); - case 4: throw new NotImplementedException(); - case 5: val = 0; break; - case 6: val = S; break; - case 7: val = S; break; - case 8: val = B + A; break; - case 9: throw new NotImplementedException(); - case 10: throw new NotImplementedException(); - default: throw new InvalidOperationException(); - } - byte[] tmp = new byte[4]; - Marshal.Copy((IntPtr)(rel.Address + _loadoffset), tmp, 0, 4); - long currentVal = BitConverter.ToUInt32(tmp, 0); - tmp = BitConverter.GetBytes((uint)(currentVal + val)); - Marshal.Copy(tmp, 0, (IntPtr)(rel.Address + _loadoffset), 4); - } - - private void RegisterSymbols() - { - _symlist = ((ISymbolTable) _elf.GetSection(".symtab")).Entries - .Cast>() - .ToList(); - - // when there are duplicate names, don't register either in the dictionary - _symdict = _symlist - .GroupBy(e => e.Name) - .Where(g => g.Count() == 1) - .ToDictionary(g => g.Key, g => g.First()); - } - - public void Seal() - { - Enter(); - try - { - _sealedheap.Seal(); - } - finally - { - Exit(); - } - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - foreach (var d in _disposeList) - d.Dispose(); - _disposeList.Clear(); - PurgeMemoryBlocks(); - _base = null; - _heap = null; - _sealedheap = null; - _invisibleheap = null; - } - } - - #region clib monkeypatches - - // our clib expects a few function pointers to be defined for it - - /// - /// abort() / other abnormal situation - /// - /// desired exit code - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void Trap_D(); - - /// - /// expand heap - /// - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate IntPtr Sbrk_D(UIntPtr n); - - /// - /// output a string - /// - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void DebugPuts_D(string s); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate IntPtr SbrkSealed_D(UIntPtr n); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate IntPtr SbrkInvisible_D(UIntPtr n); - - [CLibPatch("_ecl_trap")] - private void Trap() - { - throw new InvalidOperationException("Waterbox code trapped!"); - } - - [CLibPatch("_ecl_sbrk")] - private IntPtr Sbrk(UIntPtr n) - { - return Z.US(_heap.Allocate((ulong)n, 1)); - } - - [CLibPatch("_ecl_debug_puts")] - private void DebugPuts(string s) - { - Console.WriteLine("Waterbox debug puts: {0}", s); - } - - [CLibPatch("_ecl_sbrk_sealed")] - private IntPtr SbrkSealed(UIntPtr n) - { - return Z.US(_sealedheap.Allocate((ulong)n, 16)); - } - - [CLibPatch("_ecl_sbrk_invisible")] - private IntPtr SbrkInvisible(UIntPtr n) - { - return Z.US(_invisibleheap.Allocate((ulong)n, 16)); - } - - /// - /// list of delegates that need to not be GCed - /// - private List _delegates = new List(); - - private void ConnectAllClibPatches() - { - _delegates.Clear(); // in case we're reconnecting - - var methods = GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Where(mi => mi.GetCustomAttributes(typeof(CLibPatchAttribute), false).Length > 0); - foreach (var mi in methods) - { - var delegateType = GetType().GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic) - .Single(t => t.Name == mi.Name + "_D"); - var del = Delegate.CreateDelegate(delegateType, this, mi); - IntPtr ptr = Marshal.GetFunctionPointerForDelegate(del); - _delegates.Add(del); - var sym = _symdict[((CLibPatchAttribute)mi.GetCustomAttributes(typeof(CLibPatchAttribute), false)[0]).NativeName]; - if (sym.Size != IntPtr.Size) - throw new InvalidOperationException("Unexpected function pointer size patching clib!"); - IntPtr dest = Z.SS(sym.Value + _loadoffset); - Marshal.Copy(new[] { ptr }, 0, dest, 1); - } - } - - [AttributeUsage(AttributeTargets.Method)] - private class CLibPatchAttribute : Attribute - { - public string NativeName { get; } - public CLibPatchAttribute(string nativeName) - { - NativeName = nativeName; - } - } - - #endregion - - public IntPtr? GetProcAddrOrNull(string entryPoint) => _symdict.TryGetValue(entryPoint, out var sym) ? Z.SS(sym.Value + _loadoffset) : (IntPtr?) null; - - public IntPtr GetProcAddrOrThrow(string entryPoint) => GetProcAddrOrNull(entryPoint) ?? throw new InvalidOperationException($"could not find {entryPoint} in exports"); - - #region state - - const ulong MAGIC = 0xb00b1e5b00b1e569; - - public void SaveStateBinary(BinaryWriter bw) - { - Enter(); - try - { - bw.Write(MAGIC); - bw.Write(_elfhash); - bw.Write(_loadoffset); - foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Writable) != 0)) - { - var ms = _base.GetStream((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, false); - bw.Write(sec.Size); - ms.CopyTo(bw.BaseStream); - } - - _heap?.SaveStateBinary(bw); - _sealedheap?.SaveStateBinary(bw); - bw.Write(MAGIC); - } - finally - { - Exit(); - } - } - - public void LoadStateBinary(BinaryReader br) - { - Enter(); - try - { - if (br.ReadUInt64() != MAGIC) - throw new InvalidOperationException("Magic not magic enough!"); - if (!br.ReadBytes(_elfhash.Length).SequenceEqual(_elfhash)) - throw new InvalidOperationException("Elf changed disguise!"); - if (br.ReadInt64() != _loadoffset) - throw new InvalidOperationException("Trickys elves moved on you!"); - - foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Writable) != 0)) - { - var len = br.ReadInt64(); - if (sec.Size != len) - throw new InvalidOperationException("Unexpected section size for " + sec.Name); - var ms = _base.GetStream((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, true); - WaterboxUtils.CopySome(br.BaseStream, ms, len); - } - - _heap?.LoadStateBinary(br); - _sealedheap?.LoadStateBinary(br); - if (br.ReadUInt64() != MAGIC) - throw new InvalidOperationException("Magic not magic enough!"); - - // the syscall trampolines were overwritten in loadstate (they're in .bss), and if we're cross-session, - // are no longer valid. cores must similarly resend any external pointers they gave the core. - ConnectAllClibPatches(); - } - finally - { - Exit(); - } - } - - #endregion - - #region utils - - private byte[] HashSection(ulong ptr, ulong len) - { - using var h = SHA1.Create(); - var ms = _base.GetStream(ptr, len, false); - return h.ComputeHash(ms); - } - - #endregion - } -}