diff --git a/Makefile b/Makefile index 6e426ca6..7336806c 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ TARGET_N64 ?= 1 # ido - uses the SGI IRIS Development Option compiler, which is used to build # an original matching N64 ROM # gcc - uses the GNU C Compiler -COMPILER ?= ido +COMPILER ?= gcc $(eval $(call validate-option,COMPILER,ido gcc)) @@ -327,7 +327,7 @@ else endif endif ifeq ($(COMPILER),gcc) - LD := $(CROSS)ld + LD := LD_LIBRARY_PATH=$(LD_PATH) $(LD_PATH)/mips64-elf-ld else LD := LD_LIBRARY_PATH=$(LD_PATH) $(LD_PATH)/mips64-elf-ld endif diff --git a/lib/ultra/os/parameters.s b/lib/ultra/os/parameters.s index d6b3d048..42f5f3c0 100644 --- a/lib/ultra/os/parameters.s +++ b/lib/ultra/os/parameters.s @@ -31,21 +31,4 @@ ABS(__osBbPakBindings, 0x80000394) ABS(__osBbStateName, 0x800003a4) ABS(__osBbStateDirty, 0x800003b4) ABS(__osBbAuxDataLimit, 0x800003b8) - -/* padding */ -.fill 0x64 -#else -/* padding */ -/* JP is the only version without padding even though 2.0D has it (US) */ -#if (LIBULTRA_VERSION > OS_VER_D) || (LIBULTRA_VERSION == OS_VER_D && LIBULTRA_REVISION >= 1) -.repeat 0x34 -.byte 0 -.endr -#endif -#if LIBULTRA_VERSION >= OS_VER_H -.repeat 0x20 -.byte 0 -.endr -#endif - #endif diff --git a/libultra.mk b/libultra.mk index 527fdd87..e8e8ed2d 100644 --- a/libultra.mk +++ b/libultra.mk @@ -49,7 +49,7 @@ endif # LIBGULTRA - whenever to compile libultra using IDO or GNU # 1 - uses egcs to match iQue (uses gcc if COMPILER is gcc) # 0 - uses ido, used to match JP, US, EU and Shindou -LIBGULTRA ?= 0 +LIBGULTRA ?= 1 $(eval $(call validate-option,LIBGULTRA,0 1)) ULTRA_C_FILES := $(foreach dir,$(ULTRA_SRC_DIRS),$(wildcard $(dir)/*.c)) @@ -83,7 +83,7 @@ ULTRA_CFLAGS = -non_shared -Wab,-r4300_mul -Xcpluscomm -Xfullwarn -G 0 -signed ULTRA_ASFLAGS = -non_shared -Wab,-r4300_mul -Xcpluscomm -Xfullwarn -G 0 -nostdinc -o32 -c GULTRA_CC := COMPILER_PATH=$(EGCS_PATH) $(EGCS_PATH)/gcc -GULTRA_CFLAGS = -mcpu=r4300 -fno-pic -Wa,--strip-local-absolute -G 0 +GULTRA_CFLAGS = -mcpu=r4300 -fno-pic -Wa,--strip-local-absolute -G 0 -fno-common GULTRA_ASFLAGS = -mcpu=r4300 -fno-pic -x assembler-with-cpp -c -DEGCS_GCC ifeq ($(LIBGULTRA),1) diff --git a/sm64.ld b/sm64.ld index a5364b84..559985a6 100755 --- a/sm64.ld +++ b/sm64.ld @@ -230,7 +230,7 @@ SECTIONS #undef JP_PADDING_TEXT #endif -#ifdef VERSION_CN +#ifndef __sgi BUILD_DIR/libgcc.a:_divdi3.o(.text); BUILD_DIR/libgcc.a:_moddi3.o(.text); BUILD_DIR/libgcc.a:_udivdi3.o(.text); @@ -390,7 +390,7 @@ SECTIONS #include "lib/ultra/ld.inc" #undef SECTION -#ifdef VERSION_CN +#ifndef __sgi BUILD_DIR/libgcc.a:_divdi3.o(.rodata*); BUILD_DIR/libgcc.a:_moddi3.o(.rodata*); BUILD_DIR/libgcc.a:_udivdi3.o(.rodata*); diff --git a/tools/patch_elf_32bit.c b/tools/patch_elf_32bit.c index 8e529520..e2cb32bd 100644 --- a/tools/patch_elf_32bit.c +++ b/tools/patch_elf_32bit.c @@ -1,3 +1,10 @@ +// This file will find all mips3 object files in an ar archive and set the ABI flags to O32 +// this allows gcc to link them with the mips2 object files. +// Irix CC doesn't set the elf e_flags properly. +// +// In addition, it sorts symbol tables to put local symbols before global ones, +// which is required by modern ld. + #include #include #include @@ -13,6 +20,7 @@ typedef uint16_t Elf32_Half; /* Types for signed and unsigned 32-bit quantities. */ typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; /* Type of addresses. */ typedef uint32_t Elf32_Addr; @@ -26,37 +34,82 @@ typedef uint32_t Elf32_Off; typedef struct { - unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ - Elf32_Half e_type; /* Object file type */ - Elf32_Half e_machine; /* Architecture */ - Elf32_Word e_version; /* Object file version */ - Elf32_Addr e_entry; /* Entry point virtual address */ - Elf32_Off e_phoff; /* Program header table file offset */ - Elf32_Off e_shoff; /* Section header table file offset */ - Elf32_Word e_flags; /* Processor-specific flags */ - Elf32_Half e_ehsize; /* ELF header size in bytes */ - Elf32_Half e_phentsize; /* Program header table entry size */ - Elf32_Half e_phnum; /* Program header table entry count */ - Elf32_Half e_shentsize; /* Section header table entry size */ - Elf32_Half e_shnum; /* Section header table entry count */ - Elf32_Half e_shstrndx; /* Section header string table index */ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ } Elf32_Ehdr; +typedef struct { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + + /* Conglomeration of the identification bytes, for easy testing as a word. */ -#define ELFMAG "\177ELF" -#define SELFMAG 4 +#define ELFMAG "\177ELF" +#define SELFMAG 4 -#define EI_CLASS 4 /* File class byte index */ -#define ELFCLASS32 1 /* 32-bit objects */ +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASS32 1 /* 32-bit objects */ -#define EI_DATA 5 /* Data encoding byte index */ -#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ + +#define ELF32_ST_BIND(i) ((i)>>4) +#define ELF32_R_SYM(i) ((i)>>8) +#define ELF32_R_TYPE(i) ((unsigned char)(i)) +#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t)) + +#define SHT_SYMTAB 2 +#define SHT_RELA 4 +#define SHT_NOBITS 8 +#define SHT_REL 9 + +#define STB_LOCAL 0 /* end from elf.h */ -// This file will find all mips3 object files in an ar archive and set the ABI flags to O32 -// this allows gcc to link them with the mips2 object files. -// Irix CC doesn't set the elf e_flags properly. +#define BSWAP16(x) ((uint16_t)(((x) & 0xff00) >> 8) | (uint16_t)(((x) & 0x00ff) << 8)) +#define BSWAP32(x) (uint32_t)(BSWAP16((x) >> 16) | (uint32_t)(BSWAP16(x) << 16)) // the AR file is structured as followed //"!" followed by 0x0A (linefeed) 8 characters @@ -67,7 +120,7 @@ typedef struct // you can find the location of the next header by adding file_size_in_bytes (after parsing) // all file headers start at an even offset so if the file size in bytes is odd you have to add 1 // the first two "files" are special. One is a symbol table with a pointer to the header of the file -// contaning the symbol the other is an extended list of filenames +// containing the symbol the other is an extended list of filenames struct ar_header { char identifier[16]; char file_modification_timestamp[12]; @@ -78,34 +131,180 @@ struct ar_header { char ending[2]; }; -//These constants found by inspecting output of objdump +// These constants found by inspecting output of objdump #define FLAGS_MIPS3 0x20 #define FLAGS_O32ABI 0x100000 +void *malloc_checked(size_t size) +{ + void *ret = calloc(size, 1); + if (!ret) { + printf("Failed to allocate memory\n"); + exit(-1); + } + return ret; +} + +Elf32_Sym *syms; + +int cmpsym(const void *lhs, const void *rhs) +{ + size_t ind1 = *(size_t *)lhs; + size_t ind2 = *(size_t *)rhs; + int local1 = ELF32_ST_BIND(syms[ind1].st_info) == STB_LOCAL; + int local2 = ELF32_ST_BIND(syms[ind2].st_info) == STB_LOCAL; + if (local1 != local2) { + return local1 ? -1 : 1; + } + if (ind1 != ind2) { + return ind1 < ind2 ? -1 : 1; + } + return 0; +} + int fix_mips_elf(FILE *f, size_t filesize) { - Elf32_Ehdr hdr; - if (filesize < sizeof(hdr) || (1 != fread(&hdr, sizeof(hdr), 1, f))) { - printf("Failed to read ELF header\n"); + int changed = 0; + uint8_t *buf = malloc_checked(filesize); + if (1 != fread(buf, filesize, 1, f)) { + printf("Failed to read ELF\n"); + return -1; + } + fseek(f, -(long)filesize, SEEK_CUR); + + if (filesize < sizeof(Elf32_Ehdr)) { + printf("truncated header\n"); return -1; } - if (strncmp((const char *) hdr.e_ident, ELFMAG, SELFMAG) == 0) { - // found an ELF file. - if (hdr.e_ident[EI_CLASS] != ELFCLASS32 || hdr.e_ident[EI_DATA] != ELFDATA2MSB) { - printf("Expected 32bit big endian object files\n"); + Elf32_Ehdr *hdr = (Elf32_Ehdr *)buf; + if (hdr->e_ident[EI_CLASS] != ELFCLASS32 || hdr->e_ident[EI_DATA] != ELFDATA2MSB) { + printf("Expected 32bit big endian object files\n"); + return -1; + } + + if ((hdr->e_flags & 0xFF) == FLAGS_MIPS3 && (hdr->e_flags & FLAGS_O32ABI) == 0) { + hdr->e_flags |= FLAGS_O32ABI; + changed = 1; + } + + size_t e_shoff = BSWAP32(hdr->e_shoff); + size_t num_sections = BSWAP16(hdr->e_shnum); + if (BSWAP16(hdr->e_shentsize) != sizeof(Elf32_Shdr) || + e_shoff > filesize || + filesize - e_shoff < num_sections * sizeof(Elf32_Shdr)) { + printf("bad e_shentsize/e_shoff\n"); + return -1; + } + + Elf32_Shdr *sections = (Elf32_Shdr *)(buf + e_shoff); + Elf32_Shdr *symtab = NULL; + for (size_t i = 0; i < num_sections; i++) { + Elf32_Shdr *s = §ions[i]; + Elf32_Off sh_offset = BSWAP32(s->sh_offset); + if (BSWAP32(s->sh_type) != SHT_NOBITS && + (sh_offset > filesize || filesize - sh_offset < BSWAP32(s->sh_size))) { + printf("bad section data\n"); return -1; } + if (BSWAP32(sections[i].sh_type) == SHT_SYMTAB) { + symtab = §ions[i]; + } + } - if ((hdr.e_flags & 0xFF) == FLAGS_MIPS3 && (hdr.e_flags & FLAGS_O32ABI) == 0) { - hdr.e_flags |= FLAGS_O32ABI; - fseek(f, -(long)sizeof(hdr), SEEK_CUR); - if (1 != fwrite(&hdr, sizeof(hdr), 1, f)) { - printf("Failed to write back ELF header after patching.\n"); + if (symtab == NULL) { + printf("missing symtab\n"); + return -1; + } + + Elf32_Word symtab_size = BSWAP32(symtab->sh_size); + if (BSWAP32(symtab->sh_entsize) != sizeof(Elf32_Sym) || + symtab_size % sizeof(Elf32_Sym) != 0) { + printf("bad symtab sh_size/sh_entsize\n"); + return -1; + } + + syms = (Elf32_Sym *)(buf + BSWAP32(symtab->sh_offset)); + size_t num_syms = symtab_size / sizeof(Elf32_Sym); + size_t num_locals = 0; + size_t *new_sym_order = malloc_checked(num_syms * sizeof(*new_sym_order)); + for (size_t i = 0; i < num_syms; i++) { + new_sym_order[i] = i; + if (ELF32_ST_BIND(syms[i].st_info) == STB_LOCAL) { + num_locals++; + } + } + qsort(new_sym_order, num_syms, sizeof(*new_sym_order), cmpsym); + + Elf32_Sym *new_syms = malloc_checked(symtab_size); + for (size_t i = 0; i < num_syms; i++) { + new_syms[i] = syms[new_sym_order[i]]; + } + if (memcmp(syms, new_syms, symtab_size)) { + changed = 1; + } + memcpy(syms, new_syms, symtab_size); + + if (symtab->sh_info != BSWAP32(num_locals)) { + changed = 1; + } + symtab->sh_info = BSWAP32(num_locals); + + size_t *new_sym_order_inv = malloc_checked(num_syms * sizeof(*new_sym_order)); + for (size_t i = 0; i < num_syms; i++) { + new_sym_order_inv[new_sym_order[i]] = i; + } + + for (size_t i = 0; i < num_sections; i++) { + Elf32_Shdr *s = §ions[i]; + if (BSWAP32(s->sh_type) == SHT_REL) { + if (BSWAP32(s->sh_entsize) != sizeof(Elf32_Rel) || + BSWAP32(s->sh_size) % sizeof(Elf32_Rel) != 0) { + printf("bad rel section\n"); return -1; } + Elf32_Rel *rels = (Elf32_Rel *)(buf + BSWAP32(s->sh_offset)); + size_t num_rels = BSWAP32(s->sh_size) / sizeof(Elf32_Rel); + for (size_t j = 0; j < num_rels; j++) { + Elf32_Word r_info = BSWAP32(rels[j].r_info); + Elf32_Word sym = ELF32_R_SYM(r_info); + Elf32_Word type = ELF32_R_TYPE(r_info); + sym = (Elf32_Word)new_sym_order_inv[sym]; + r_info = ELF32_R_INFO(sym, type); + rels[j].r_info = BSWAP32(r_info); + } + } + + if (BSWAP32(s->sh_type) == SHT_RELA) { + if (BSWAP32(s->sh_entsize) != sizeof(Elf32_Rela) || + BSWAP32(s->sh_size) % sizeof(Elf32_Rela) != 0) { + printf("bad rela section\n"); + return -1; + } + Elf32_Rela *rels = (Elf32_Rela *)(buf + BSWAP32(s->sh_offset)); + size_t num_rels = BSWAP32(s->sh_size) / sizeof(Elf32_Rela); + for (size_t j = 0; j < num_rels; j++) { + Elf32_Word r_info = BSWAP32(rels[j].r_info); + Elf32_Word sym = ELF32_R_SYM(r_info); + Elf32_Word type = ELF32_R_TYPE(r_info); + sym = (Elf32_Word)new_sym_order_inv[sym]; + r_info = ELF32_R_INFO(sym, type); + rels[j].r_info = BSWAP32(r_info); + } } } + + if (changed) { + if (1 != fwrite(buf, filesize, 1, f)) { + printf("Failed to write back ELF after patching.\n"); + return -1; + } + fseek(f, -(long)filesize, SEEK_CUR); + } + free(buf); + free(new_syms); + free(new_sym_order); + free(new_sym_order_inv); return 0; } @@ -121,12 +320,26 @@ int fix_mips_ar(FILE *f) return -1; } size_t filesize = atoi(current_header.file_size_in_bytes); - if (fix_mips_elf(f, filesize)) { - return -1; + + if (filesize >= SELFMAG) { + uint8_t magic[SELFMAG]; + + if (1 != fread(magic, SELFMAG, 1, f)) { + printf("Failed to read ELF magic\n"); + return -1; + } + fseek(f, -SELFMAG, SEEK_CUR); + + if (memcmp(magic, ELFMAG, SELFMAG) == 0) { + if (fix_mips_elf(f, filesize)) { + return -1; + } + } } + if (filesize % 2 == 1) filesize++; - fseek(f, filesize - sizeof(Elf32_Ehdr), SEEK_CUR); + fseek(f, filesize, SEEK_CUR); } return 0; }