/* * PSP Software Development Kit - https://github.com/pspdev * ----------------------------------------------------------------------- * Licensed under the BSD license, see LICENSE in PSPSDK root for details. * * psp-fixup-imports.c - Simple program to fixup an ELF's imports * * Copyright (c) 2005 James Forshaw * */ #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "types.h" #include "elftypes.h" #include "prxtypes.h" #include "sha1.h" #define PRX_LIBSTUB_SECT ".lib.stub" #define PRX_STUBTEXT_SECT ".sceStub.text" #define PRX_NID_SECT ".rodata.sceNid" struct NidMap { unsigned int oldnid; unsigned int newnid; }; #define MAX_MAPNIDS 1024 struct ImportMap { struct ImportMap *next; char name[32]; int count; /* Could fail on things like PAF but who is going to want to remap 1000+ nids ? */ struct NidMap nids[MAX_MAPNIDS]; }; static const char *g_outfile; static const char *g_infile; static const char *g_mapfile; static unsigned char *g_elfdata = NULL; static unsigned int g_elfsize; static struct ElfHeader g_elfhead = {0}; static struct ElfSection *g_elfsections = NULL; static struct ElfSection *g_modinfo = NULL; static struct ElfSection *g_libstub = NULL; static struct ElfSection *g_stubtext = NULL; static struct ElfSection *g_nid = NULL; static struct ElfSection *g_symtab = NULL; static struct ElfSection *g_strtab = NULL; static struct ImportMap *g_map = NULL; static int g_reversemap = 0; /* Specifies that the current usage is to the print the pspsdk path */ static int g_verbose = 0; static struct option arg_opts[] = { {"output", required_argument, NULL, 'o'}, {"reverse", no_argument, NULL, 'r' }, {"map", required_argument, NULL, 'm'}, {"verbose", no_argument, NULL, 'v'}, { NULL, 0, NULL, 0 } }; /* Process the arguments */ int process_args(int argc, char **argv) { int ch; g_outfile = NULL; g_infile = NULL; g_mapfile = NULL; ch = getopt_long(argc, argv, "vro:m:", arg_opts, NULL); while(ch != -1) { switch(ch) { case 'v' : g_verbose = 1; break; case 'o' : g_outfile = optarg; break; case 'm' : g_mapfile = optarg; break; case 'r' : g_reversemap = 1; break; default : break; }; ch = getopt_long(argc, argv, "vro:m:", arg_opts, NULL); } argc -= optind; argv += optind; if(argc < 1) { return 0; } g_infile = argv[0]; if(g_outfile == NULL) { g_outfile = argv[0]; } if(g_verbose) { fprintf(stderr, "Loading %s, outputting to %s\n", g_infile, g_outfile); } return 1; } void print_help(void) { fprintf(stderr, "Usage: psp-fixup-imports [-v] [-o outfile.elf] infile.elf\n"); fprintf(stderr, "Options:\n"); fprintf(stderr, "-o, --output outfile : Output to a different file\n"); fprintf(stderr, "-m, --map mapfile : Specify a firmware NID mapfile\n"); fprintf(stderr, "-r, --reverse : Reverse the mapping\n"); fprintf(stderr, "-v, --verbose : Verbose output\n"); } /* Scan through the sections trying to find this address */ const unsigned char *find_data(unsigned int iAddr) { int i; for(i = 0; i < g_elfhead.iShnum; i++) { if((g_elfsections[i].iAddr <= iAddr) && ((g_elfsections[i].iAddr + g_elfsections[i].iSize) > iAddr)) { return g_elfsections[i].pData + (iAddr - g_elfsections[i].iAddr); } } return NULL; } /* find symbol name. addr needs to be in host endianness */ static const char *getsymname(unsigned addr) { if(!g_symtab || !g_strtab) return 0; if(g_symtab->iSize == 0 || g_symtab->pData == 0) return 0; if(g_strtab->iSize == 0 || g_strtab->pData == 0) return 0; Elf32_Sym *curr = (void*)g_symtab->pData; Elf32_Sym *end = curr + (g_symtab->iSize / sizeof(Elf32_Sym)); addr = LW(addr); while(curr < end) { if(curr->st_value == addr) { if(curr->st_name && curr->st_name < g_strtab->iSize) return (char*)g_strtab->pData + curr->st_name; else return 0; } ++curr; } return 0; } struct ImportMap *find_map_by_name(const char *name) { struct ImportMap *currmap = g_map; while(currmap) { if(strcmp(name, currmap->name) == 0) { break; } currmap = currmap->next; } return currmap; } unsigned char *load_file(const char *file, unsigned int *size) { FILE *fp; unsigned char *data = NULL; do { fp = fopen(file, "rb"); if(fp != NULL) { (void) fseek(fp, 0, SEEK_END); *size = ftell(fp); rewind(fp); if(*size < sizeof(Elf32_Ehdr)) { fprintf(stderr, "Error, invalid file size\n"); fclose(fp); break; } data = (unsigned char *) malloc(*size); if(data == NULL) { fprintf(stderr, "Error, could not allocate memory for ELF\n"); fclose(fp); break; } size_t result = fread(data, 1, *size, fp); if (result < 0) { fprintf(stderr, "Error, could not read the ELF\n"); fclose(fp); break; } fclose(fp); } else { fprintf(stderr, "Error, could not find file %s\n", file); } } while(0); return data; } /* Validate the ELF header */ int validate_header(unsigned char *data) { Elf32_Ehdr *head; int ret = 0; head = (Elf32_Ehdr*) data; do { /* Read in the header structure */ g_elfhead.iMagic = LW(head->e_magic); g_elfhead.iClass = head->e_class; g_elfhead.iData = head->e_data; g_elfhead.iIdver = head->e_idver; g_elfhead.iType = LH(head->e_type); g_elfhead.iMachine = LH(head->e_machine); g_elfhead.iVersion = LW(head->e_version); g_elfhead.iEntry = LW(head->e_entry); g_elfhead.iPhoff = LW(head->e_phoff); g_elfhead.iShoff = LW(head->e_shoff); g_elfhead.iFlags = LW(head->e_flags); g_elfhead.iEhsize = LH(head->e_ehsize); g_elfhead.iPhentsize = LH(head->e_phentsize); g_elfhead.iPhnum = LH(head->e_phnum); g_elfhead.iShentsize = LH(head->e_shentsize); g_elfhead.iShnum = LH(head->e_shnum); g_elfhead.iShstrndx = LH(head->e_shstrndx); if(g_verbose) { fprintf(stderr, "Magic %08x, Class %02x, Data %02x, Idver %02x\n", g_elfhead.iMagic, g_elfhead.iClass, g_elfhead.iData, g_elfhead.iIdver); fprintf(stderr, "Type %04x, Machine %04x, Version %08x, Entry %08x\n", g_elfhead.iType, g_elfhead.iMachine, g_elfhead.iVersion, g_elfhead.iEntry); fprintf(stderr, "Phoff %08x, Shoff %08x, Flags %08x, Ehsize %08x\n", g_elfhead.iPhoff, g_elfhead.iShoff, g_elfhead.iFlags, g_elfhead.iEhsize); fprintf(stderr, "Phentsize %04x, Phnum %04x\n", g_elfhead.iPhentsize, g_elfhead.iPhnum); fprintf(stderr, "Shentsize %04x, Shnum %08x, Shstrndx %04x\n", g_elfhead.iShentsize, g_elfhead.iShnum, g_elfhead.iShstrndx); } if(g_elfhead.iMagic != ELF_MAGIC) { fprintf(stderr, "Error, invalid magic in the header\n"); break; } if((g_elfhead.iType != ELF_EXEC_TYPE) && (g_elfhead.iType != ELF_PRX_TYPE)) { fprintf(stderr, "Error, not EXEC type elf\n"); break; } if(g_elfhead.iMachine != ELF_MACHINE_MIPS) { fprintf(stderr, "Error, not MIPS type ELF\n"); break; } if(g_elfhead.iShnum < g_elfhead.iShstrndx) { fprintf(stderr, "Error, number of headers is less than section string index\n"); break; } ret = 1; } while(0); return ret; } /* Load sections into ram */ int load_sections(unsigned char *data) { int ret = 0; unsigned int load_addr = 0xFFFFFFFF; if(g_elfhead.iShnum > 0) { do { Elf32_Shdr *sect; int i; g_elfsections = (struct ElfSection *) malloc(sizeof(struct ElfSection) * g_elfhead.iShnum); if(g_elfsections == NULL) { fprintf(stderr, "Error, could not allocate memory for sections\n"); break; } memset(g_elfsections, 0, sizeof(struct ElfSection) * g_elfhead.iShnum); for(i = 0; i < g_elfhead.iShnum; i++) { sect = (Elf32_Shdr *) (g_elfdata + g_elfhead.iShoff + (i * g_elfhead.iShentsize)); g_elfsections[i].iName = LW(sect->sh_name); g_elfsections[i].iType = LW(sect->sh_type); g_elfsections[i].iAddr = LW(sect->sh_addr); g_elfsections[i].iFlags = LW(sect->sh_flags); g_elfsections[i].iOffset = LW(sect->sh_offset); g_elfsections[i].iSize = LW(sect->sh_size); g_elfsections[i].iLink = LW(sect->sh_link); g_elfsections[i].iInfo = LW(sect->sh_info); g_elfsections[i].iAddralign = LW(sect->sh_addralign); g_elfsections[i].iEntsize = LW(sect->sh_entsize); g_elfsections[i].iIndex = i; if(g_elfsections[i].iOffset != 0) { g_elfsections[i].pData = g_elfdata + g_elfsections[i].iOffset; } if(g_elfsections[i].iFlags & SHF_ALLOC) { g_elfsections[i].blOutput = 1; if(g_elfsections[i].iAddr < load_addr) { load_addr = g_elfsections[i].iAddr; } } if(((g_elfsections[i].iType == SHT_REL) || (g_elfsections[i].iType == SHT_PRXRELOC)) && (g_elfsections[g_elfsections[i].iInfo].iFlags & SHF_ALLOC)) { g_elfsections[i].pRef = &g_elfsections[g_elfsections[i].iInfo]; } } /* Okay so we have loaded all the sections, lets fix up the names */ for(i = 0; i < g_elfhead.iShnum; i++) { strcpy(g_elfsections[i].szName, (char *) (g_elfsections[g_elfhead.iShstrndx].pData + g_elfsections[i].iName)); if(strcmp(g_elfsections[i].szName, PSP_MODULE_INFO_NAME) == 0) { g_modinfo = &g_elfsections[i]; } else if(strcmp(g_elfsections[i].szName, PRX_LIBSTUB_SECT) == 0) { g_libstub = &g_elfsections[i]; } else if(strcmp(g_elfsections[i].szName, PRX_STUBTEXT_SECT) == 0) { g_stubtext = &g_elfsections[i]; } else if(strcmp(g_elfsections[i].szName, PRX_NID_SECT) == 0) { g_nid = &g_elfsections[i]; } else if(strcmp(g_elfsections[i].szName, ".symtab") == 0) { g_symtab = &g_elfsections[i]; } else if(strcmp(g_elfsections[i].szName, ".strtab") == 0) { g_strtab = &g_elfsections[i]; } } if(g_verbose) { for(i = 0; i < g_elfhead.iShnum; i++) { fprintf(stderr, "\nSection %d: %s\n", i, g_elfsections[i].szName); fprintf(stderr, "Name %08x, Type %08x, Flags %08x, Addr %08x\n", g_elfsections[i].iName, g_elfsections[i].iType, g_elfsections[i].iFlags, g_elfsections[i].iAddr); fprintf(stderr, "Offset %08x, Size %08x, Link %08x, Info %08x\n", g_elfsections[i].iOffset, g_elfsections[i].iSize, g_elfsections[i].iLink, g_elfsections[i].iInfo); fprintf(stderr, "Addralign %08x, Entsize %08x pData %p\n", g_elfsections[i].iAddralign, g_elfsections[i].iEntsize, g_elfsections[i].pData); } fprintf(stderr, "ELF Load Base address %08x\n", load_addr); } if(g_modinfo == NULL) { fprintf(stderr, "Error, no sceModuleInfo section found\n"); break; } if(g_libstub == NULL) { fprintf(stderr, "Error, no .lib.stub section found\n"); break; } if(g_stubtext == NULL) { fprintf(stderr, "Error, no stub text section found\n"); break; } if(g_nid == NULL) { fprintf(stderr, "Error, no nid section found\n"); break; } ret = 1; } while(0); } else { fprintf(stderr, "Error, no sections in the ELF\n"); } return ret; } /* Load an ELF file */ int load_elf(const char *elf) { int ret = 0; do { g_elfdata = load_file(elf, &g_elfsize); if(g_elfdata == NULL) { break; } if(!validate_header(g_elfdata)) { break; } if(!load_sections(g_elfdata)) { break; } ret = 1; } while(0); return ret; } /* Free allocated memory */ void free_data(void) { if(g_elfdata != NULL) { free(g_elfdata); g_elfdata = NULL; } if(g_elfsections != NULL) { free(g_elfsections); g_elfsections = NULL; } } void strip_wsp(char *str) { int len; char *p; len = strlen(str); while((len > 0) && (isspace(str[len-1]))) { str[--len] = 0; } p = str; while(isspace(*p)) { p++; len--; } memmove(str, p, len); str[len] = 0; } /* Load map file in * File format is :- * @LibraryName followed by 0 or more * OLDNID=NEWNID [ Comment ] * * Read in badly :P */ int load_mapfile(const char *mapfile) { int ret = 1; char buf[1024]; struct ImportMap *currmap = NULL; int line = 0; if(mapfile == NULL) return ret; do { FILE *fp; fp = fopen(mapfile, "r"); if(fp == NULL) return ret; while(fgets(buf, sizeof(buf), fp)) { line++; strip_wsp(buf); if((buf[0]) && (buf[0] != '#')) { if(buf[0] == '@') { struct ImportMap *temp; temp = (struct ImportMap *) malloc(sizeof(struct ImportMap)); if(temp == NULL) { printf("Error allocating memory for import map\n"); ret = 0; break; } memset(temp, 0, sizeof(struct ImportMap)); if(currmap == NULL) { g_map = temp; } else { temp->next = currmap; g_map = temp; } currmap = temp; if(buf[1]) { strncpy(currmap->name, &buf[1], 32); currmap->name[31] = 0; } else { printf("Invalid library name at line %d\n", line); break; } if(g_verbose) { printf("Mapping library %s\n", currmap->name); } } else { unsigned int oldnid; unsigned int newnid; char *endp; if(currmap->count == MAX_MAPNIDS) { printf("Error, number of defined nids exceed maximum\n"); break; } /* Hex data should be prefixed with 0 */ if(buf[0] == '0') { errno = 0; oldnid = strtoul(buf, &endp, 16); if((errno != 0) || (*endp != ':')) { printf("Invalid NID entry on line %d\n", line); continue; } } else { unsigned char hash[SHA1_DIGEST_SIZE]; endp = strchr(buf, ':'); if(endp == NULL) { printf("Invalid NID entry on line %d\n", line); continue; } sha1(hash, (unsigned char *) buf, endp-buf); oldnid = hash[0] | (hash[1] << 8) | (hash[2] << 16) | (hash[3] << 24); } newnid = strtoul(endp+1, &endp, 16); if(g_verbose) { fprintf(stderr, "NID Mapping 0x%08x to 0x%08x\n", oldnid, newnid); } currmap->nids[currmap->count].oldnid = oldnid; currmap->nids[currmap->count].newnid = newnid; currmap->count++; } } } fclose(fp); } while(0); return ret; } #define MIPS_JR_31 0x03e00008 #define MIPS_NOP 0x0 int fixup_imports(void) { unsigned int *pText; unsigned int *pNid; struct PspModuleImport *pLastImport = NULL; int count; /* First let's check the sizes are correct */ if(g_stubtext->iSize != (g_nid->iSize * 2)) { fprintf(stderr, "Error, size of text section and nid section do not match\n"); return 0; } count = g_nid->iSize / 4; pText = (unsigned int *) g_stubtext->pData; pNid = (unsigned int *) g_nid->pData; if(g_verbose) { fprintf(stderr, "Import count %d\n", count); } while(count > 0) { unsigned int stub_addr; unsigned int stub_nid; unsigned int sect_nid; stub_addr = LW(pText[0]); stub_nid = LW(pText[1]); sect_nid = LW(pNid[0]); /* Check if this is an original format NID */ if((stub_addr != MIPS_JR_31) || (stub_nid != MIPS_NOP)) { struct PspModuleImport *pImport; u16 func_count; if(g_verbose) { const char *tmp = getsymname(stub_addr); fprintf(stderr, "Found import to fixup. pStub %08x (%s), Nid %08x, NidInSect %08x\n", stub_addr, tmp ? tmp : "", stub_nid, sect_nid); } if(stub_nid != sect_nid) { fprintf(stderr, "Error, unmatched NIDs\n"); return 0; } if((stub_addr < g_libstub->iAddr) || (stub_addr > (g_libstub->iAddr + g_libstub->iSize)) || (stub_addr & 3)) { fprintf(stderr, "Error, invalid stub address\n"); return 0; } pImport = (struct PspModuleImport *) (g_libstub->pData + (stub_addr - g_libstub->iAddr)); if(g_verbose) { const char *tmp = getsymname(LW(pImport->name)); fprintf(stderr, "Import Stub %p, %08x (%s), %08x, %02x, %02x, %04x, %08x, %08x\n", pImport, LW(pImport->name), tmp?tmp:"", LW(pImport->flags), pImport->entry_size, pImport->var_count, LH(pImport->func_count), LW(pImport->nids), LW(pImport->funcs)); } func_count = LH(pImport->func_count); if(func_count == 0) { /* Setup the stub */ SW(&pImport->nids, ((unsigned char *) pNid - g_nid->pData) + g_nid->iAddr); SW(&pImport->funcs, ((unsigned char *) pText - g_stubtext->pData) + g_stubtext->iAddr); } else { if((pLastImport) && (pImport != pLastImport)) { fprintf(stderr, "Warning: could not fixup imports, stubs out of order.\n"); fprintf(stderr, "Ensure the SDK libraries are linked in last to correct this.\n"); fprintf(stderr, "Continuing, your binary may or not work.\n"); } } pLastImport = pImport; func_count++; SH(&pImport->func_count, func_count); SW(&pText[0], MIPS_JR_31); SW(&pText[1], MIPS_NOP); } else { /* Set last import to some value so we know if we have out of order stubs over a fixed stub table */ pLastImport = (struct PspModuleImport *) pText; } pText += 2; pNid++; count--; } /* Should indicate no error occurred */ if(count == 0) { if(g_verbose) { fprintf(stderr, "No libraries to fixup\n"); } } return 1; } int fixup_nidmap(void) { struct PspModuleImport *pImport; int size; pImport = (struct PspModuleImport *) g_libstub->pData; size = g_libstub->iSize; while(size >= sizeof(struct PspModuleImport)) { const char *str; str = (const char*) find_data(LW(pImport->name)); if(str) { struct ImportMap *pMap; pMap = find_map_by_name(str); if(pMap) { int count; unsigned int *pNid; pNid = (unsigned int*) find_data(LW(pImport->nids)); count = LH(pImport->func_count) + pImport->var_count; if(pNid && (count > 0)) { if(g_verbose) { fprintf(stderr, "Mapping library %s\n", str); } while(count > 0) { int i; for(i = 0; i < pMap->count; i++) { unsigned oldnid, newnid; if(g_reversemap) { oldnid = pMap->nids[i].newnid; newnid = pMap->nids[i].oldnid; } else { newnid = pMap->nids[i].newnid; oldnid = pMap->nids[i].oldnid; } if(oldnid == *pNid) { if(g_verbose) { fprintf(stderr, "Mapping 0x%08x to 0x%08x\n", oldnid, newnid); } *pNid = newnid; break; } } pNid++; count--; } } } } pImport++; size -= sizeof(struct PspModuleImport); } return 1; } int main(int argc, char **argv) { int ret = 0; if(process_args(argc, argv)) { if(load_mapfile(g_mapfile) && load_elf(g_infile)) { if(fixup_imports() && fixup_nidmap()) { FILE *fp; fp = fopen(g_outfile, "wb"); if(fp != NULL) { (void) fwrite(g_elfdata, 1, g_elfsize, fp); fclose(fp); } else { fprintf(stderr, "Error, couldn't open %s for writing\n", g_outfile); return 0; } } else { ret = 1; } free_data(); } else { ret = 1; } } else { print_help(); ret = 1; } return ret; }