Linking and unwinds

The goal here is to provide an unwind implementation that works.  We (probably) don't have a working ldso, so reconfigure libunwind to work without that.  To do that, it needs __eh_frame_* variables which it can addressof to find the locations of .eh_frame and .eh_frame_hdr.  There is no way I could get gold to add these at all, so switch back to ld and add a custom linkscript.  The custom linkscript gives us the opportunity to simplify the savestate memory mapping stuff inside ElfLoader, which should knock a bit of time off of state loads -- those VirtualProtect calls are not cheap.  This also removes a potential source of nondeterminism with certain clever out of range pointers.

Any waterbox core that I didn't recompile for this commit will assert now until it is recompiled, because .wbxsyscall is in the wrong palace.
This commit is contained in:
nattthebear 2020-05-23 16:26:48 -04:00
parent 815d49bd63
commit 24286be735
7 changed files with 316 additions and 42 deletions

Binary file not shown.

Binary file not shown.

View File

@ -26,8 +26,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
private readonly Section<ulong> _sealed;
private readonly Section<ulong> _invisible;
private readonly List<Section<ulong>> _savedSections;
private readonly bool _skipCoreConsistencyCheck;
private readonly bool _skipMemoryConsistencyCheck;
@ -37,6 +35,20 @@ namespace BizHawk.Emulation.Cores.Waterbox
public string ModuleName { get; }
/// <summary>
/// Where writable data begins
/// </summary>
private ulong _writeStart;
/// <summary>
/// Where writiable data begins after seal
/// </summary>
private ulong _postSealWriteStart;
/// <summary>
/// Where the saveable program data begins
/// </summary>
private ulong _saveStart;
private ulong _execEnd;
public ElfLoader(string moduleName, byte[] fileData, ulong assumedStart, bool skipCoreConsistencyCheck, bool skipMemoryConsistencyCheck)
{
ModuleName = moduleName;
@ -76,12 +88,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
_sectionsByName.TryGetValue(".sealed", out _sealed);
_sectionsByName.TryGetValue(".invis", out _invisible);
_savedSections = _elf.Sections
.Where(s => (s.Flags & SectionFlags.Allocatable) != 0 && (s.Flags & SectionFlags.Writable) != 0)
.Where(s => !IsSpecialReadonlySection(s) && s != _invisible)
.OrderBy(s => s.LoadAddress)
.ToList();
_visibleSymbols = _allSymbols
.Where(s => s.Binding == SymbolBinding.Global && s.Visibility == SymbolVisibility.Default)
.ToDictionary(s => s.Name);
@ -93,7 +99,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
.Where(s => s.PointedSection == _imports)
.ToList();
Memory = MemoryBlock.Create(start, size);
Memory.Activate();
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.RW);
@ -104,6 +109,37 @@ namespace BizHawk.Emulation.Cores.Waterbox
Marshal.Copy(data, 0, Z.US(seg.Address), Math.Min((int)seg.Size, (int)seg.FileSize));
}
{
// Compute RW boundaries
var allocated = _elf.Sections
.Where(s => (s.Flags & SectionFlags.Allocatable) != 0);
var writable = allocated
.Where(s => (s.Flags & SectionFlags.Writable) != 0);
var postSealWritable = writable
.Where(s => !IsSpecialReadonlySection(s));
var saveable = postSealWritable
.Where(s => s != _invisible);
var executable = allocated
.Where(s => (s.Flags & SectionFlags.Executable) != 0);
_writeStart = WaterboxUtils.AlignDown(writable.Min(s => s.LoadAddress));
_postSealWriteStart = WaterboxUtils.AlignDown(postSealWritable.Min(s => s.LoadAddress));
_saveStart = WaterboxUtils.AlignDown(saveable.Min(s => s.LoadAddress));
_execEnd = WaterboxUtils.AlignUp(executable.Max(s => s.LoadAddress + s.Size));
// validate; this may require linkscript cooperation
// due to the segment limitations, the only thing we'd expect to catch is a custom eventually readonly section
// in the wrong place (because the linkscript doesn't know "eventually readonly")
if (_execEnd > _writeStart)
throw new InvalidOperationException($"ElfLoader: Executable data to {_execEnd:X16} overlaps writable data from {_writeStart}");
var actuallySaved = allocated.Where(a => a.LoadAddress + a.Size > _saveStart);
var oopsSaved = actuallySaved.Except(saveable);
foreach (var s in oopsSaved)
throw new InvalidOperationException($"ElfLoader: Section {s.Name} will be saved, but that was not expected");
}
PrintSections();
PrintGdbData();
PrintTopSavableSymbols();
@ -126,7 +162,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
(s.Flags & SectionFlags.Allocatable) != 0 ? "R" : " ",
(s.Flags & SectionFlags.Writable) != 0 ? "W" : " ",
(s.Flags & SectionFlags.Executable) != 0 ? "X" : " ",
_savedSections.Contains(s) ? "V" : " ",
s.LoadAddress + s.Size > _saveStart ? "V" : " ",
s.Name,
s.Size);
}
@ -135,7 +171,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
private void PrintTopSavableSymbols()
{
var tops = _allSymbols
.Where(s => _savedSections.Contains(s.PointedSection))
.Where(s => s.Value + s.Size > _saveStart)
.OrderByDescending(s => s.Size)
.Where(s => s.Size >= 20 * 1024)
.Take(30)
@ -170,17 +206,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// </summary>
private void Protect()
{
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.R);
foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Allocatable) != 0))
{
if (_everythingSealed && IsSpecialReadonlySection(sec))
continue;
var writeStart = _everythingSealed ? _postSealWriteStart : _writeStart;
if ((sec.Flags & SectionFlags.Executable) != 0)
Memory.Protect(sec.LoadAddress, sec.Size, MemoryBlock.Protection.RX);
else if ((sec.Flags & SectionFlags.Writable) != 0)
Memory.Protect(sec.LoadAddress, sec.Size, MemoryBlock.Protection.RW);
}
Memory.Protect(Memory.Start, _execEnd - Memory.Start, MemoryBlock.Protection.RX);
Memory.Protect(_execEnd, writeStart - _execEnd, MemoryBlock.Protection.R);
Memory.Protect(writeStart, Memory.EndExclusive - writeStart, MemoryBlock.Protection.RW);
}
// connect all of the .wbxsyscall stuff
@ -304,12 +334,10 @@ namespace BizHawk.Emulation.Cores.Waterbox
bw.Write(_elfHash);
bw.Write(Memory.XorHash);
foreach (var s in _savedSections)
{
var ms = Memory.GetXorStream(s.LoadAddress, s.Size, false);
bw.Write(s.Size);
ms.CopyTo(bw.BaseStream);
}
var len = Memory.EndExclusive - _saveStart;
var ms = Memory.GetXorStream(_saveStart, len, false);
bw.Write(len);
ms.CopyTo(bw.BaseStream);
}
public void LoadStateBinary(BinaryReader br)
@ -343,18 +371,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
throw new InvalidOperationException("Memory consistency check failed. Is this savestate from different SyncSettings?");
}
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.RW);
foreach (var s in _savedSections)
{
if (br.ReadUInt64() != s.Size)
throw new InvalidOperationException("Unexpected section size for " + s.Name);
var ms = Memory.GetXorStream(s.LoadAddress, s.Size, true);
WaterboxUtils.CopySome(br.BaseStream, ms, (long)s.Size);
}
Protect();
var len = Memory.EndExclusive - _saveStart;
if (br.ReadUInt64() != len)
throw new InvalidOperationException("Unexpected saved length");
var ms = Memory.GetXorStream(_saveStart, len, true);
WaterboxUtils.CopySome(br.BaseStream, ms, (long)len);
}
}
}

View File

@ -25,7 +25,7 @@ CC := $(SYSROOT)/bin/musl-gcc
COMMONFLAGS := -mabi=ms -fvisibility=hidden -I$(WATERBOX_DIR)/emulibc -Wall -mcmodel=large \
-mstack-protector-guard=global -no-pie -fno-pic -fno-pie
CCFLAGS := $(CCFLAGS) $(COMMONFLAGS)
LDFLAGS := $(LDFLAGS) -fuse-ld=gold -static -Wl,-Ttext,0x0000036f00000000 #-Wl,--plugin,$(LD_PLUGIN)
LDFLAGS := $(LDFLAGS) -static -Wl,--eh-frame-hdr -T $(WATERBOX_DIR)/linkscript.T #-Wl,--plugin,$(LD_PLUGIN)
CCFLAGS_DEBUG := -O0 -g
CCFLAGS_RELEASE := -O3 -flto
LDFLAGS_DEBUG :=

View File

@ -22,6 +22,8 @@ cmake \
-DCOMPILER_RT_BUILD_PROFILE=OFF \
-DCOMPILER_RT_CAN_EXECUTE_TESTS=OFF \
-DCOMPILER_RT_USE_BUILTINS_LIBRARY=ON \
-UCOMPILER_RT_BAREMETAL_BUILD \
-DCOMPILER_RT_BAREMETAL_BUILD=ON \
-DCMAKE_INSTALL_PREFIX="$SYSROOT" \
-DCMAKE_AR="/usr/bin/gcc-ar" \
-DCMAKE_RANLIB="/usr/bin/gcc-ranlib" \

View File

@ -5,8 +5,8 @@ LLVMDIR="`realpath \"$MYPATH/../../../llvm-project\"`"
rm -rf build0
mkdir build0
cd build0
export CFLAGS="-mabi=ms -mcmodel=large -mstack-protector-guard=global -fno-use-cxa-atexit -no-pie -fno-pic -fno-pie"
export CXXFLAGS="-mabi=ms -mcmodel=large -mstack-protector-guard=global -fno-use-cxa-atexit -no-pie -fno-pic -fno-pie"
export CFLAGS="-mabi=ms -mcmodel=large -mstack-protector-guard=global -fno-use-cxa-atexit -no-pie -fno-pic -fno-pie -D_WIN64 -D_LIBUNWIND_IS_BAREMETAL -D_LIBUNWIND_SUPPORT_DWARF_UNWIND"
export CXXFLAGS="-mabi=ms -mcmodel=large -mstack-protector-guard=global -fno-use-cxa-atexit -no-pie -fno-pic -fno-pie -D_WIN64 -D_LIBUNWIND_IS_BAREMETAL -D_LIBUNWIND_SUPPORT_DWARF_UNWIND"
cmake \
-DCMAKE_C_COMPILER="$SYSROOT/bin/musl-gcc" \
-DCMAKE_CXX_COMPILER="$SYSROOT/bin/musl-gcc" \

251
waterbox/linkscript.T Normal file
View File

@ -0,0 +1,251 @@
/* Script for -z combreloc -z separate-code */
/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
Copying and distribution of this script, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
"elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
SECTIONS
{
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x36f00000000)); . = SEGMENT_START("text-segment", 0x36f00000000) + SIZEOF_HEADERS;
.interp : { *(.interp) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rela.dyn :
{
*(.rela.init)
*(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
*(.rela.fini)
*(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
*(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
*(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
*(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
*(.rela.ctors)
*(.rela.dtors)
*(.rela.got)
*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
*(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
*(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
*(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
*(.rela.ifunc)
}
.rela.plt :
{
*(.rela.plt)
PROVIDE_HIDDEN (__rela_iplt_start = .);
*(.rela.iplt)
PROVIDE_HIDDEN (__rela_iplt_end = .);
}
. = ALIGN(CONSTANT (MAXPAGESIZE));
.init :
{
KEEP (*(SORT_NONE(.init)))
}
.plt : { *(.plt) *(.iplt) }
.plt.got : { *(.plt.got) }
.plt.sec : { *(.plt.sec) }
.text :
{
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(SORT(.text.sorted.*))
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf.em. */
*(.gnu.warning)
}
.fini :
{
KEEP (*(SORT_NONE(.fini)))
}
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
. = ALIGN(CONSTANT (MAXPAGESIZE));
/* Adjust the address for the rodata segment. We want to adjust up to
the same address within the page on the next page up. */
. = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)));
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
__eh_frame_hdr_start = .;
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
__eh_frame_hdr_end = .;
__eh_frame_start = .;
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
__eh_frame_end = .;
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) }
/* These sections are generated by the Sun/Oracle C++ compiler. */
.exception_ranges : ONLY_IF_RO { *(.exception_ranges*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gnu_extab : ONLY_IF_RW { *(.gnu_extab) }
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
.exception_ranges : ONLY_IF_RW { *(.exception_ranges*) }
/* Thread Local Storage sections */
.tdata :
{
PROVIDE_HIDDEN (__tdata_start = .);
*(.tdata .tdata.* .gnu.linkonce.td.*)
}
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
}
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
.dynamic : { *(.dynamic) }
.got : { *(.got) *(.igot) }
. = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
.got.plt : { *(.got.plt) *(.igot.plt) }
/* waterbox sections that are eventually RO at runtime */
. = ALIGN(CONSTANT (MAXPAGESIZE));
.wbxsyscall : { *(.wbxsyscall) }
.sealed : { *(.sealed) }
. = ALIGN(CONSTANT (MAXPAGESIZE));
/* Put the invisible section by itself */
.invis : { *(.invis) }
. = ALIGN(CONSTANT (MAXPAGESIZE));
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
. = .;
__bss_start = .;
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we do not
pad the .data section. */
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
.lbss :
{
*(.dynlbss)
*(.lbss .lbss.* .gnu.linkonce.lb.*)
*(LARGE_COMMON)
}
. = ALIGN(64 / 8);
. = SEGMENT_START("ldata-segment", .);
.lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
{
*(.lrodata .lrodata.* .gnu.linkonce.lr.*)
}
.ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
{
*(.ldata .ldata.* .gnu.linkonce.l.*)
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
. = ALIGN(64 / 8);
_end = .; PROVIDE (end = .);
. = DATA_SEGMENT_END (.);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
.gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
/* DWARF Extension. */
.debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}