New relocation-generating program (#393)

* git subrepo clone git@github.com:EllipticEllipsis/fado.git tools/fado

subrepo:
  subdir:   "tools/fado"
  merged:   "d202857b"
upstream:
  origin:   "git@github.com:EllipticEllipsis/fado.git"
  branch:   "master"
  commit:   "d202857b"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* Makefile adjustments and additions to build fado

* git subrepo pull --force tools/fado

subrepo:
  subdir:   "tools/fado"
  merged:   "46c4d751"
upstream:
  origin:   "git@github.com:EllipticEllipsis/fado.git"
  branch:   "master"
  commit:   "46c4d751"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* git subrepo pull --force tools/fado

subrepo:
  subdir:   "tools/fado"
  merged:   "88114ebc"
upstream:
  origin:   "git@github.com:EllipticEllipsis/fado.git"
  branch:   "master"
  commit:   "88114ebc"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* Fix typo in makefile

* Fix it, maybe?

* git subrepo pull tools/fado

subrepo:
  subdir:   "tools/fado"
  merged:   "f7efb10a9"
upstream:
  origin:   "git@github.com:EllipticEllipsis/fado.git"
  branch:   "master"
  commit:   "f7efb10a9"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* Update build tools
This commit is contained in:
EllipticEllipsis
2022-08-04 04:49:25 +01:00
committed by GitHub
parent 4693978015
commit 26c8cdd221
44 changed files with 5416 additions and 443 deletions
+27 -11
View File
@@ -113,6 +113,7 @@ ELF2ROM := tools/buildtools/elf2rom
MKLDSCRIPT := tools/buildtools/mkldscript
YAZ0 := tools/buildtools/yaz0
ZAPD := tools/ZAPD/ZAPD.out
FADO := tools/fado/fado.elf
OPTFLAGS := -O2 -g3
ASFLAGS := -march=vr4300 -32 -Iinclude
@@ -184,9 +185,11 @@ O_FILES := $(foreach f,$(S_FILES:.s=.o),build/$f) \
$(foreach f,$(C_FILES:.c=.o),build/$f) \
$(foreach f,$(BASEROM_FILES),build/$f.o)
OVL_RELOC_FILES := $(shell $(CPP) $(CPPFLAGS) $(SPEC) | grep -o '[^"]*_reloc.o' )
# Automatic dependency files
# (Only asm_processor dependencies are handled for now)
DEP_FILES := $(O_FILES:.o=.asmproc.d)
# (Only asm_processor dependencies and reloc dependencies are handled for now)
DEP_FILES := $(O_FILES:.o=.asmproc.d) $(OVL_RELOC_FILES:.o=.d)
# create build directories
$(shell mkdir -p build/baserom $(foreach dir,$(SRC_DIRS) $(ASM_DIRS) $(ASSET_BIN_DIRS),build/$(dir)))
@@ -262,9 +265,20 @@ $(ROM): $(ELF)
$(ROMC): $(ROM)
python3 tools/z64compress_wrapper.py $(COMPFLAGS) $(ROM) $@ $(ELF) build/$(SPEC)
$(ELF): $(TEXTURE_FILES_OUT) $(ASSET_FILES_OUT) $(O_FILES) build/ldscript.txt build/undefined_syms.txt
$(ELF): $(TEXTURE_FILES_OUT) $(ASSET_FILES_OUT) $(O_FILES) $(OVL_RELOC_FILES) build/ldscript.txt build/undefined_syms.txt
$(LD) -T build/undefined_syms.txt -T build/ldscript.txt --no-check-sections --accept-unknown-input-arch --emit-relocs -Map build/mm.map -o $@
## Order-only prerequisites
# These ensure e.g. the O_FILES are built before the OVL_RELOC_FILES.
# The intermediate phony targets avoid quadratically-many dependencies between the targets and prerequisites.
o_files: $(O_FILES)
$(OVL_RELOC_FILES): | o_files
asset_files: $(TEXTURE_FILES_OUT) $(ASSET_FILES_OUT)
$(O_FILES): | asset_files
.PHONY: o_files asset_files
#### Main commands ####
@@ -313,9 +327,11 @@ init:
build/undefined_syms.txt: undefined_syms.txt
$(CPP) $(CPPFLAGS) $< > build/undefined_syms.txt
build/ldscript.txt: $(SPEC)
$(CPP) $(CPPFLAGS) $< > build/spec
$(MKLDSCRIPT) build/spec $@
build/$(SPEC): $(SPEC)
$(CPP) $(CPPFLAGS) $< > $@
build/ldscript.txt: build/$(SPEC)
$(MKLDSCRIPT) $< $@
build/asm/%.o: asm/%.s
$(AS) $(ASFLAGS) $< -o $@
@@ -334,13 +350,13 @@ build/data/%.o: data/%.s
build/src/overlays/%.o: src/overlays/%.c
$(CC_CHECK) $<
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
$(OBJDUMP_CMD)
# TODO: `() || true` is currently necessary to suppress `Error 1 (ignored)` make warnings caused by `test`, but this will go away if
# the following is moved to a separate rule that is only run once when all the required objects have been compiled.
$(ZAPD) bovl -eh -i $@ -cfg $< --outputpath $(@D)/$(notdir $(@D))_reloc.s
(test -f $(@D)/$(notdir $(@D))_reloc.s && $(AS) $(ASFLAGS) $(@D)/$(notdir $(@D))_reloc.s -o $(@D)/$(notdir $(@D))_reloc.o) || true
@$(OBJDUMP) -d $@ > $(@:.o=.s)
$(RM_MDEBUG)
build/src/overlays/%_reloc.o: build/$(SPEC)
$(FADO) $$(tools/buildtools/reloc_prereq $< $(notdir $*)) -n $(notdir $*) -o $(@:.o=.s) -M $(@:.o=.d)
$(AS) $(ASFLAGS) $(@:.o=.s) -o $@
build/src/%.o: src/%.c
$(CC_CHECK) $<
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
+1 -1
View File
@@ -640,7 +640,7 @@ beginseg
include "build/src/code/audio/audio_init_params.o"
include "build/src/code/jpegutils.o"
include "build/src/code/jpegdecoder.o"
include_readonly "build/src/code/z_game_over.o"
include_data_with_rodata "build/src/code/z_game_over.o"
include "build/src/code/z_construct.o"
include "build/data/code/audio_tables.rodata.o"
include "build/data/code/aspMain.rodata.o"
+2
View File
@@ -4,12 +4,14 @@ PROGRAMS := vtxdis
all: $(PROGRAMS)
$(MAKE) -C ZAPD
$(MAKE) -C fado
$(MAKE) -C buildtools
$(MAKE) -C z64compress
clean:
$(RM) $(PROGRAMS)
$(MAKE) -C ZAPD clean
$(MAKE) -C fado clean
$(MAKE) -C buildtools clean
$(MAKE) -C z64compress clean
+1
View File
@@ -3,4 +3,5 @@
elf2rom
makeromfs
mkldscript
reloc_prereq
yaz0
+21 -5
View File
@@ -1,16 +1,32 @@
CC := gcc
CFLAGS := -Wall -Wextra -pedantic -std=c99 -g -O2
PROGRAMS := yaz0 makeromfs elf2rom mkldscript
PROGRAMS := elf2rom makeromfs mkldscript reloc_prereq yaz0
ifeq ($(shell command -v clang >/dev/null 2>&1; echo $$?),0)
CC := clang
else
CC := gcc
endif
LLD ?= 0
ifeq ($(shell command -v ld.lld >/dev/null 2>&1; echo $$?),0)
LLD := 1
endif
ifneq ($(LLD),0)
CFLAGS += -fuse-ld=lld
endif
all: $(PROGRAMS)
clean:
$(RM) $(PROGRAMS)
mkldscript_SOURCES := mkldscript.c util.c
elf2rom_SOURCES := elf2rom.c elf32.c n64chksum.c util.c
yaz0_SOURCES := yaz0tool.c yaz0.c util.c
makeromfs_SOURCES := makeromfs.c n64chksum.c util.c
elf2rom_SOURCES := elf2rom.c elf32.c n64chksum.c util.c
makeromfs_SOURCES := makeromfs.c n64chksum.c util.c
mkldscript_SOURCES := mkldscript.c spec.c util.c
reloc_prereq_SOURCES := reloc_prereq.c spec.c util.c
yaz0_SOURCES := yaz0tool.c yaz0.c util.c
define COMPILE =
$(1): $($1_SOURCES)
File diff suppressed because it is too large Load Diff
+89
View File
@@ -0,0 +1,89 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "spec.h"
#include "util.h"
void print_usage(char* prog_name) {
printf("USAGE: %s SPEC OVERLAY_SEGMENT_NAME\n"
"Search the preprocessed SPEC for an overlay segment name, \n"
"e.g. \"ovl_En_Firefly\", and return a space-separated list of the files it\n"
"includes. The relocation file must be the last include in the segment\n"
"OVERLAY_SEGMENT_NAME, and have the filename \"OVERLAY_SEGMENT_NAME_reloc.o\",\n"
"but can be in a different directory from the other files.\n",
prog_name);
}
int main(int argc, char** argv) {
char* spec_path;
char* overlay_name;
char* spec;
size_t size;
Segment segment;
int exit_status = 0;
bool segmentFound = false;
if (argc != 3) {
print_usage(argv[0]);
return 1;
}
spec_path = argv[1];
overlay_name = argv[2];
// printf("spec path: %s\n", spec_path);
// printf("overlay name: %s\n", overlay_name);
spec = util_read_whole_file(spec_path, &size);
segmentFound = get_single_segment_by_name(&segment, spec, overlay_name);
if (!segmentFound) {
fprintf(stderr, ERRMSG_START "no segment \"%s\" found\n" ERRMSG_END, overlay_name);
goto error_out;
}
{
size_t overlay_name_length;
const char* reloc_suffix = "_reloc.o";
char* expected_filename;
/* Relocation file must be the last `include` (so .ovl section is linked last) */
if (strstr(segment.includes[segment.includesCount - 1].fpath, reloc_suffix) == NULL) {
fprintf(stderr, ERRMSG_START "last include in overlay segment \"%s\" is not a `%s` file\n" ERRMSG_END,
overlay_name, reloc_suffix);
goto error_out;
}
overlay_name_length = strlen(overlay_name);
expected_filename = malloc(overlay_name_length + strlen(reloc_suffix) + 1);
strcpy(expected_filename, overlay_name);
strcat(expected_filename, reloc_suffix);
if (strstr(segment.includes[segment.includesCount - 1].fpath, expected_filename) == NULL) {
fprintf(stderr, ERRMSG_START "Relocation file \"%s\" should have filename \"%s\"\n" ERRMSG_END,
segment.includes[segment.includesCount - 1].fpath, expected_filename);
goto error_out;
}
free(expected_filename);
}
{
int i;
/* Skip `_reloc.o` include */
for (i = 0; i < segment.includesCount - 1; i++) {
printf("%s ", segment.includes[i].fpath);
}
putchar('\n');
}
if (0) {
error_out:
exit_status = 1;
}
free_single_segment_elements(&segment);
free(spec);
return exit_status;
}
+365
View File
@@ -0,0 +1,365 @@
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "spec.h"
#define ARRAY_COUNT(arr) (sizeof(arr) / sizeof(arr[0]))
static struct Segment* add_segment(struct Segment** segments, int* segments_count) {
struct Segment* seg;
(*segments_count)++;
*segments = realloc(*segments, *segments_count * sizeof(**segments));
seg = &(*segments)[*segments_count - 1];
memset(seg, 0, sizeof(*seg));
seg->align = 16;
return seg;
}
static char* skip_whitespace(char* str) {
while (isspace(*str))
str++;
return str;
}
// null terminates the current token and returns a pointer to the next token
static char* token_split(char* str) {
while (!isspace(*str)) {
if (*str == 0)
return str; // end of string
str++;
}
*str = 0; // terminate token
str++;
return skip_whitespace(str);
}
// null terminates the current line and returns a pointer to the next line
static char* line_split(char* str) {
while (*str != '\n') {
if (*str == 0)
return str; // end of string
str++;
}
*str = 0; // terminate line
return str + 1;
}
static bool parse_number(const char* str, unsigned int* num) {
char* endptr;
long int n = strtol(str, &endptr, 0);
*num = n;
return endptr > str;
}
static bool parse_flags(char* str, unsigned int* flags) {
unsigned int f = 0;
while (str[0] != 0) {
char* next = token_split(str);
if (strcmp(str, "BOOT") == 0)
f |= FLAG_BOOT;
else if (strcmp(str, "OBJECT") == 0)
f |= FLAG_OBJECT;
else if (strcmp(str, "RAW") == 0)
f |= FLAG_RAW;
else if (strcmp(str, "NOLOAD") == 0)
f |= FLAG_NOLOAD;
else
return false;
str = next;
}
*flags = f;
return true;
}
static bool parse_quoted_string(char* str, char** out) {
if (*str != '"')
return false;
str++;
*out = str;
while (*str != '"') {
if (*str == 0)
return false; // unterminated quote
str++;
}
*str = 0;
str++;
str = skip_whitespace(str);
if (*str != 0)
return false; // garbage after filename
return true;
}
static bool is_pow_of_2(unsigned int n) {
return (n & (n - 1)) == 0;
}
// clang-format off
static const char* const stmtNames[] = {
[STMT_address] = "address",
[STMT_after] = "after",
[STMT_align] = "align",
[STMT_beginseg] = "beginseg",
[STMT_compress] = "compress",
[STMT_endseg] = "endseg",
[STMT_entry] = "entry",
[STMT_flags] = "flags",
[STMT_include] = "include",
[STMT_include_data_with_rodata] = "include_data_with_rodata",
[STMT_name] = "name",
[STMT_number] = "number",
[STMT_romalign] = "romalign",
[STMT_stack] = "stack",
[STMT_increment] = "increment",
[STMT_pad_text] = "pad_text",
};
// clang-format on
STMTId get_stmt_id_by_stmt_name(const char* stmtName, int lineNum) {
STMTId stmt;
for (stmt = 0; stmt < ARRAY_COUNT(stmtNames); stmt++) {
if (strcmp(stmtName, stmtNames[stmt]) == 0)
return stmt;
}
util_fatal_error("line %i: unknown statement '%s'", lineNum, stmtName);
return -1;
}
bool parse_segment_statement(struct Segment* currSeg, STMTId stmt, char* args, int lineNum) {
// ensure no duplicates (except for 'include' or 'pad_text')
if (stmt != STMT_include && stmt != STMT_include_data_with_rodata && stmt != STMT_pad_text &&
(currSeg->fields & (1 << stmt)))
util_fatal_error("line %i: duplicate '%s' statement", lineNum, stmtNames[stmt]);
currSeg->fields |= 1 << stmt;
currSeg->compress = false;
// statements valid within a segment definition
switch (stmt) {
case STMT_beginseg:
util_fatal_error("line %i: '%s' inside of a segment definition", lineNum, stmtNames[stmt]);
break;
case STMT_endseg:
// verify segment data
if (currSeg->name == NULL)
util_fatal_error("line %i: no name specified for segment", lineNum);
if (currSeg->includesCount == 0)
util_fatal_error("line %i: no includes specified for segment", lineNum);
return true;
break;
case STMT_name:
if (!parse_quoted_string(args, &currSeg->name))
util_fatal_error("line %i: invalid name", lineNum);
break;
case STMT_after:
if (!parse_quoted_string(args, &currSeg->after))
util_fatal_error("line %i: invalid name for 'after'", lineNum);
break;
case STMT_address:
if (!parse_number(args, &currSeg->address))
util_fatal_error("line %i: expected number after 'address'", lineNum);
break;
case STMT_number:
if (!parse_number(args, &currSeg->number))
util_fatal_error("line %i: expected number after 'number'", lineNum);
break;
case STMT_flags:
if (!parse_flags(args, &currSeg->flags))
util_fatal_error("line %i: invalid flags", lineNum);
break;
case STMT_align:
if (!parse_number(args, &currSeg->align))
util_fatal_error("line %i: expected number after 'align'", lineNum);
if (!is_pow_of_2(currSeg->align))
util_fatal_error("line %i: alignment is not a power of two", lineNum);
break;
case STMT_romalign:
if (!parse_number(args, &currSeg->romalign))
util_fatal_error("line %i: expected number after 'romalign'", lineNum);
if (!is_pow_of_2(currSeg->romalign))
util_fatal_error("line %i: alignment is not a power of two", lineNum);
break;
case STMT_include:
case STMT_include_data_with_rodata:
currSeg->includesCount++;
currSeg->includes = realloc(currSeg->includes, currSeg->includesCount * sizeof(*currSeg->includes));
if (!parse_quoted_string(args, &currSeg->includes[currSeg->includesCount - 1].fpath))
util_fatal_error("line %i: invalid filename", lineNum);
currSeg->includes[currSeg->includesCount - 1].linkerPadding = 0;
currSeg->includes[currSeg->includesCount - 1].dataWithRodata = (stmt == STMT_include_data_with_rodata);
break;
case STMT_increment:
if (!parse_number(args, &currSeg->increment))
util_fatal_error("line %i: expected number after 'increment'", lineNum);
break;
case STMT_compress:
currSeg->compress = true;
break;
case STMT_pad_text:
currSeg->includes[currSeg->includesCount - 1].linkerPadding += 0x10;
break;
default:
fprintf(stderr, "warning: '%s' is not implemented\n", stmtNames[stmt]);
break;
}
return false;
}
/**
* `segments` should be freed with `free_rom_spec` after use.
* Will write to the contents of `spec` to introduce string terminating '\0's.
* `segments` also contains pointers to inside `spec`, so `spec` should not be freed before `segments`
*/
void parse_rom_spec(char* spec, struct Segment** segments, int* segment_count) {
int lineNum = 1;
char* line = spec;
struct Segment* currSeg = NULL;
// iterate over lines
while (line[0] != 0) {
char* nextLine = line_split(line);
char* stmtName;
if (line[0] != 0) {
stmtName = skip_whitespace(line);
}
if (line[0] != 0 && stmtName[0] != 0) {
char* args = token_split(stmtName);
STMTId stmt = get_stmt_id_by_stmt_name(stmtName, lineNum);
if (currSeg != NULL) {
bool segmentEnded = parse_segment_statement(currSeg, stmt, args, lineNum);
if (segmentEnded) {
currSeg = NULL;
}
} else {
// commands valid outside a segment definition
switch (stmt) {
case STMT_beginseg:
currSeg = add_segment(segments, segment_count);
currSeg->includes = NULL;
break;
case STMT_endseg:
util_fatal_error("line %i: '%s' outside of a segment definition", lineNum, stmtName);
break;
default:
fprintf(stderr, "warning: '%s' is not implemented\n", stmtName);
break;
}
}
}
line = nextLine;
lineNum++;
}
}
/**
* @brief Parses the spec, looking only for the segment with the name `segmentName`.
* Returns true if the segment was found, false otherwise
*
* @param[out] dstSegment The Segment to be filled. Will only contain the data of the searched segment, or garbage if
* the segment was not found. dstSegment must be previously allocated, a stack variable is recommended
* @param[in,out] spec A null-terminated string containing the whole spec file. This string will be modified by this
* function
* @param[in] segmentName The name of the segment being searched
*/
bool get_single_segment_by_name(struct Segment* dstSegment, char* spec, const char* segmentName) {
bool insideSegment = false;
int lineNum = 1;
char* line = spec;
memset(dstSegment, 0, sizeof(struct Segment));
// iterate over lines
while (line[0] != '\0') {
char* nextLine = line_split(line);
char* stmtName = skip_whitespace(line);
if (stmtName[0] != '\0') {
char* args = token_split(stmtName);
STMTId stmt = get_stmt_id_by_stmt_name(stmtName, lineNum);
if (insideSegment) {
bool segmentEnded = parse_segment_statement(dstSegment, stmt, args, lineNum);
if (stmt == STMT_name) {
if (strcmp(segmentName, dstSegment->name) != 0) {
// Not the segment we are looking for
insideSegment = false;
}
} else if (segmentEnded) {
return true;
}
} else {
if (stmt == STMT_beginseg) {
insideSegment = true;
if (dstSegment->includes != NULL) {
free(dstSegment->includes);
}
memset(dstSegment, 0, sizeof(struct Segment));
}
}
}
line = nextLine;
lineNum++;
}
return false;
}
/**
* @brief Frees the elements of the passed Segment. Will not free the pointer itself
*
* @param segment
*/
void free_single_segment_elements(struct Segment* segment) {
if (segment->includes != NULL) {
free(segment->includes);
}
}
void free_rom_spec(struct Segment* segments, int segment_count) {
int i;
for (i = 0; i < segment_count; i++) {
if (segments[i].includes != NULL)
free(segments[i].includes);
}
free(segments);
}
+64
View File
@@ -0,0 +1,64 @@
#ifndef SPEC_H
#define SPEC_H
#include <stdint.h>
#include <stdbool.h>
typedef enum {
STMT_address,
STMT_after,
STMT_align,
STMT_beginseg,
STMT_compress,
STMT_endseg,
STMT_entry,
STMT_flags,
STMT_include,
STMT_include_data_with_rodata,
STMT_name,
STMT_number,
STMT_romalign,
STMT_stack,
STMT_increment,
STMT_pad_text,
} STMTId;
enum {
FLAG_BOOT = (1 << 0),
FLAG_OBJECT = (1 << 1),
FLAG_RAW = (1 << 2),
FLAG_NOLOAD = (1 << 3),
};
struct Include {
char* fpath;
int linkerPadding;
uint8_t dataWithRodata;
};
typedef struct Segment {
uint32_t fields;
char* name;
char* after;
uint32_t flags;
uint32_t address;
uint32_t stack;
uint32_t align;
uint32_t romalign;
uint32_t increment;
uint32_t entry;
uint32_t number;
struct Include* includes;
int includesCount;
bool compress;
} Segment;
void parse_rom_spec(char* spec, struct Segment** segments, int* segment_count);
bool get_single_segment_by_name(struct Segment* dstSegment, char *spec, const char *segmentName);
void free_single_segment_elements(struct Segment *segment);
void free_rom_spec(struct Segment* segments, int segment_count);
#endif
+5 -5
View File
@@ -12,18 +12,18 @@ void util_fatal_error(const char *msgfmt, ...)
{
va_list args;
fputs("error: ", stderr);
fputs(ERRMSG_START, stderr);
va_start(args, msgfmt);
vfprintf(stderr, msgfmt, args);
va_end(args);
fputc('\n', stderr);
fputs(ERRMSG_END "\n", stderr);
exit(1);
}
// reads a whole file into memory, and returns a pointer to the data
// reads a whole file into memory, and returns a malloc'd pointer to the data.
void *util_read_whole_file(const char *filename, size_t *pSize)
{
FILE *file = fopen(filename, "rb");
@@ -59,13 +59,13 @@ void *util_read_whole_file(const char *filename, size_t *pSize)
void util_write_whole_file(const char *filename, const void *data, size_t size)
{
FILE *file = fopen(filename, "wb");
if (file == NULL)
util_fatal_error("failed to open file '%s' for writing: %s", filename, strerror(errno));
if (fwrite(data, size, 1, file) != 1)
util_fatal_error("error writing to file '%s': %s", filename, strerror(errno));
fclose(file);
}
+10 -3
View File
@@ -1,8 +1,15 @@
#ifndef _UTIL_H_
#define _UTIL_H_
#ifndef UTIL_H
#define UTIL_H
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#define ERRMSG_START "\x1b[91merror\x1b[97m: "
#define ERRMSG_END "\x1b[0m"
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
__attribute__((format(printf, 1, 2), noreturn))
#endif
void util_fatal_error(const char *msgfmt, ...);
+23
View File
@@ -0,0 +1,23 @@
IndentWidth: 4
Language: Cpp
UseTab: Never
ColumnLimit: 120
PointerAlignment: Left
BreakBeforeBraces: Attach
SpaceAfterCStyleCast: false
Cpp11BracedListStyle: false
IndentCaseLabels: true
BinPackArguments: true
BinPackParameters: true
AlignAfterOpenBracket: Align
AlignOperands: true
BreakBeforeTernaryOperators: true
BreakBeforeBinaryOperators: None
AllowShortBlocksOnASingleLine: true
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AlignEscapedNewlines: Left
AlignTrailingComments: true
SortIncludes: false
+56
View File
@@ -0,0 +1,56 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# Custom
build/
.vscode
+12
View File
@@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = git@github.com:EllipticEllipsis/fado.git
branch = master
commit = f7efb10a9a65f27e9ccad7ce270234f20d386ac9
parent = 90cfafec47fe4b73ad9009e9501e147e86025aa6
method = merge
cmdver = 0.4.3
+661
View File
File diff suppressed because it is too large Load Diff
+74
View File
@@ -0,0 +1,74 @@
DEBUG ?= 0
LLD ?= 0
ASAN ?= 0
EXPERIMENTAL?= 0
ELF := fado.elf
CC := $(shell ./find_program.sh gcc clang clang-[0-9][0-9] clang-[0-9])
LD := $(shell ./find_program.sh ld ld.lld ld.lld-*)
INC := -I include -I lib
WARNINGS := -Wall -Wextra -Wpedantic -Wshadow -Werror=implicit-function-declaration -Wvla -Wno-unused-function
CFLAGS := -std=c11
LDFLAGS :=
ifeq ($(DEBUG),0)
OPTFLAGS := -O2
CFLAGS += -Werror
else
OPTFLAGS := -O0 -g3 -DDEBUG_ON
endif
ifneq ($(ASAN),0)
CFLAGS += -fsanitize=address -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize=undefined
endif
ifneq ($(LLD),0)
LDFLAGS += -fuse-ld=lld
else
ifneq ($(LD),ld)
LDFLAGS += -fuse-ld=lld
endif
endif
ifneq ($(EXPERIMENTAL),0)
CFLAGS += -DEXPERIMENTAL
endif
# GCC is too stupid to be trusted with these warnings
ifeq ($(CC),gcc)
WARNINGS += -Wno-implicit-fallthrough -Wno-maybe-uninitialized
endif
SRC_DIRS := $(shell find src -type d)
C_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c))
H_FILES := $(foreach dir,$(INC),$(wildcard $(dir)/*.h))
O_FILES := $(foreach f,$(C_FILES:.c=.o),build/$f)
LIB_DIRS := $(shell find lib -type d)
# exclude test file since we don't want it
C_LIB_FILES := $(filter-out lib/vc_vector/vc_vector_test.c, $(foreach dir,$(LIB_DIRS),$(wildcard $(dir)/*.c)))
O_LIB_FILES := $(foreach f,$(C_LIB_FILES:.c=.o),build/$f)
# Main targets
all: $(ELF)
clean:
$(RM) -r build $(ELF)
format:
clang-format-11 -i $(C_FILES) $(H_FILES) lib/fairy/*
.PHONY: all clean format
# create build directories
$(shell mkdir -p $(foreach dir,$(SRC_DIRS),build/$(dir)) $(foreach dir,$(LIB_DIRS),build/$(dir)))
$(ELF): $(O_FILES) $(O_LIB_FILES)
$(CC) $(INC) $(WARNINGS) $(CFLAGS) $(OPTFLAGS) $(LDFLAGS) -o $@ $^
build/%.o: %.c $(H_FILES)
$(CC) -c $(INC) $(WARNINGS) $(CFLAGS) $(OPTFLAGS) -o $@ $<
build/lib/%.o: lib/%.c
$(CC) -c $(INC) $(WARNINGS) $(CFLAGS) $(OPTFLAGS) -o $@ $<
+77
View File
@@ -0,0 +1,77 @@
# fado
*Fairy-Assisted (relocations for) Decomplied Overlays*
<!-- Nice backronym... -->
Contains
- **Fairy** a library for reading relocatable MIPS ELF object files (big-endian, suitable for Nintendo 64 games)
- **Fado** a program for generating the `.ovl`/relocation section for Zelda64 overlay files
- **Mido** an automatic dependency file generator
Compatible with both IDO and GCC (although [see below](N_B)). Both ordinary MIPS REL sections and RELA sections are now supported.
Output format is the standard "Zelda64" .ovl section, with the relocs divided by section, as used by
- *The Legend of Zelda: Ocarina of Time* (all Nintendo 64/Gamecube/iQue releases)
- *The Legend of Zelda: Majora's Mask* (all Nintendo 64/Gamecube releases)
In theory it will also work for other Nintendo 64 games that use this system, such as *Yoshi's Story*, but has yet to be tested with these.
## Explanation
The overlay relocation sections used by Zelda64 is described [here](z64_relocation_section_format.md). Fado will produce a `.ovl` section compatible with this format, although as noted there, some compilers need persuasion to produce compatible objects.
## How to use
Compile by running `make`.
A standalone invocation of Fado would look something like
```sh
./fado.elf z_en_hs2.o -n ovl_En_Hs2 -o ovl_En_Hs2_reloc.s
```
This takes as input the compiled object file from the C file (e.g. [this one](https://github.com/zeldaret/oot/blob/eadc477187888e1ae078d021b4a00b1366f0c9a4/src/overlays/actors/ovl_En_Hs2/z_en_hs2.c)), the name of the overlay (`ovl_En_Hs2`) and will output an assembly file `ovl_En_Hs2_reloc.s` containing the relocation section. An example output is included in the repo [here](ovl_En_Hs_reloc.s). Fado will print information from the object file to assist with debugging, by splitting relocs by section, and for each, printing the type, offset, and associated symbol (or section if static):
```mips
# TEXT RELOCS
.word 0x45000084 # R_MIPS_HI16 0x000084 .data
.word 0x4600008C # R_MIPS_LO16 0x00008C .data
.word 0x450000B4 # R_MIPS_HI16 0x0000B4 .rodata
.word 0x460000BC # R_MIPS_LO16 0x0000BC .rodata
.word 0x450000C0 # R_MIPS_HI16 0x0000C0 func_80A6F1A4
.word 0x460000C4 # R_MIPS_LO16 0x0000C4 func_80A6F1A4
```
If invoking in a makefile, you will probably want to generate these from a predefined filelist, and with the appropriate dependencies. [The Ocarina of Time decomp repository](http://github.com/zeldaret/oot) contains an example of how to do this using a supplementary program to parse the `spec` format.
More information can be obtained by running
```sh
./fado.elf --help
```
which contains information on the various options, such as automatic dependency file generation, etc.
## N.B.
- Fado expects the linker script to output symbols for the section sizes, and for them to be declared separately, in the format
```
_SEGMENTNAMESegmentSECTIONSize
```
e.g.
```
_ovl_En_Hs2SegmentTextSize
```
etc.
- By default Fado expects sections to be 0x10-aligned, as is usual for IDO. Some versions of GCC like to align sections to smaller widths, which Fado will handle appropriately, but the linker script must also address this, and at least the default settings seem unable to size the sections correctly due ot placing `fill`s in the wrong places. For now it is recommended to manually align sections to 0x10 if the compiler does not automatically.
- The experimental flag `--alignment`/`-a` can be passed to Fado, and it will use the alignment declared by each section in the elf file instead of padding them to 0x10 bytes, It should be noted this option has not been fully tested because currently we don't have any linker script tool that can properly address the incorrect placing of `fill`s. Fado must be rebuilt passing `EXPERIMENTAL=1` to be able to use this flag.
- To prevent GCC producing non-compliant HI/LOs, you must pass *both* of the following compiler flags: `-mno-explicit-relocs -mno-split-addresses`. See [here](z64_relocation_section_format.md#hilo) for more details.
- It is recommended, though not strictly required, that `-fno-merge-constants` is used for GCC, to avoid unpredictable section sizes, and comply with the Zelda64 relocation format's expectation of at most one rodata section. See [here](z64_relocation_section_format.md#rodata) for more details.
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
for i in "${@:2}"
do
RESULT=$(IFS=:;find $PATH -maxdepth 1 -name "$i" -print -quit 2> /dev/null | grep -o '[^/]*$')
COUNT=$(echo "$RESULT" | wc -c)
if [ $COUNT -gt 1 ]
then
echo $RESULT
exit 0
fi
done
echo $1
exit 0
+8
View File
@@ -0,0 +1,8 @@
/* Copyright (C) 2021 Elliptic Ellipsis */
/* SPDX-License-Identifier: AGPL-3.0-only */
#pragma once
#include <stdio.h>
void Fado_Relocs(FILE* outputFile, int inputFilesCount, FILE** inputFiles, const char* ovlName);
// void Fado_WriteRelocFile(FILE* outputFile, FILE** inputFiles, int inputFilesCount);
+25
View File
@@ -0,0 +1,25 @@
/* Copyright (C) 2021 Elliptic Ellipsis */
/* SPDX-License-Identifier: AGPL-3.0-only */
#pragma once
#include <getopt.h>
#include <unistd.h>
typedef struct {
struct option longOpt;
char* helpArg;
char* helpMsg;
} OptInfo;
typedef struct {
char* helpArg;
char* helpMsg;
} PosArgInfo;
/* Formatting sizes used by Help_PrintHelp. Change them before calling Help_PrintHelp to use custom values */
extern size_t helpTextWidth;
extern size_t helpDtIndent;
extern size_t helpDdIndent;
void Help_PrintHelp(const char* prologue, size_t posArgCount, const PosArgInfo* posArgInfo, size_t optCount,
const OptInfo* optInfo, const char* epilogue);

Some files were not shown because too many files have changed in this diff Show More