MultiSeg Apps: First working version (exceptions don't work)

This commit is contained in:
Wolfgang Thaller 2017-09-26 11:27:53 +02:00
parent a4716081c2
commit d08331584e
7 changed files with 314 additions and 43 deletions

View File

@ -32,6 +32,7 @@
#include <string>
#include <unordered_map>
#include <map>
#include <vector>
#include <fstream>
#include <iostream>
@ -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<Symbol> symbols;
map<pair<int,uint32_t>, 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] = 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");

View File

@ -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,6 +100,8 @@ 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*)
@ -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";

View File

@ -18,6 +18,8 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <exception>
#include <Events.h>
@ -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;
}

25
libretro/LoadSeg.s Normal file
View File

@ -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

193
libretro/MultiSegApp.c Normal file
View File

@ -0,0 +1,193 @@
#include <OSUtils.h>
#include <Traps.h>
#include <Resources.h>
#include <Memory.h>
#include <stdint.h>
#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);
}

View File

@ -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__);
}

View File

@ -23,9 +23,7 @@
<http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdlib.h>
#include "Retro68Runtime.h"
int main(int argc, char* argv[]);