From d08331584e01c569d4e25834e1169a9b43e88925 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 26 Sep 2017 11:27:53 +0200 Subject: [PATCH] MultiSeg Apps: First working version (exceptions don't work) --- Elf2Mac/Elf2Mac.cc | 68 ++++++++++---- Elf2Mac/LdScript.cc | 28 ++++-- TestApps/ExceptionTest.cc | 33 ++++--- libretro/LoadSeg.s | 25 +++++ libretro/MultiSegApp.c | 193 ++++++++++++++++++++++++++++++++++++++ libretro/relocate.c | 8 +- libretro/start.c | 2 - 7 files changed, 314 insertions(+), 43 deletions(-) create mode 100644 libretro/LoadSeg.s create mode 100644 libretro/MultiSegApp.c diff --git a/Elf2Mac/Elf2Mac.cc b/Elf2Mac/Elf2Mac.cc index 0e5a0efcab..ab94899e5b 100644 --- a/Elf2Mac/Elf2Mac.cc +++ b/Elf2Mac/Elf2Mac.cc @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -42,11 +43,14 @@ using std::string; using std::unordered_map; +using std::map; using std::vector; using std::ofstream; using std::shared_ptr; using std::make_shared; using std::unique_ptr; +using std::pair; +using std::make_pair; size_t sectionHeaderStringTableIdx; size_t mainStringTableIdx = (size_t)-1; @@ -102,10 +106,12 @@ public: Elf_Scn *elfsec; Elf_Data *data; vector symbols; + map, int> symbolsByAddress; Symtab(Elf_Scn *elfsec); Symbol& GetSym(int idx); + int FindSym(int secidx, uint32_t addr); }; class Reloc : public GElf_Rela @@ -143,6 +149,7 @@ public: void ScanRelocs(); void FixRelocs(); + }; Symbol::Symbol() @@ -177,21 +184,32 @@ Symtab::Symtab(Elf_Scn *elfsec) gelf_getshdr(elfsec, &shdr); int count = shdr.sh_size / shdr.sh_entsize; - symbols.resize(count); + symbols.reserve(count); + + for(int i = 0; i < count; i++) + { + GElf_Sym sym; + auto res = gelf_getsym(data, i, &sym); + assert(res != 0); + symbols.emplace_back(sym); + + if(sym.st_shndx != SHN_UNDEF && sym.st_shndx < SHN_LORESERVE) + symbolsByAddress[make_pair((int)sym.st_shndx,sym.st_value)] = i; + } } Symbol &Symtab::GetSym(int idx) { - if(symbols[idx].valid) - return symbols[idx]; - else - { - GElf_Sym sym; - auto res = gelf_getsym(data, idx, &sym); - assert(res != 0); + return symbols[idx]; +} - return (symbols[idx] = Symbol(sym)); - } +int Symtab::FindSym(int secidx, uint32_t addr) +{ + auto p = symbolsByAddress.find(make_pair(secidx, addr)); + if(p != symbolsByAddress.end()) + return p->second; + else + return -1; } @@ -213,6 +231,7 @@ Section::Section(string name, int idx, SectionKind kind, Elf_Scn *elfsec) outputBase = shdr.sh_addr; } + void Section::SetRela(Elf_Scn *scn) { relasec = scn; @@ -269,7 +288,7 @@ string Section::GetAbsRelocations(bool useOffsets, bool suppressTerminatingEntry if(useOffsets) offset -= shdr.sh_addr; - + std::cout << "RELA: " << std::hex << offset << " " << (int)rela.relocBase << std::dec << std::endl; longword(out, offset | ((int)rela.relocBase << 24)); } } @@ -280,19 +299,31 @@ string Section::GetAbsRelocations(bool useOffsets, bool suppressTerminatingEntry void Section::ScanRelocs() { - for(auto& rela : relocs) + for(Reloc& rela : relocs) { int symidx = GELF_R_SYM(rela.r_info); if(symidx == 0) continue; - Symbol& sym = symtab->GetSym(symidx); + Symbol *sym = &symtab->GetSym(symidx); - if(sym.st_shndx == SHN_UNDEF) + if(sym->st_shndx == SHN_UNDEF) continue; - if(sym.st_shndx != idx) - sym.referencedExternally = true; + if(rela.r_addend != 0) + { + int symidx2 = symtab->FindSym(sym->st_shndx, sym->st_value + rela.r_addend); + if(symidx2 != -1) + { + sym = &symtab->GetSym(symidx2); + rela.r_addend = 0; + rela.r_info = GELF_R_INFO(symidx2, GELF_R_TYPE(rela.r_info)); + + } + } + + if(sym->st_shndx != idx) + sym->referencedExternally = true; } } @@ -336,6 +367,8 @@ void Section::FixRelocs() assert(sym.section.get() == this); } } + else + assert(sym.section.get() == this); break; case SectionKind::data: relocBase = RelocBase::data; @@ -636,7 +669,7 @@ void MultiSegmentApp(string output) word(code, 0); longword(code, 0); longword(code, 0); - longword(code, 0x20 + 8 * sec->firstJTEntryIndex ); + longword(code, 8 * sec->firstJTEntryIndex ); longword(code, sec->jtEntries.size()); longword(code, 0); // reloc info for A5 longword(code, 0); // assumed address for A5 @@ -773,6 +806,7 @@ int main(int argc, char *argv[]) { ofstream out(tmpfile); CreateLdScript(out, segments); + CreateLdScript(std::cout, segments); } args2.push_back("-o"); diff --git a/Elf2Mac/LdScript.cc b/Elf2Mac/LdScript.cc index 5160254a0a..c4b89f5c01 100644 --- a/Elf2Mac/LdScript.cc +++ b/Elf2Mac/LdScript.cc @@ -90,7 +90,6 @@ const char * code1Section = R"ld(/* ld script for Elf2Mac */ PROVIDE(_rsrc_start = .); . = ALIGN (2); _entry_trampoline = .; - __break_on_entry = 1; SHORT(DEFINED(__break_on_entry) ? 0xA9FF : 0x4e71); LONG(0x61000002); /* bsr *+2 */ SHORT(0x0697); /* addi.l #_, (a7) */ @@ -101,9 +100,11 @@ const char * code1Section = R"ld(/* ld script for Elf2Mac */ *(.relocvars) */libretrocrt.a:start.c.obj(.text*) */libretrocrt.a:relocate.c.obj(.text*) + */libretrocrt.a:MultiSegApp.c.obj(.text*) + */libretrocrt.a:LoadSeg.s.obj(.text*) */libretrocrt.a:*(.text*) */libgcc.a:*(.text*) - */libc.a:*(.text*) + */libc.a:*(.text*) . = ALIGN (4) ; __init_section = . ; @@ -114,7 +115,16 @@ const char * code1Section = R"ld(/* ld script for Elf2Mac */ __fini_section_end = . ; __EH_FRAME_BEGIN__ = .; + KEEP(*/libretrocrt.a:*(.eh_frame)) + KEEP(*/libgcc.a:*(.eh_frame)) + KEEP(*/libc.a:*(.eh_frame)) LONG(0); + KEEP(*/libretrocrt.a:*(.gcc_except_table)) + KEEP(*/libretrocrt.a:*(.gcc_except_table.*)) + KEEP(*/libgcc.a:*(.gcc_except_table)) + KEEP(*/libgcc.a:*(.gcc_except_table.*)) + KEEP(*/libc.a:*(.gcc_except_table)) + KEEP(*/libc.a:*(.gcc_except_table.*)) . = ALIGN(0x4) ; _etext = . ; @@ -129,21 +139,21 @@ const char * codeSectionTemplate = R"ld(/* ld script for Elf2Mac */ . = ALIGN (4) ; - /* KEEP(@FILTER@(.eh_frame)) + __EH_FRAME_BEGIN__@N@ = .; + KEEP(@FILTER@(.eh_frame)) LONG(0); KEEP(@FILTER@(.gcc_except_table)) - KEEP(@FILTER@(.gcc_except_table.*)) */ + KEEP(@FILTER@(.gcc_except_table.*)) - . = ALIGN(0x4) ; + . = ALIGN(0x4); + LONG(0xDEADBEEF); + . += 32; + LONG(__EH_FRAME_BEGIN__@N@ - .); } )ld"; const char * lastCodeExtra = R"ld( - *(.stub) *(.gnu.linkonce.t*) - *(.glue_7t) - *(.glue_7) - *(.jcr) )ld"; diff --git a/TestApps/ExceptionTest.cc b/TestApps/ExceptionTest.cc index 0597be65eb..50e0809706 100644 --- a/TestApps/ExceptionTest.cc +++ b/TestApps/ExceptionTest.cc @@ -18,6 +18,8 @@ */ #include +#include +#include #include @@ -30,8 +32,26 @@ void foobar() throw Foo(); } +void UnexpectedExceptionOccurred() +{ + printf("std::unexpected called.\n"); + printf("Press Enter...\n"); + getchar(); + exit(1); +} + +void UncaughtExceptionOccurred() +{ + printf("std::terminate called.\n"); + printf("Press Enter...\n"); + getchar(); + exit(1); +} + int main(int argc, char** argv) { + std::set_unexpected(&UnexpectedExceptionOccurred); + std::set_terminate(&UncaughtExceptionOccurred); bool throwFail = false; bool catchFail = true; for(int i = 0; i < 5; i++) @@ -53,16 +73,7 @@ int main(int argc, char** argv) if(catchFail) printf("******** FAILURE: catch block never entered\n"); - const int n = 3; - printf("Click mouse %d times...\n", n); - for(int i = 0; i < n; i++) - { - while(!Button()) - ; - while(Button()) - ; - printf("Click #%d\n", i+1); - } - FlushEvents(everyEvent, 0); + printf("Press Enter...\n"); + getchar(); return 0; } diff --git a/libretro/LoadSeg.s b/libretro/LoadSeg.s new file mode 100644 index 0000000000..33c87cef9b --- /dev/null +++ b/libretro/LoadSeg.s @@ -0,0 +1,25 @@ +.text +.globl PATCHEDLOADSEG + +PATCHEDLOADSEG: + +# Stack on Entry: +# (arguments for function) +# (return address) +# (return address from trap (== address in jt entry)) + movel %sp@, %sp@- + +# Stack now: +# (arguments for function) +# (return address) +# (don't care) +# (return address from trap (== address in jt entry)) + + jsr RETRO68LOADSEGMENT + +# Stack now: +# (arguments for function) +# (return address) +# (address of loaded function) + + rts diff --git a/libretro/MultiSegApp.c b/libretro/MultiSegApp.c new file mode 100644 index 0000000000..0841ffd63c --- /dev/null +++ b/libretro/MultiSegApp.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#include "PoorMansDebugging.h" + +#include "Retro68Runtime.h" + +static pascal void (*OriginalLoadSeg)(short id); +static pascal void (*OriginalUnloadSeg)(void *ptr); +static pascal void (*OriginalExitToShell)(); + +extern pascal void PatchedLoadSeg(); + +typedef union JTEntry +{ + struct { + uint16_t offset; + uint16_t push; + int16_t id; + uint16_t loadseg; + } near; + struct { + int16_t id; + uint16_t loadseg; + uint32_t offset; + } far; + struct { + int16_t id; + uint16_t jmp; + void* addr; + } jmp; + +} JTEntry; + +typedef struct CODEHeader +{ + int16_t magic0; + int16_t magic1; + uint32_t nearEntryOffset; + uint32_t nNearEntries; + uint32_t farEntryOffset; + uint32_t nFarEntries; + uint32_t a5relocOffset; + uint8_t *currentA5; + uint32_t relocOffset; + uint8_t *loadAddress; + uint32_t reserved; +} CODEHeader; + +/* + struct object is an internal data structure in libgcc. + Comments in unwind-dw2-fde.h imply that it will not + increase in size. + */ +struct object { long space[8]; }; + +extern void __register_frame_info (const void *, struct object *) + __attribute__ ((weak)); +extern void *__deregister_frame_info (const void *) + __attribute__ ((weak)); + + +pascal void* Retro68LoadSegment(uint8_t *p) +{ + union JTEntry *jtEntry = (JTEntry*) (p - 4); + + short id = jtEntry->far.id; + uint32_t offset = jtEntry->far.offset; + + // TODO: UseResFile? + Handle CODE = GetResource('CODE', id); + HLock(CODE); + + uint8_t *base = StripAddress((uint8_t *)*CODE); + CODEHeader *header = (CODEHeader*) base; + uint32_t codeSize = GetHandleSize(CODE); + + // TODO: StripAddress24 + uint8_t * a5 = (uint8_t*) StripAddress((void*)SetCurrentA5()); + + if(header->loadAddress != base || header->currentA5 != a5) + { + long displacements[4] = { + base - header->loadAddress, // code + a5 - header->currentA5, + a5 - header->currentA5, + a5 - header->currentA5 + }; + + header->loadAddress = base; + header->currentA5 = a5; + + Handle RELA = NULL; + RELA = GetResource('RELA', id); + assert(RELA); + Retro68ApplyRelocations(base + 40, codeSize, *RELA, displacements); + } + else + DebugStr("\prelocation unnecessary???"); + + + /* Update JT Entries */ + // FIXME: hardcoded JT offset, there is a LM global for this somewhere: + jtEntry = (JTEntry*) (a5 + 32 + header->farEntryOffset); + int n = header->nFarEntries; + while(n--) + { + void * addr = base + jtEntry->far.offset; + //jtEntry->jmp.id = jtEntry->far.id; + jtEntry->jmp.jmp = 0x4EF9; + jtEntry->jmp.addr = addr; + ++jtEntry; + } + + // TODO: Flush cache + + /* Load Exception Information */ + if (__register_frame_info) + { + int32_t offset = ((int32_t*) (base + codeSize))[-1]; + void *eh_frame_info = *(void**) (base + codeSize + offset); + struct object *object = (struct object*) (base + codeSize - 36); + __register_frame_info(eh_frame_info, object); + } + return base + offset; +} + +static pascal void PatchedUnloadSeg(void *ptr) +{ + +} +static pascal void PatchedExitToShell() +{ + SetToolTrapAddress((UniversalProcPtr)OriginalLoadSeg, _LoadSeg); + SetToolTrapAddress((UniversalProcPtr)OriginalUnloadSeg, _UnLoadSeg); + SetToolTrapAddress((UniversalProcPtr)OriginalExitToShell, _ExitToShell); + OriginalExitToShell(); +} + +// section boundaries +extern uint8_t _stext, _etext, _sdata, _edata, _sbss, _ebss; + +void Retro68InitMultisegApp() +{ + uint8_t * a5 = (uint8_t*) StripAddress((void*)SetCurrentA5()); + + // CODE Segment 1 is already loaded - we are in it. + // Update the jump table addresses. + JTEntry *jtEntry = (JTEntry*) (a5 + 32 + 16); // TODO: hardcoded offsets + while(jtEntry->far.id == 1) + { + void * addr = &_stext - 4 + jtEntry->far.offset; + //jtEntry->jmp.id = jtEntry->far.id; + jtEntry->jmp.jmp = 0x4EF9; + jtEntry->jmp.addr = addr; + ++jtEntry; + } + + // Zero-initialize bss + for(uint32_t *p = (uint32_t*) &_sbss; + p < (uint32_t*) &_ebss; ++p) + *p = 0; + + // Set up patched LoadSeg + + // NOTE: OriginalLoadSeg is the first global variable we can use + OriginalLoadSeg = (void(*)(short)) GetToolTrapAddress(_LoadSeg); + OriginalUnloadSeg = (void(*)(void*)) GetToolTrapAddress(_UnLoadSeg); + OriginalExitToShell = (void(*)()) GetToolTrapAddress(_ExitToShell); + SetToolTrapAddress((UniversalProcPtr)&PatchedLoadSeg, _LoadSeg); + SetToolTrapAddress((UniversalProcPtr)&PatchedUnloadSeg, _UnLoadSeg); + SetToolTrapAddress((UniversalProcPtr)&PatchedExitToShell, _ExitToShell); + + // Load and relocate statically initialized DATA + Handle DATA = Get1Resource('DATA', 0); + BlockMoveData(*DATA, &_sdata, &_edata - &_sdata); + ReleaseResource(DATA); + + long displacements[4] = { + 0, + a5 - (uint8_t*)NULL, + a5 - (uint8_t*)NULL, + a5 - (uint8_t*)NULL + }; + + Handle RELA = NULL; + RELA = GetResource('RELA', 0); + assert(RELA); + Retro68ApplyRelocations(&_sdata, &_edata - &_sdata, *RELA, displacements); + +} diff --git a/libretro/relocate.c b/libretro/relocate.c index ea11c345d0..d69600c7e0 100644 --- a/libretro/relocate.c +++ b/libretro/relocate.c @@ -305,9 +305,9 @@ void Retro68Relocate() void Retro68CallConstructors() { - /*static struct object object; + static struct object object; if (__register_frame_info) - __register_frame_info(&__EH_FRAME_BEGIN__, &object);*/ + __register_frame_info(&__EH_FRAME_BEGIN__, &object); { uint8_t *p = &__init_section; uint8_t *e = &__init_section_end; @@ -342,8 +342,8 @@ void Retro68CallDestructors() p += 6; } } -/* if (__deregister_frame_info) - __deregister_frame_info(&__EH_FRAME_BEGIN__);*/ + if (__deregister_frame_info) + __deregister_frame_info(&__EH_FRAME_BEGIN__); } diff --git a/libretro/start.c b/libretro/start.c index e4f47dd89d..0162009cdc 100644 --- a/libretro/start.c +++ b/libretro/start.c @@ -23,9 +23,7 @@ . */ -#include #include - #include "Retro68Runtime.h" int main(int argc, char* argv[]);