#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "PEF.h" #include "ResourceFile.h" #include #include namespace fs = boost::filesystem; using std::cout; using std::cerr; using std::endl; using std::pair; using std::make_pair; using std::vector; using std::string; using std::ofstream; using std::ios_base; bool verboseFlag = false; struct CFragMember { string name; uint32_t architecture; uint8_t usage; char *data; uint32_t length; CFragMember() : architecture(0), usage(0), data(0), length(0) {} CFragMember(string n, uint32_t a, uint8_t u, char *d, uint32_t l) : name(n), architecture(a), usage(u), data(d), length(l) { } }; void RunCommand(const char *command, std::vector args) { std::vector ptrs; ptrs.push_back(command); for(auto& s : args) ptrs.push_back(s.c_str()); ptrs.push_back(NULL); pid_t pid = fork(); if(pid == -1) { perror(command); exit(1); } if(pid) { int status = 0; while(waitpid(pid, &status, 0) == -1 && errno == EINTR) ; if(!WIFEXITED(status) || WEXITSTATUS(status) != 0) { std::cerr << command << " failed.\n"; exit(1); } } else { execvp(command, const_cast(ptrs.data())); perror("exec"); exit(1); } } void MakeImportLibrary(char *pefptr, size_t pefsize, fs::path dest, fs::path tmpdir) { PEFContainerHeader *containerHeader = (PEFContainerHeader*) pefptr; eswap(containerHeader); assert(containerHeader->tag1 == 'Joy!'); assert(containerHeader->tag2 == 'peff'); PEFSectionHeader *sectionHeaders = (PEFSectionHeader*) (pefptr + kPEFFirstSectionHeaderOffset); PEFSectionHeader *loaderHeader = NULL; uint16_t n = containerHeader->sectionCount; for(uint16_t i=0; i < n; i++) { eswap(§ionHeaders[i]); if(sectionHeaders[i].sectionKind == kPEFLoaderSection) loaderHeader = §ionHeaders[i]; } PEFLoaderInfoHeader *loaderInfoHeader = (PEFLoaderInfoHeader*) (pefptr + loaderHeader->containerOffset); eswap(loaderInfoHeader); uint32_t hashTableSize = 1; uint32_t hashTablePower = loaderInfoHeader->exportHashTablePower; while(hashTablePower--) hashTableSize *= 2; uint32_t nSymbols = loaderInfoHeader->exportedSymbolCount; const char *symbols /* use char pointer to avoid packing issues */ = (pefptr + loaderHeader->containerOffset + loaderInfoHeader->exportHashOffset + hashTableSize * sizeof(PEFExportedSymbolHashSlot) + nSymbols * sizeof(PEFExportedSymbolKey)); const char *stringTable = pefptr + loaderHeader->containerOffset + loaderInfoHeader->loaderStringsOffset; /*const char *stringTableEnd = pefptr + loaderHeader->containerOffset + loaderInfoHeader->exportHashOffset;*/ vector< pair< const char *, uint8_t > > classesAndNamePtrs; for(uint32_t i=0; i < nSymbols; i++) { PEFExportedSymbol *sym = (PEFExportedSymbol*) (symbols + 10*i); eswap(sym); uint8_t symclass = PEFExportedSymbolClass(sym->classAndName); uint32_t nameoffset = PEFExportedSymbolNameOffset(sym->classAndName); const char *nameptr = stringTable + nameoffset; classesAndNamePtrs.push_back( make_pair(nameptr, symclass) ); } std::sort(classesAndNamePtrs.begin(), classesAndNamePtrs.end()); vector< pair > exportedSymbols; for(uint32_t i=0; i < nSymbols; i++) { uint8_t symclass = classesAndNamePtrs[i].second; const char *namestart = classesAndNamePtrs[i].first; string name; if(i + 1 < nSymbols) { const char *nameend = classesAndNamePtrs[i+1].first; name = string(namestart,nameend); } else name = string(namestart); exportedSymbols.push_back( make_pair( name, symclass ) ); } std::sort(exportedSymbols.begin(), exportedSymbols.end()); fs::path stub_exp(tmpdir / "__stub.exp"), stub_s(tmpdir / "__stub.s"), stub_o(tmpdir / "__stub.o"); { fs::ofstream expFile(stub_exp); fs::ofstream sFile(stub_s); sFile << "\t.toc\n"; for(uint32_t i=0; i< nSymbols; i++) { string& sym = exportedSymbols[i].first; if(exportedSymbols[i].second == kPEFTVectorSymbol) { expFile << sym << endl; sFile << ".text" << endl; sFile << "\t.globl " << sym << endl; sFile << "\t.globl ." << sym << endl; sFile << "\t.csect " << sym << "[DS]" << endl; sFile << sym << ':' << endl; sFile << ".long ." << sym << ", TOC[tc0], 0" << endl; sFile << ".text" << endl; sFile << '.' << sym << ':' << endl; sFile << "\tblr" << endl; } else if(exportedSymbols[i].second == kPEFDataSymbol) { expFile << sym << endl; sFile << "\t.globl " << sym << endl; sFile << "\t.csect .data[RW],3" << endl; sFile << "\t.align 2" << endl; sFile << sym << ':' << endl; sFile << "\t.long\t42" << endl; } } //cerr << "Generating: " << name << " -> " << libname << endl; RunCommand("powerpc-apple-macos-as", std::vector { stub_s.string(), "-o", stub_o.string() }); RunCommand("powerpc-apple-macos-ld", std::vector { "-shared", "--no-check-sections", "-bexport:" + stub_exp.string(), "-o", dest.string(), stub_o.string() }); } } bool MakeImportLibraryMulti(fs::path path, fs::path libname) { ResourceFile resFile; bool readSuccess = resFile.read(path.string()); if (!readSuccess) { std::cerr << "Could not read input file.\n"; return false; } std::vector data(resFile.data.begin(), resFile.data.end()); char *dataPtr = data.data(); std::vector members; if(data.size() < 8 || std::string(data.begin(), data.begin()+8) != "Joy!peff") { std::cerr << "Not a PEF shared library. Ignoring.\n"; return false; } if(resFile.resources.resources.find(ResRef("cfrg",0)) == resFile.resources.resources.end()) { std::cerr << "No 'cfrg' resource found.\n"; exit(1); } Resource& cfrgRes = resFile.resources.resources[ResRef("cfrg",0)]; CFragResource *cfrg = (CFragResource *)cfrgRes.getData().data(); eswap(cfrg); CFragResourceMember *member = &(cfrg -> firstMember); for(uint16_t i = 0; i < cfrg->memberCount; i++) { eswap(member); string membername = string(member->name+1, member->name+1+member->name[0]); members.emplace_back( membername, member->architecture, member->usage, dataPtr + member->offset, member->length); member = (CFragResourceMember*) (((char*)member) + member->memberSize); } if(!std::any_of(members.begin(), members.end(), [](const auto& member) { return member.architecture == 'pwpc' || member.architecture == '\?\?\?\?'; })) { std::cerr << "Does not contain a PowerPC variant. Ignoring.\n"; return false; } fs::path tmpdir = libname.parent_path() / fs::unique_path("makeimport-tmp-%%%%-%%%%-%%%%-%%%%"); fs::create_directory(tmpdir); try { fs::path archiveTmp(tmpdir / "__archive.a"); std::vector arArguments { "cq", archiveTmp.string() }; int memberIndex = 0; for(auto &member : members) { std::ostringstream memberNameStream; // classic MacOS shared library names are in MacRoman and // may contain wierd characters; the shared library name is used // as the file name for the archive member, so there can be problems. // // We encode the file name to hex (and add a human readable name). // MakePEF contains corresponding decoder logic. memberNameStream << "imp__"; for(char c : member.name) { if(isalnum(c)) memberNameStream << c; } memberNameStream << "_"; for(char c : member.name) { int cc = (unsigned char) c; memberNameStream << std::setw(2) << std::setfill('0') << std::hex << cc; } memberNameStream << "_" << memberIndex++; string memberName = memberNameStream.str(); if(member.usage == 0 /* import library */ || member.usage == 3 /* stub library */) ; else if(member.usage == 4 /* weak stub library */) memberName += "__weak"; if(verboseFlag) { char archStr[5]; archStr[0] = member.architecture >> 24; archStr[1] = member.architecture >> 16; archStr[2] = member.architecture >> 8; archStr[3] = member.architecture; archStr[4] = 0; std::cerr << memberName << " (" << archStr << ")\n"; } if(member.architecture == 'pwpc' || member.architecture == '\?\?\?\?') { fs::path shlib_file(tmpdir / (memberName + ".o")); MakeImportLibrary(member.data, member.length, shlib_file, tmpdir); arArguments.push_back(shlib_file.string()); } } RunCommand("powerpc-apple-macos-ar", arArguments); fs::rename(archiveTmp, libname); } catch(...) { fs::remove_all(tmpdir); throw; } fs::remove_all(tmpdir); return true; } int main (int argc, char * const argv[]) { //printf("%d\n",argc); if(argc != 3) { cerr << "Usage: makeimport " << endl; return 1; } int fd = open(argv[1], O_RDONLY, 0); if(fd < 0) { perror(argv[1]); return 1; } if(!MakeImportLibraryMulti(argv[1], argv[2])) return 1; close(fd); return 0; }