diff --git a/Elf2Mac/Elf2Mac.cc b/Elf2Mac/Elf2Mac.cc index 2b08f88ace..47bb99872a 100644 --- a/Elf2Mac/Elf2Mac.cc +++ b/Elf2Mac/Elf2Mac.cc @@ -64,42 +64,73 @@ shared_ptr
dataSection; enum class SectionKind { - code, + undefined = -1, + code = 0, data, - bss + bss, + jumptable }; class Symbol : public GElf_Sym { public: bool valid; + bool referencedExternally; + SectionKind sectionKind; Symbol(); Symbol(GElf_Sym sym); }; +class Symtab +{ +public: + Elf_Scn *elfsec; + Elf_Data *data; + vector symbols; + + Symtab(Elf_Scn *elfsec); + + Symbol& GetSym(int idx); +}; + +class Section +{ +public: + string name; + int idx; + SectionKind kind; + Elf_Scn *elfsec, *relasec; + Elf_Data *data; + + std::vector relocs; + + Section(string name, int idx, SectionKind kind, Elf_Scn *elfsec); + void SetRela(Elf_Scn *scn); + + uint32_t GetSize(); + string GetData(); + string GetAbsRelocations(bool suppressTerminatingEntry = false); + + void ScanRelocs(); +}; + Symbol::Symbol() : valid(false) { } Symbol::Symbol(GElf_Sym sym) - : GElf_Sym(sym), valid(true) + : GElf_Sym(sym), valid(true), + referencedExternally(false), + sectionKind(SectionKind::undefined) { + if(st_shndx != SHN_UNDEF) + { + sectionKind = sectionsByElfIndex[st_shndx]->kind; + } } -class Symtab -{ - vector symbols; -public: - Elf_Scn *elfsec; - Elf_Data *data; - - Symtab(Elf_Scn *elfsec); - - Symbol& GetSym(int idx); -}; - Symtab::Symtab(Elf_Scn *elfsec) : elfsec(elfsec) { @@ -126,25 +157,8 @@ Symbol &Symtab::GetSym(int idx) } } - -class Section -{ -public: - string name; - SectionKind kind; - Elf_Scn *elfsec, *relasec; - Elf_Data *data; - - Section(string name, SectionKind kind, Elf_Scn *elfsec); - void SetRela(Elf_Scn *scn); - - uint32_t GetSize(); - string GetData(); - string GetAbsRelocations(); -}; - -Section::Section(string name, SectionKind kind, Elf_Scn *elfsec) - : name(name), kind(kind), elfsec(elfsec), relasec(NULL) +Section::Section(string name, int idx, SectionKind kind, Elf_Scn *elfsec) + : name(name), idx(idx), kind(kind), elfsec(elfsec), relasec(NULL) { data = elf_getdata(elfsec, NULL); } @@ -152,6 +166,20 @@ Section::Section(string name, SectionKind kind, Elf_Scn *elfsec) void Section::SetRela(Elf_Scn *scn) { relasec = scn; + GElf_Shdr shdr; + gelf_getshdr(relasec, &shdr); + + int nRela = shdr.sh_size / shdr.sh_entsize; + Elf_Data *data = elf_getdata(relasec, NULL); + for(int i = 0; i < nRela; i++) + { + GElf_Rela rela; + gelf_getrela(data, i, &rela); + relocs.push_back(rela); + } + + std::sort(relocs.begin(), relocs.end(), + [](GElf_Rela& a, GElf_Rela& b) { return a.r_offset < b.r_offset; }); } uint32_t Section::GetSize() @@ -164,45 +192,55 @@ string Section::GetData() return string((char*)data->d_buf, (char*)data->d_buf + data->d_size); } -string Section::GetAbsRelocations() +string Section::GetAbsRelocations(bool suppressTerminatingEntry) { if(!relasec) return ""; std::ostringstream out; - - std::vector relocs; - - GElf_Shdr shdr; - gelf_getshdr(relasec, &shdr); - - int nRela = shdr.sh_size / shdr.sh_entsize; - Elf_Data *data = elf_getdata(relasec, NULL); - for(int i = 0; i < nRela; i++) + for(auto& rela : relocs) { - GElf_Rela rela; - gelf_getrela(data, i, &rela); - //printf("rel: %d %d %x %x\n", (int)GELF_R_TYPE(rela.r_info), (int)GELF_R_SYM(rela.r_info), (unsigned)rela.r_addend, (unsigned)rela.r_offset); int symidx = GELF_R_SYM(rela.r_info); if(symidx == 0) continue; - GElf_Sym sym = symtab->GetSym(symidx); + Symbol& sym = symtab->GetSym(symidx); if(sym.st_shndx == SHN_UNDEF) continue; if(GELF_R_TYPE(rela.r_info) == R_68K_32) - relocs.push_back(rela.r_offset); + { + assert(sym.sectionKind != SectionKind::undefined); + + longword(out, rela.r_offset | ((int)sym.sectionKind << 24)); + } } - std::sort(relocs.begin(), relocs.end()); - for(int reloc : relocs) - longword(out, reloc); + if(!suppressTerminatingEntry) + longword(out, -1); return out.str(); } +void Section::ScanRelocs() +{ + for(auto& rela : relocs) + { + int symidx = GELF_R_SYM(rela.r_info); + if(symidx == 0) + continue; + + Symbol& sym = symtab->GetSym(symidx); + + if(sym.st_shndx == SHN_UNDEF) + continue; + + if(sym.st_shndx != idx) + sym.referencedExternally = true; + } +} + void GrokELF(string input) { @@ -219,13 +257,13 @@ void GrokELF(string input) Elf_Scn* bssSection = NULL; - int idx = 0; - for(Elf_Scn *scn = NULL; (scn = elf_nextscn(elf, scn)) != NULL;idx++) + int idx = 1; + for(Elf_Scn *scn = NULL; (scn = elf_nextscn(elf, scn)) != NULL; idx++) { GElf_Shdr shdr; gelf_getshdr(scn, &shdr); std::string name = elf_strptr(elf, sectionHeaderStringTableIdx, shdr.sh_name); - //printf("section: %s\n", name.c_str()); + //std::cout << "section #" << idx << ": " << name << std::endl; if(shdr.sh_type == SHT_SYMTAB && !symtab) @@ -251,7 +289,7 @@ void GrokELF(string input) && !bssSection) // ignore everything after bss, that's just debug info { SectionKind kind = name == ".data" ? SectionKind::data : SectionKind::code; - auto section = make_shared
(name,kind, scn); + auto section = make_shared
(name, idx, kind, scn); sections[name] = sectionsByElfIndex[idx] = section; if(kind == SectionKind::data) @@ -267,7 +305,7 @@ void GrokELF(string input) // (What's the official way to distinguish a debug info section from a "real" section?) sections[name] = sectionsByElfIndex[idx] = - make_shared
(name,SectionKind::bss, scn); + make_shared
(name, idx, SectionKind::bss, scn); } } @@ -283,9 +321,9 @@ void FlatCode(std::ostream& out) out << dataSection->GetData(); for(auto sec : codeSections) - out << sec->GetAbsRelocations(); + out << sec->GetAbsRelocations(true); out << dataSection->GetAbsRelocations(); - longword(out, -1); + } void FlatCode(string fn) @@ -356,6 +394,53 @@ void SingleSegmentApp(string output) file.write(); } + + +void MultiSegmentApp(string output) +{ + ResourceFile file(output); + Resources& rsrc = file.resources; + + for(auto sec : codeSections) + { + sec->ScanRelocs(); + } + + rsrc.addResource(Resource(ResType("CODE"), 0, + fromhex( + "00000028 00000000 00000008 00000020" + "0000 3F3C 0001 A9F0" + ) + )); + + + int id = 1; + for(auto sec : codeSections) + { + std::ostringstream code; + word(code, 0); //FIXME: header + word(code, 1); + code << sec->GetData(); + + rsrc.addResource(Resource(ResType("CODE"), id, + code.str())); + + + rsrc.addResource(Resource(ResType("RELA"),id, sec->GetAbsRelocations())); + id++; + } + + rsrc.addResource(Resource(ResType("DATA"),0, dataSection->GetData())); + + + + + file.creator = ResType("????"); + file.type = ResType("APPL"); + + file.write(); +} + string argvZero; void RealLD(vector args) @@ -412,6 +497,7 @@ int main(int argc, char *argv[]) string outputFile = "a.out"; bool elf2mac = false; bool flatoutput = false; + bool segments = false; vector args2; for(auto p = args.begin(), e = args.end(); p != e; ++p) @@ -458,10 +544,12 @@ int main(int argc, char *argv[]) int fd = mkstemp(tmpfile); if(fd < 0) errx(EXIT_FAILURE, "can't create temp file"); + { ofstream out(tmpfile); - CreateLdScript(out); + CreateLdScript(out, segments); } + args2.push_back("-o"); args2.push_back(outputFile + ".gdb"); args2.push_back("-T"); @@ -471,6 +559,8 @@ int main(int argc, char *argv[]) GrokELF(outputFile + ".gdb"); if(flatoutput) FlatCode(outputFile); + else if(segments) + MultiSegmentApp(outputFile); else SingleSegmentApp(outputFile); } diff --git a/Elf2Mac/Elf2Mac.h b/Elf2Mac/Elf2Mac.h index 4fcd5711dd..4c658d66f2 100644 --- a/Elf2Mac/Elf2Mac.h +++ b/Elf2Mac/Elf2Mac.h @@ -22,6 +22,6 @@ #include -void CreateLdScript(std::ostream& out); +void CreateLdScript(std::ostream& out, bool segments); #endif // ELF2MAC_H diff --git a/Elf2Mac/LdScript.cc b/Elf2Mac/LdScript.cc index f1760b3d64..3421064606 100644 --- a/Elf2Mac/LdScript.cc +++ b/Elf2Mac/LdScript.cc @@ -232,17 +232,18 @@ const char * scriptEnd = R"ld( )ld"; -void CreateLdScript(std::ostream& out) +void CreateLdScript(std::ostream& out, bool segments) { -#if 1 - out << scriptStart << textSection << scriptEnd; -#else - out << scriptStart << code1Section; - string code = codeSectionTemplate; - boost::replace_all(code, "@N@", "2"); - boost::replace_all(code, "@FILTER@", "*"); - boost::replace_all(code, "@EXTRA@", lastCodeExtra); - out << code; - out << scriptEnd; -#endif + if(segments) + { + out << scriptStart << code1Section; + string code = codeSectionTemplate; + boost::replace_all(code, "@N@", "2"); + boost::replace_all(code, "@FILTER@", "*"); + boost::replace_all(code, "@EXTRA@", lastCodeExtra); + out << code; + out << scriptEnd; + } + else + out << scriptStart << textSection << scriptEnd; } diff --git a/libretro/relocate.c b/libretro/relocate.c index efd19f86e7..594f19077e 100644 --- a/libretro/relocate.c +++ b/libretro/relocate.c @@ -204,14 +204,30 @@ void Retro68Relocate() rState->bssPtr = NewPtrClear(bss_size); bss_displacement = (uint8_t*)rState->bssPtr - orig_sbss; } - + + /* + Relocation records consist of 4 bytes each. + The lower three bytes are the offset of the longword being relocated. + the first byte of each longword specifies which segment + the relocation leads to; the corresponding displacements are taken + from the following table: + */ + long displacements[4] = { + displacement, // code + displacement, // data (contiguous with code) + bss_displacement, // bss (allocated separately) + SetCurrentA5() // jump table (TODO) + }; + // Process relocation records for(long *reloc = (long*)( base + text_and_data_size ); *reloc != -1; ++reloc) { - uint8_t *addrPtr = base + *reloc; + uint32_t r = *reloc; + uint8_t *addrPtr = base + (r & 0xFFFFFF); uint8_t *addr; + uint8_t kind = r >> 24; assert(addrPtr >= base); assert(addrPtr < base + text_and_data_size); @@ -225,8 +241,7 @@ void Retro68Relocate() /*assert((uint8_t*)addr >= orig_stext); // TODO: not right for repeated reloc assert((uint8_t*)addr <= orig_stext + total_size);*/ - addr += (addr - orig_stext) >= text_and_data_size ? - bss_displacement : displacement; + addr += displacements[kind]; /*assert((Ptr)addr >= (Ptr)base && (Ptr)addr <= (Ptr)base + text_and_data_size || (Ptr)addr >= rState->bssPtr && (Ptr)addr <= rState->bssPtr + bss_size);*/