sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

patch_elf_32bit.c (5282B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <stdint.h>
      5 
      6 #define ARMAG  "!<arch>\n"
      7 #define SARMAG 8
      8 
      9 /* from elf.h */
     10 
     11 /* Type for a 16-bit quantity.  */
     12 typedef uint16_t Elf32_Half;
     13 
     14 /* Types for signed and unsigned 32-bit quantities.  */
     15 typedef uint32_t Elf32_Word;
     16 
     17 /* Type of addresses.  */
     18 typedef uint32_t Elf32_Addr;
     19 
     20 /* Type of file offsets.  */
     21 typedef uint32_t Elf32_Off;
     22 
     23 /* The ELF file header.  This appears at the start of every ELF file.  */
     24 
     25 #define EI_NIDENT (16)
     26 
     27 typedef struct
     28 {
     29   unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
     30   Elf32_Half	e_type;			/* Object file type */
     31   Elf32_Half	e_machine;		/* Architecture */
     32   Elf32_Word	e_version;		/* Object file version */
     33   Elf32_Addr	e_entry;		/* Entry point virtual address */
     34   Elf32_Off	e_phoff;		/* Program header table file offset */
     35   Elf32_Off	e_shoff;		/* Section header table file offset */
     36   Elf32_Word	e_flags;		/* Processor-specific flags */
     37   Elf32_Half	e_ehsize;		/* ELF header size in bytes */
     38   Elf32_Half	e_phentsize;		/* Program header table entry size */
     39   Elf32_Half	e_phnum;		/* Program header table entry count */
     40   Elf32_Half	e_shentsize;		/* Section header table entry size */
     41   Elf32_Half	e_shnum;		/* Section header table entry count */
     42   Elf32_Half	e_shstrndx;		/* Section header string table index */
     43 } Elf32_Ehdr;
     44 
     45 /* Conglomeration of the identification bytes, for easy testing as a word.  */
     46 #define	ELFMAG		"\177ELF"
     47 #define	SELFMAG		4
     48 
     49 #define EI_CLASS	4		/* File class byte index */
     50 #define ELFCLASS32	1		/* 32-bit objects */
     51 
     52 #define EI_DATA		5		/* Data encoding byte index */
     53 #define ELFDATA2MSB	2		/* 2's complement, big endian */
     54 
     55 /* end from elf.h */
     56 
     57 // This file will find all mips3 object files in an ar archive and set the ABI flags to O32
     58 // this allows gcc to link them with the mips2 object files.
     59 // Irix CC doesn't set the elf e_flags properly.
     60 
     61 // the AR file is structured as followed
     62 //"!<arch>" followed by 0x0A (linefeed) 8 characters
     63 // then a file header that follows the following structure
     64 // everything is represented using space padded characters
     65 // the last two characters are alwos 0x60 0x0A
     66 // then come the file contents
     67 // you can find the location of the next header by adding file_size_in_bytes (after parsing)
     68 // all file headers start at an even offset so if the file size in bytes is odd you have to add 1
     69 // the first two "files" are special.  One is a symbol table with a pointer to the header of the file
     70 // contaning the symbol the other is an extended list of filenames
     71 struct ar_header {
     72     char identifier[16];
     73     char file_modification_timestamp[12];
     74     char owner_id[6];
     75     char group_id[6];
     76     char file_mode[8];
     77     char file_size_in_bytes[10];
     78     char ending[2];
     79 };
     80 
     81 //These constants found by inspecting output of objdump
     82 #define FLAGS_MIPS3 0x20
     83 #define FLAGS_O32ABI 0x100000 
     84 
     85 int fix_mips_elf(FILE *f, size_t filesize)
     86 {
     87     Elf32_Ehdr hdr;
     88     if (filesize < sizeof(hdr) || (1 != fread(&hdr, sizeof(hdr), 1, f))) {
     89         printf("Failed to read ELF header\n");
     90         return -1;
     91     }
     92 
     93     if (strncmp((const char *) hdr.e_ident, ELFMAG, SELFMAG) == 0) {
     94         // found an ELF file.
     95         if (hdr.e_ident[EI_CLASS] != ELFCLASS32 || hdr.e_ident[EI_DATA] != ELFDATA2MSB) {
     96             printf("Expected 32bit big endian object files\n");
     97             return -1;
     98         }
     99 
    100         if ((hdr.e_flags & 0xFF) == FLAGS_MIPS3 && (hdr.e_flags & FLAGS_O32ABI) == 0) {
    101             hdr.e_flags |= FLAGS_O32ABI;
    102             fseek(f, -(long)sizeof(hdr), SEEK_CUR);
    103             if (1 != fwrite(&hdr, sizeof(hdr), 1, f)) {
    104                 printf("Failed to write back ELF header after patching.\n");
    105                 return -1;
    106             }
    107         }
    108     }
    109     return 0;
    110 }
    111 
    112 int fix_mips_ar(FILE *f)
    113 {
    114     struct ar_header current_header;
    115     fseek(f, 0x8, SEEK_SET); // skip header, this is safe enough given that we check to make sure the
    116                              // file header is valid
    117 
    118     while (1 == fread(&current_header, sizeof(current_header), 1, f)) {
    119         if (current_header.ending[0] != 0x60 && current_header.ending[1] != 0x0A) {
    120             printf("Expected file header\n");
    121             return -1;
    122         }
    123         size_t filesize = atoi(current_header.file_size_in_bytes);
    124         if (fix_mips_elf(f, filesize)) {
    125             return -1;
    126         }
    127         if (filesize % 2 == 1)
    128             filesize++;
    129         fseek(f, filesize - sizeof(Elf32_Ehdr), SEEK_CUR);
    130     }
    131     return 0;
    132 }
    133 
    134 int main(int argc, char **argv) {
    135     FILE *f = fopen(argv[1], "r+b");
    136     uint8_t magic[8];
    137     int status = 0;
    138 
    139     if (f == NULL) {
    140         printf("Failed to open file! %s\n", argv[1]);
    141         return -1;
    142     }
    143     if (1 != fread(&magic, sizeof(magic), 1, f)) {
    144         printf("Failed to read file magic\n");
    145         return -1;
    146     }
    147     rewind(f);
    148     if (!memcmp(ARMAG, magic, SARMAG)) {
    149         status = fix_mips_ar(f);
    150     } else if (!memcmp(ELFMAG, magic, SELFMAG)) {
    151         fseek(f, 0, SEEK_END);
    152         size_t filesize = ftell(f);
    153         rewind(f);
    154         status = fix_mips_elf(f, filesize);
    155     } else {
    156         printf("Unknown file magic: %02x%02x%02X%02X\n", magic[0], magic[1], magic[2], magic[3]);
    157         status = -1;
    158     }
    159     fclose(f);
    160     return status;
    161 }