mirror of https://github.com/zeldaret/oot.git
Partial linking of overlay segments, relax linker script alignment
This commit is contained in:
parent
b204d6c089
commit
cf2b2716a7
71
Makefile
71
Makefile
|
|
@ -316,8 +316,8 @@ CHECK_WARNINGS += -Werror=implicit-int -Werror=implicit-function-declaration -We
|
|||
# `-traditional-cpp` was passed) so we use `gcc -E` instead.
|
||||
CPP := gcc -E
|
||||
MKLDSCRIPT := tools/mkldscript
|
||||
MKOVLRULES := tools/mkovlrules
|
||||
MKDMADATA := tools/mkdmadata
|
||||
ELF2ROM := tools/elf2rom
|
||||
BIN2C := tools/bin2c
|
||||
N64TEXCONV := tools/assets/n64texconv/n64texconv
|
||||
FADO := tools/fado/fado.elf
|
||||
|
|
@ -494,14 +494,6 @@ ASSET_FILES_OUT := $(foreach f,$(ASSET_FILES_BIN_EXTRACTED:.bin=.bin.inc.c),$(f:
|
|||
# Find all .o files included in the spec
|
||||
SPEC_O_FILES := $(shell $(CPP) $(CPPFLAGS) -I. $(SPEC) | $(BUILD_DIR_REPLACE) | sed -n -E 's/^[ \t]*include[ \t]*"([a-zA-Z0-9/_.-]+\.o)"/\1/p')
|
||||
|
||||
# Split out reloc files
|
||||
O_FILES := $(filter-out %_reloc.o,$(SPEC_O_FILES))
|
||||
OVL_RELOC_FILES := $(filter %_reloc.o,$(SPEC_O_FILES))
|
||||
|
||||
# Automatic dependency files
|
||||
# (Only asm_processor dependencies and reloc dependencies are handled for now)
|
||||
DEP_FILES := $(O_FILES:.o=.d) $(O_FILES:.o=.asmproc.d) $(OVL_RELOC_FILES:.o=.d) $(BUILD_DIR)/spec.d
|
||||
|
||||
TEXTURE_FILES_PNG_EXTRACTED := $(foreach dir,$(ASSET_BIN_DIRS_EXTRACTED),$(wildcard $(dir)/*.png))
|
||||
TEXTURE_FILES_PNG_COMMITTED := $(foreach dir,$(ASSET_BIN_DIRS_COMMITTED),$(wildcard $(dir)/*.png))
|
||||
TEXTURE_FILES_JPG_EXTRACTED := $(foreach dir,$(ASSET_BIN_DIRS_EXTRACTED),$(wildcard $(dir)/*.jpg))
|
||||
|
|
@ -511,10 +503,13 @@ TEXTURE_FILES_OUT := $(foreach f,$(TEXTURE_FILES_PNG_EXTRACTED:.png=.inc.c),$(f:
|
|||
$(foreach f,$(TEXTURE_FILES_JPG_EXTRACTED:.jpg=.jpg.inc.c),$(f:$(EXTRACTED_DIR)/%=$(BUILD_DIR)/%)) \
|
||||
$(foreach f,$(TEXTURE_FILES_JPG_COMMITTED:.jpg=.jpg.inc.c),$(BUILD_DIR)/$f)
|
||||
|
||||
OVL_SEGMENTS_DIR := $(BUILD_DIR)/segments
|
||||
|
||||
# create build directories
|
||||
$(shell mkdir -p $(BUILD_DIR)/baserom \
|
||||
$(BUILD_DIR)/assets/text \
|
||||
$(BUILD_DIR)/linker_scripts)
|
||||
$(BUILD_DIR)/linker_scripts \
|
||||
$(OVL_SEGMENTS_DIR))
|
||||
$(shell mkdir -p $(foreach dir, \
|
||||
$(SRC_DIRS) \
|
||||
$(UNDECOMPILED_DATA_DIRS) \
|
||||
|
|
@ -534,6 +529,24 @@ $(shell mkdir -p $(foreach dir, \
|
|||
$(dir:$(EXTRACTED_DIR)/%=$(BUILD_DIR)/%)))
|
||||
endif
|
||||
|
||||
# Generate and include segment makefile rules for combining overlay .o files into single .plf files, from which
|
||||
# overlay relocations will be generated.
|
||||
# If this makefile doesn't exist or if the spec has been modified since make was last ran it will use the rule
|
||||
# later on in the file to regenerate this file before including it. The test against MAKECMDGOALS ensures this
|
||||
# doesn't happen if we're not running a task that needs these partially linked files; this is especially important
|
||||
# for setup since the rule to generate the segment makefile rules requires setup to have ran first.
|
||||
OVLDFLAGS = -r -T linker_scripts/segment.ld -Map $(@:.plf=.map)
|
||||
ifeq ($(MAKECMDGOALS),$(filter-out clean assetclean distclean setup,$(MAKECMDGOALS)))
|
||||
include $(OVL_SEGMENTS_DIR)/Makefile
|
||||
else
|
||||
OVL_SEGMENT_FILES :=
|
||||
endif
|
||||
OVL_RELOC_FILES := $(OVL_SEGMENT_FILES:.plf=.reloc.o)
|
||||
|
||||
# Automatic dependency files
|
||||
# (Only asm_processor dependencies and reloc dependencies are handled for now)
|
||||
DEP_FILES := $(O_FILES:.o=.asmproc.d) $(OVL_RELOC_FILES:.o=.d)
|
||||
|
||||
$(BUILD_DIR)/src/boot/build.o: CPP_DEFINES += -DBUILD_CREATOR="\"$(BUILD_CREATOR)\"" -DBUILD_DATE="\"$(BUILD_DATE)\"" -DBUILD_TIME="\"$(BUILD_TIME)\""
|
||||
|
||||
$(BUILD_DIR)/src/audio/internal/seqplayer.o: CPP_DEFINES += -DMML_VERSION=MML_VERSION_OOT
|
||||
|
|
@ -742,7 +755,7 @@ else
|
|||
$(BUILD_DIR)/assets/%.o: CFLAGS += -fno-zero-initialized-in-bss -fno-toplevel-reorder
|
||||
$(BUILD_DIR)/src/%.o: CFLAGS += -fexec-charset=euc-jp
|
||||
$(BUILD_DIR)/src/libultra/libc/ll.o: OPTFLAGS := -Ofast
|
||||
$(BUILD_DIR)/src/overlays/%.o: CFLAGS += -fno-merge-constants -mno-explicit-relocs -mno-split-addresses
|
||||
$(BUILD_DIR)/src/overlays/%.o: CFLAGS += -mno-explicit-relocs -mno-split-addresses
|
||||
endif
|
||||
|
||||
#### Main Targets ###
|
||||
|
|
@ -821,7 +834,8 @@ else
|
|||
endif
|
||||
|
||||
$(ROM): $(ELF)
|
||||
$(ELF2ROM) -cic $(CIC) $< $@
|
||||
$(OBJCOPY) --pad-to 0x$$($(OBJDUMP) -t $< | grep _RomSize | cut -d ' ' -f 1) -O binary $< $@
|
||||
$(PYTHON) -m ipl3checksum sum --cic 6105 --update $@
|
||||
|
||||
$(ROMC): $(ROM) $(ELF) $(BUILD_DIR)/compress_ranges.txt
|
||||
$(PYTHON) tools/compress.py --in $(ROM) --out $@ --dmadata-start `./tools/dmadata_start.sh $(NM) $(ELF)` --compress `cat $(BUILD_DIR)/compress_ranges.txt` --threads $(N_THREADS) $(COMPRESS_ARGS)
|
||||
|
|
@ -837,7 +851,8 @@ ifeq ($(PLATFORM),IQUE)
|
|||
endif
|
||||
endif
|
||||
|
||||
$(ELF): $(TEXTURE_FILES_OUT) $(ASSET_FILES_OUT) $(O_FILES) $(OVL_RELOC_FILES) $(LDSCRIPT) $(BUILD_DIR)/linker_scripts/makerom.ld $(BUILD_DIR)/undefined_syms.txt \
|
||||
$(ELF): $(TEXTURE_FILES_OUT) $(ASSET_FILES_OUT) $(O_FILES) $(OVL_SEGMENT_FILES) $(OVL_RELOC_FILES) $(LDSCRIPT) \
|
||||
$(BUILD_DIR)/linker_scripts/makerom.ld $(BUILD_DIR)/undefined_syms.txt \
|
||||
$(SAMPLEBANK_O_FILES) $(SOUNDFONT_O_FILES) $(SEQUENCE_O_FILES) \
|
||||
$(BUILD_DIR)/assets/audio/sequence_font_table.o $(BUILD_DIR)/assets/audio/audiobank_padding.o
|
||||
$(LD) $(LDFLAGS) -o $@
|
||||
|
|
@ -846,22 +861,31 @@ $(BUILD_DIR)/linker_scripts/makerom.ld: linker_scripts/makerom.ld
|
|||
$(CPP) -I include $(CPPFLAGS) $< > $@
|
||||
|
||||
## Order-only prerequisites
|
||||
# These ensure e.g. the O_FILES are built before the OVL_RELOC_FILES.
|
||||
# These ensure e.g. texture files are built before object files that include them.
|
||||
# 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
|
||||
.PHONY: asset_files
|
||||
|
||||
$(BUILD_DIR)/spec: $(SPEC) $(SPEC_INCLUDES)
|
||||
$(CPP) $(CPPFLAGS) -MD -MP -MF $@.d -MT $@ -I. $< | $(BUILD_DIR_REPLACE) > $@
|
||||
|
||||
$(LDSCRIPT): $(BUILD_DIR)/spec
|
||||
$(MKLDSCRIPT) $< $@
|
||||
$(LDSCRIPT): $(BUILD_DIR)/$(SPEC)
|
||||
$(MKLDSCRIPT) $< $@ $(OVL_SEGMENTS_DIR)
|
||||
|
||||
# Generates a makefile containing rules for building .plf files
|
||||
# from overlay .o files for every overlay defined in the spec.
|
||||
$(OVL_SEGMENTS_DIR)/Makefile: $(BUILD_DIR)/$(SPEC)
|
||||
$(MKOVLRULES) $< $(OVL_SEGMENTS_DIR) $@
|
||||
|
||||
# Generates relocations for each overlay after partial linking so that the final
|
||||
# link step cannot later insert padding between individual overlay files after
|
||||
# relocations have already been calculated.
|
||||
$(OVL_SEGMENTS_DIR)/%.reloc.o: $(OVL_SEGMENTS_DIR)/%.plf
|
||||
$(FADO) $< -n $(notdir $*) -o $(@:.o=.s)
|
||||
$(AS) $(ASFLAGS) $(@:.o=.s) -o $@
|
||||
|
||||
$(BUILD_DIR)/undefined_syms.txt: undefined_syms.txt
|
||||
$(CPP) $(CPPFLAGS) $< > $@
|
||||
|
|
@ -974,12 +998,9 @@ $(BUILD_DIR)/src/overlays/misc/ovl_kaleido_scope/ovl_kaleido_scope_reloc.o: POST
|
|||
endif
|
||||
endif
|
||||
|
||||
$(BUILD_DIR)/src/overlays/%_reloc.o: $(BUILD_DIR)/spec
|
||||
$(FADO) $$(tools/reloc_prereq $< $(notdir $*)) -n $(notdir $*) -o $(@:.o=.s) -M $(@:.o=.d)
|
||||
$(POSTPROCESS_OBJ) $(@:.o=.s)
|
||||
$(AS) $(ASFLAGS) $(@:.o=.s) -o $@
|
||||
|
||||
# Assets from assets/
|
||||
$(BUILD_DIR)/assets/%.inc.c: assets/%.png
|
||||
$(ZAPD) btex -eh -tt $(subst .,,$(suffix $*)) -i $< -o $@
|
||||
|
||||
$(BUILD_DIR)/assets/%.inc.c: assets/%.png
|
||||
tools/assets/build_from_png/build_from_png $< $(dir $@) assets/$(dir $*) $(wildcard $(EXTRACTED_DIR)/assets/$(dir $*))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
OUTPUT_ARCH (mips)
|
||||
|
||||
/* Pass through all sections for partial linking of overlays. Also performs constant merging for GCC. */
|
||||
|
||||
SECTIONS {
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(*.rodata.cst4)
|
||||
*(*.rodata.cst8)
|
||||
*(*.rodata.*)
|
||||
}
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
/* GNU ld assumes that the linker script always combines .gptab.data and
|
||||
* .gptab.sdata into .gptab.sdata, and likewise for .gptab.bss and .gptab.sbss.
|
||||
* To avoid dealing with this, we just discard all .gptab sections.
|
||||
*/
|
||||
*(.gptab.*)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
16
spec/spec
16
spec/spec
|
|
@ -911,38 +911,39 @@ endseg
|
|||
beginseg
|
||||
name "ovl_title"
|
||||
compress
|
||||
flags OVERLAY
|
||||
address 0x80800000
|
||||
include "$(BUILD_DIR)/src/overlays/gamestates/ovl_title/z_title.o"
|
||||
include "$(BUILD_DIR)/src/overlays/gamestates/ovl_title/ovl_title_reloc.o"
|
||||
endseg
|
||||
|
||||
beginseg
|
||||
name "ovl_select"
|
||||
compress
|
||||
flags OVERLAY
|
||||
include "$(BUILD_DIR)/src/overlays/gamestates/ovl_select/z_select.o"
|
||||
include "$(BUILD_DIR)/src/overlays/gamestates/ovl_select/ovl_select_reloc.o"
|
||||
endseg
|
||||
|
||||
beginseg
|
||||
name "ovl_opening"
|
||||
compress
|
||||
flags OVERLAY
|
||||
include "$(BUILD_DIR)/src/overlays/gamestates/ovl_opening/z_opening.o"
|
||||
include "$(BUILD_DIR)/src/overlays/gamestates/ovl_opening/ovl_opening_reloc.o"
|
||||
endseg
|
||||
|
||||
beginseg
|
||||
name "ovl_file_choose"
|
||||
compress
|
||||
flags OVERLAY
|
||||
include "$(BUILD_DIR)/src/overlays/gamestates/ovl_file_choose/z_file_nameset_data.o"
|
||||
include "$(BUILD_DIR)/src/overlays/gamestates/ovl_file_choose/z_file_copy_erase.o"
|
||||
include "$(BUILD_DIR)/src/overlays/gamestates/ovl_file_choose/z_file_nameset.o"
|
||||
include "$(BUILD_DIR)/src/overlays/gamestates/ovl_file_choose/z_file_choose.o"
|
||||
include "$(BUILD_DIR)/src/overlays/gamestates/ovl_file_choose/ovl_file_choose_reloc.o"
|
||||
endseg
|
||||
|
||||
beginseg
|
||||
name "ovl_kaleido_scope"
|
||||
compress
|
||||
flags OVERLAY
|
||||
include "$(BUILD_DIR)/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.o"
|
||||
include "$(BUILD_DIR)/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.o"
|
||||
include "$(BUILD_DIR)/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.o"
|
||||
|
|
@ -956,32 +957,31 @@ beginseg
|
|||
#else
|
||||
include "$(BUILD_DIR)/src/overlays/misc/ovl_kaleido_scope/z_lmap_mark_data_mq.o"
|
||||
#endif
|
||||
include "$(BUILD_DIR)/src/overlays/misc/ovl_kaleido_scope/ovl_kaleido_scope_reloc.o"
|
||||
endseg
|
||||
|
||||
beginseg
|
||||
name "ovl_player_actor"
|
||||
compress
|
||||
flags OVERLAY
|
||||
include "$(BUILD_DIR)/src/overlays/actors/ovl_player_actor/z_player.o"
|
||||
include "$(BUILD_DIR)/src/overlays/actors/ovl_player_actor/ovl_player_actor_reloc.o"
|
||||
endseg
|
||||
|
||||
beginseg
|
||||
name "ovl_map_mark_data"
|
||||
compress
|
||||
flags OVERLAY
|
||||
#if !OOT_MQ
|
||||
include "$(BUILD_DIR)/src/overlays/misc/ovl_map_mark_data/z_map_mark_data.o"
|
||||
#else
|
||||
include "$(BUILD_DIR)/src/overlays/misc/ovl_map_mark_data/z_map_mark_data_mq.o"
|
||||
#endif
|
||||
include "$(BUILD_DIR)/src/overlays/misc/ovl_map_mark_data/ovl_map_mark_data_reloc.o"
|
||||
endseg
|
||||
|
||||
beginseg
|
||||
name "ovl_En_Test"
|
||||
compress
|
||||
flags OVERLAY
|
||||
include "$(BUILD_DIR)/src/overlays/actors/ovl_En_Test/z_en_test.o"
|
||||
include "$(BUILD_DIR)/src/overlays/actors/ovl_En_Test/ovl_En_Test_reloc.o"
|
||||
endseg
|
||||
|
||||
// Overlays for most actors and effects are reordered between versions. On N64 and iQue,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
# Output files
|
||||
*.exe
|
||||
bin2c
|
||||
elf2rom
|
||||
makeromfs
|
||||
mkdmadata
|
||||
mkldscript
|
||||
mkovlrules
|
||||
preprocess_pragmas
|
||||
reloc_prereq
|
||||
vtxdis
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
CFLAGS := -Wall -Wextra -pedantic -std=gnu99 -g -O2
|
||||
PROGRAMS := bin2c elf2rom makeromfs mkdmadata mkldscript preprocess_pragmas reloc_prereq vtxdis
|
||||
PROGRAMS := bin2c makeromfs mkdmadata mkldscript mkovlrules preprocess_pragmas reloc_prereq vtxdis
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
|
|
@ -54,11 +54,11 @@ distclean: clean
|
|||
|
||||
.PHONY: all clean distclean
|
||||
|
||||
elf2rom_SOURCES := elf2rom.c elf32.c n64chksum.c util.c
|
||||
bin2c_SOURCES := bin2c.c
|
||||
makeromfs_SOURCES := makeromfs.c n64chksum.c util.c
|
||||
mkdmadata_SOURCES := mkdmadata.c spec.c util.c
|
||||
mkldscript_SOURCES := mkldscript.c spec.c util.c
|
||||
mkovlrules_SOURCES := mkovlrules.c spec.c util.c
|
||||
preprocess_pragmas_SOURCES := preprocess_pragmas.c
|
||||
reloc_prereq_SOURCES := reloc_prereq.c spec.c util.c
|
||||
vtxdis_SOURCES := vtxdis.c
|
||||
|
|
|
|||
257
tools/elf2rom.c
257
tools/elf2rom.c
|
|
@ -1,257 +0,0 @@
|
|||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "elf32.h"
|
||||
#include "n64chksum.h"
|
||||
#include "util.h"
|
||||
|
||||
#define ROM_SEG_START_SUFFIX ".rom_start"
|
||||
#define ROM_SEG_END_SUFFIX ".rom_end"
|
||||
|
||||
struct RomSegment
|
||||
{
|
||||
const char *name;
|
||||
const void *data;
|
||||
int size;
|
||||
int romStart;
|
||||
int romEnd;
|
||||
};
|
||||
|
||||
static struct RomSegment *g_romSegments = NULL;
|
||||
static int g_romSegmentsCount = 0;
|
||||
static int g_romSize;
|
||||
|
||||
static bool parse_number(const char *str, int *num)
|
||||
{
|
||||
char *endptr;
|
||||
long int n = strtol(str, &endptr, 0);
|
||||
*num = n;
|
||||
return endptr > str;
|
||||
}
|
||||
|
||||
static char *sprintf_alloc(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int size;
|
||||
char *buffer;
|
||||
|
||||
va_start(args, fmt);
|
||||
size = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
buffer = malloc(size);
|
||||
|
||||
va_start(args, fmt);
|
||||
vsprintf(buffer, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static struct RomSegment *add_rom_segment(const char *name)
|
||||
{
|
||||
int index = g_romSegmentsCount;
|
||||
|
||||
g_romSegmentsCount++;
|
||||
g_romSegments = realloc(g_romSegments, g_romSegmentsCount * sizeof(*g_romSegments));
|
||||
g_romSegments[index].name = name;
|
||||
return &g_romSegments[index];
|
||||
}
|
||||
|
||||
static int find_symbol_value(struct Elf32_Symbol *syms, int numsymbols, const char *name)
|
||||
{
|
||||
struct Elf32_Symbol *sym;
|
||||
int lo, hi, mid, cmp;
|
||||
|
||||
// Binary search for the symbol. We maintain the invariant that [lo, hi) is
|
||||
// the interval that remains to search.
|
||||
lo = 0;
|
||||
hi = numsymbols;
|
||||
while (lo < hi)
|
||||
{
|
||||
mid = lo + (hi - lo) / 2;
|
||||
sym = &syms[mid];
|
||||
cmp = strcmp(sym->name, name);
|
||||
|
||||
if (cmp == 0)
|
||||
return (int) sym->value;
|
||||
else if (cmp < 0)
|
||||
lo = mid + 1;
|
||||
else
|
||||
hi = mid;
|
||||
}
|
||||
|
||||
util_fatal_error("Symbol %s is not defined\n", name);
|
||||
}
|
||||
|
||||
static int find_rom_address(struct Elf32_Symbol *syms, int numsymbols, const char *name, const char *suffix)
|
||||
{
|
||||
char *symName = sprintf_alloc("_%sSegmentRom%s", name, suffix);
|
||||
int ret = find_symbol_value(syms, numsymbols, symName);
|
||||
free(symName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmp_symbol_by_name(const void *a, const void *b)
|
||||
{
|
||||
return strcmp(
|
||||
((struct Elf32_Symbol *)a)->name,
|
||||
((struct Elf32_Symbol *)b)->name);
|
||||
}
|
||||
|
||||
static void parse_input_file(const char *filename)
|
||||
{
|
||||
struct Elf32 elf;
|
||||
struct Elf32_Symbol *syms;
|
||||
const void *data;
|
||||
size_t size;
|
||||
int numRomSymbols;
|
||||
int i;
|
||||
|
||||
data = util_read_whole_file(filename, &size);
|
||||
|
||||
if (!elf32_init(&elf, data, size) || elf.machine != ELF_MACHINE_MIPS)
|
||||
util_fatal_error("%s is not a valid 32-bit MIPS ELF file", filename);
|
||||
|
||||
// sort all symbols that contain the substring "Rom" for fast access
|
||||
// (sorting all symbols costs 0.1s, might as well avoid that)
|
||||
syms = malloc(elf.numsymbols * sizeof(struct Elf32_Symbol));
|
||||
numRomSymbols = 0;
|
||||
for (i = 0; i < elf.numsymbols; i++)
|
||||
{
|
||||
if (!elf32_get_symbol(&elf, &syms[numRomSymbols], i))
|
||||
util_fatal_error("invalid or corrupt ELF file");
|
||||
if (strstr(syms[numRomSymbols].name, "Rom"))
|
||||
numRomSymbols++;
|
||||
}
|
||||
qsort(syms, numRomSymbols, sizeof(struct Elf32_Symbol), cmp_symbol_by_name);
|
||||
|
||||
// get ROM segments
|
||||
// sections of type SHT_PROGBITS and whose name is ..secname are considered ROM segments
|
||||
for (i = 0; i < elf.shnum; i++)
|
||||
{
|
||||
struct Elf32_Section sec;
|
||||
struct RomSegment *segment;
|
||||
|
||||
if (!elf32_get_section(&elf, &sec, i))
|
||||
util_fatal_error("invalid or corrupt ELF file");
|
||||
if (sec.type == SHT_PROGBITS && sec.name[0] == '.' && sec.name[1] == '.'
|
||||
// HACK! ld sometimes marks NOLOAD sections as SHT_PROGBITS for no apparent reason,
|
||||
// so we must ignore the ..secname.bss sections explicitly
|
||||
&& strchr(sec.name + 2, '.') == NULL)
|
||||
{
|
||||
segment = add_rom_segment(sec.name + 2);
|
||||
segment->data = elf.data + sec.offset;
|
||||
segment->romStart = find_rom_address(syms, numRomSymbols, segment->name, "Start");
|
||||
segment->romEnd = find_rom_address(syms, numRomSymbols, segment->name, "End");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
g_romSize = find_symbol_value(syms, numRomSymbols, "_RomSize");
|
||||
|
||||
free(syms);
|
||||
}
|
||||
|
||||
// Writes the N64 ROM
|
||||
static void write_rom_file(const char *filename, int cicType)
|
||||
{
|
||||
uint8_t *buffer = calloc(g_romSize, 1);
|
||||
int i;
|
||||
uint32_t chksum[2];
|
||||
|
||||
// write segments
|
||||
for (i = 0; i < g_romSegmentsCount; i++)
|
||||
{
|
||||
int size = g_romSegments[i].romEnd - g_romSegments[i].romStart;
|
||||
|
||||
memcpy(buffer + g_romSegments[i].romStart, g_romSegments[i].data, size);
|
||||
}
|
||||
|
||||
// write checksum
|
||||
if (!n64chksum_calculate(buffer, cicType, chksum))
|
||||
util_fatal_error("invalid cic type %i", cicType);
|
||||
util_write_uint32_be(buffer + 0x10, chksum[0]);
|
||||
util_write_uint32_be(buffer + 0x14, chksum[1]);
|
||||
|
||||
util_write_whole_file(filename, buffer, g_romSize);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void usage(const char *execname)
|
||||
{
|
||||
printf("usage: %s -cic <number> input.elf output.z64\n", execname);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
const char *inputFileName = NULL;
|
||||
const char *outputFileName = NULL;
|
||||
int cicType = -1;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if (argv[i][0] == '-')
|
||||
{
|
||||
if (strcmp(argv[i], "-cic") == 0)
|
||||
{
|
||||
i++;
|
||||
if (i >= argc || !parse_number(argv[i], &cicType))
|
||||
{
|
||||
fputs("error: expected number after -cic\n", stderr);
|
||||
goto bad_args;
|
||||
}
|
||||
}
|
||||
else if (strcmp(argv[i], "-help") == 0)
|
||||
{
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "unknown option %s\n", argv[i]);
|
||||
goto bad_args;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inputFileName == NULL)
|
||||
inputFileName = argv[i];
|
||||
else if (outputFileName == NULL)
|
||||
outputFileName = argv[i];
|
||||
else
|
||||
{
|
||||
fputs("error: too many parameters specified\n", stderr);
|
||||
goto bad_args;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inputFileName == NULL)
|
||||
{
|
||||
fputs("error: no input file specified\n", stderr);
|
||||
goto bad_args;
|
||||
}
|
||||
if (outputFileName == NULL)
|
||||
{
|
||||
fputs("error: no output file specified\n", stderr);
|
||||
goto bad_args;
|
||||
}
|
||||
if (cicType == -1)
|
||||
{
|
||||
fputs("error: no CIC type specified\n", stderr);
|
||||
goto bad_args;
|
||||
}
|
||||
|
||||
parse_input_file(inputFileName);
|
||||
write_rom_file(outputFileName, cicType);
|
||||
return 0;
|
||||
|
||||
bad_args:
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -241,7 +241,7 @@ def compare_pointers(version: str) -> dict[Path, BssSection]:
|
|||
) as p:
|
||||
for mapfile_segment in source_code_segments:
|
||||
for file in mapfile_segment:
|
||||
if not str(file.filepath).endswith(".o"):
|
||||
if not str(file.filepath).endswith((".o", ".plf")):
|
||||
continue
|
||||
if file.sectionType == ".bss":
|
||||
continue
|
||||
|
|
@ -282,39 +282,67 @@ def compare_pointers(version: str) -> dict[Path, BssSection]:
|
|||
if not file.sectionType == ".bss":
|
||||
continue
|
||||
|
||||
pointers_in_section = [
|
||||
p
|
||||
for p in pointers
|
||||
if file.vram <= p.build_value < file.vram + file.size
|
||||
]
|
||||
|
||||
object_file = file.filepath.relative_to(f"build/{version}")
|
||||
# Hack to handle the combined z_message_z_game_over.o file.
|
||||
# Fortunately z_game_over has no BSS so we can just analyze z_message instead.
|
||||
if str(object_file) == "src/code/z_message_z_game_over.o":
|
||||
object_file = Path("src/code/z_message.o")
|
||||
|
||||
c_file = object_file.with_suffix(".c")
|
||||
# c_file = object_file.with_suffix(".c")
|
||||
|
||||
# For the baserom, assume that the lowest address is the start of the BSS section. This might
|
||||
# not be true if the first BSS variable is not referenced so account for that specifically.
|
||||
|
||||
base_start_address = (
|
||||
min(p.base_value for p in pointers_in_section)
|
||||
if pointers_in_section
|
||||
else 0
|
||||
)
|
||||
# Account for the fact that z_rumble starts with unreferenced bss
|
||||
if str(c_file) == "src/code/z_rumble.c":
|
||||
base_start_address -= 0x10
|
||||
elif str(c_file) == "src/boot/z_locale.c":
|
||||
base_start_address -= 0x18
|
||||
if object_file.suffix == ".plf":
|
||||
# For partially linked overlays, read the map file for the plf to get the
|
||||
# object file corresponding to a single source file
|
||||
plf_map = mapfile_parser.mapfile.MapFile()
|
||||
plf_map.readMapFile(file.filepath.with_suffix(".map"))
|
||||
for plf_seg in plf_map:
|
||||
for plf_file in plf_seg:
|
||||
if not plf_file.sectionType == ".bss":
|
||||
continue
|
||||
c_file = plf_file.filepath.relative_to(f"build/{version}").with_suffix(".c")
|
||||
|
||||
build_start_address = file.vram
|
||||
pointers_in_section = [
|
||||
p
|
||||
for p in pointers
|
||||
if file.vram + plf_file.vram <= p.build_value < file.vram + plf_file.vram + plf_file.size
|
||||
]
|
||||
|
||||
bss_sections[c_file] = BssSection(
|
||||
base_start_address, build_start_address, pointers_in_section
|
||||
)
|
||||
base_start_address = (
|
||||
min(p.base_value for p in pointers_in_section)
|
||||
if pointers_in_section
|
||||
else 0
|
||||
)
|
||||
# Account for the fact that z_rumble starts with unreferenced bss
|
||||
if str(c_file) == "src/code/z_rumble.c":
|
||||
base_start_address -= 0x10
|
||||
elif str(c_file) == "src/boot/z_locale.c":
|
||||
base_start_address -= 0x18
|
||||
|
||||
bss_sections[c_file] = BssSection(base_start_address, file.vram + plf_file.vram, pointers_in_section)
|
||||
else:
|
||||
c_file = object_file.with_suffix(".c")
|
||||
|
||||
pointers_in_section = [
|
||||
p
|
||||
for p in pointers
|
||||
if file.vram <= p.build_value < file.vram + file.size
|
||||
]
|
||||
|
||||
base_start_address = (
|
||||
min(p.base_value for p in pointers_in_section)
|
||||
if pointers_in_section
|
||||
else 0
|
||||
)
|
||||
# Account for the fact that z_rumble starts with unreferenced bss
|
||||
if str(c_file) == "src/code/z_rumble.c":
|
||||
base_start_address -= 0x10
|
||||
elif str(c_file) == "src/boot/z_locale.c":
|
||||
base_start_address -= 0x18
|
||||
|
||||
bss_sections[c_file] = BssSection(base_start_address, file.vram, pointers_in_section)
|
||||
|
||||
return bss_sections
|
||||
|
||||
|
|
|
|||
|
|
@ -9,229 +9,235 @@
|
|||
#include "spec.h"
|
||||
#include "util.h"
|
||||
|
||||
// Note: *SECTION ALIGNMENT* Object files built with a compiler such as GCC can, by default, use narrower
|
||||
// alignment for sections size, compared to IDO padding sections to a 0x10-aligned size.
|
||||
// To properly generate relocations relative to section starts, sections currently need to be aligned
|
||||
// explicitly (to 0x10 currently, a narrower alignment might work), otherwise the linker does implicit alignment
|
||||
// and inserts padding between the address indicated by section start symbols (such as *SegmentRoDataStart) and
|
||||
// the actual aligned start of the section.
|
||||
// With IDO, the padding of sections to an aligned size makes the section start at aligned addresses out of the box,
|
||||
// so the explicit alignment has no further effect.
|
||||
|
||||
struct Segment *g_segments;
|
||||
int g_segmentsCount;
|
||||
|
||||
static void write_ld_script(FILE *fout)
|
||||
static void write_includes(const struct Segment *seg, FILE *fout, const char *segments_dir, const char *section,
|
||||
bool linker_pad)
|
||||
{
|
||||
// Note sections contain a suffix wildcard as compilers other than IDO such as GCC may emit sections titled
|
||||
// e.g. .rodata.cstN, .rodata.strN.M, .text.FUNCNAME depending on their settings.
|
||||
if (seg->flags & FLAG_OVL) {
|
||||
// For overlays they are already partially linked.
|
||||
fprintf(fout, " %s/%s.plf (%s*)\n", segments_dir, seg->name, section);
|
||||
} else {
|
||||
// For non-overlays, list each include separately.
|
||||
int i;
|
||||
for (i = 0; i < seg->includesCount; i++) {
|
||||
fprintf(fout, " %s (%s*)\n", seg->includes[i].fpath, section);
|
||||
if (linker_pad && seg->includes[i].linkerPadding != 0)
|
||||
fprintf(fout, " . += 0x%X;\n", seg->includes[i].linkerPadding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_ld_script(FILE *fout, const char *segments_dir)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
|
||||
fputs("OUTPUT_ARCH (mips)\n\n"
|
||||
"SECTIONS {\n"
|
||||
" _RomSize = 0;\n"
|
||||
" _RomStart = _RomSize;\n\n",
|
||||
fputs("OUTPUT_ARCH (mips)\n"
|
||||
"\n"
|
||||
"SECTIONS\n"
|
||||
"{\n"
|
||||
" _RomPos = 0;\n"
|
||||
"\n",
|
||||
fout);
|
||||
|
||||
for (i = 0; i < g_segmentsCount; i++)
|
||||
{
|
||||
for (i = 0; i < g_segmentsCount; i++) {
|
||||
const struct Segment *seg = &g_segments[i];
|
||||
|
||||
fprintf(fout, " /* %s */\n\n", seg->name);
|
||||
|
||||
// align start of ROM segment
|
||||
if (seg->fields & (1 << STMT_romalign))
|
||||
fprintf(fout, " _RomSize = (_RomSize + %i) & ~ %i;\n", seg->romalign - 1, seg->romalign - 1);
|
||||
fprintf(fout, " _RomPos = ALIGN(_RomPos, %i);\n", seg->romalign);
|
||||
|
||||
// initialized data (.text, .data, .rodata, .sdata)
|
||||
|
||||
fprintf(fout, " _%sSegmentRomStartTemp = _RomSize;\n"
|
||||
" _%sSegmentRomStart = _%sSegmentRomStartTemp;\n"
|
||||
" ..%s ", seg->name, seg->name, seg->name, seg->name);
|
||||
// Begin initialized data (.text, .data, .rodata)
|
||||
fprintf(fout, " _%sSegmentRomStartTemp = _RomPos;\n"
|
||||
" _%sSegmentRomStart = _%sSegmentRomStartTemp;\n"
|
||||
" ..%s ", seg->name, seg->name, seg->name, seg->name);
|
||||
|
||||
if (seg->fields & (1 << STMT_after))
|
||||
fprintf(fout, "(_%sSegmentEnd + %i) & ~ %i ", seg->after, seg->align - 1, seg->align - 1);
|
||||
// Continue after the requested segment, aligning to the required alignment for the new segment.
|
||||
fprintf(fout, "ALIGN(_%sSegmentEnd, %i)", seg->after, seg->align);
|
||||
else if (seg->fields & (1 << STMT_number))
|
||||
fprintf(fout, "0x%02X000000 ", seg->number);
|
||||
// Start at a new segmented address.
|
||||
fprintf(fout, "0x%02X000000", seg->number);
|
||||
else if (seg->fields & (1 << STMT_address))
|
||||
fprintf(fout, "0x%08X ", seg->address);
|
||||
// Start at a new absolute address.
|
||||
fprintf(fout, "0x%08X", seg->address);
|
||||
else
|
||||
fprintf(fout, "ALIGN(0x%X) ", seg->align);
|
||||
// Continue after previous segment, aligning to the required alignment for the new segment.
|
||||
fprintf(fout, "ALIGN(0x%X)", seg->align);
|
||||
|
||||
// (AT(_RomSize) isn't necessary, but adds useful "load address" lines to the map file)
|
||||
fprintf(fout, ": AT(_RomSize)\n {\n"
|
||||
" _%sSegmentStart = .;\n"
|
||||
" . = ALIGN(0x10);\n"
|
||||
" _%sSegmentTextStart = .;\n",
|
||||
seg->name, seg->name);
|
||||
// AT(_RomPos) isn't necessary, but adds useful "load address" lines to the map file.
|
||||
// Also force an alignment of at least 0x10 at the start of any segment. This is especially important for
|
||||
// overlays as the final link step must not introduce alignment padding between the SegmentTextStart symbol
|
||||
// and the section contents as this would cause all generated relocations done prior to be wrong.
|
||||
fprintf(fout, " : AT(_RomPos)\n"
|
||||
" {\n"
|
||||
" . = ALIGN(0x10);\n"
|
||||
" _%sSegmentStart = .;\n"
|
||||
"\n",
|
||||
seg->name);
|
||||
|
||||
for (j = 0; j < seg->includesCount; j++)
|
||||
{
|
||||
fprintf(fout, " %s (.text)\n", seg->includes[j].fpath);
|
||||
if (seg->includes[j].linkerPadding != 0)
|
||||
fprintf(fout, " . += 0x%X;\n", seg->includes[j].linkerPadding);
|
||||
fprintf(fout, " . = ALIGN(0x10);\n");
|
||||
}
|
||||
|
||||
fprintf(fout, " _%sSegmentTextEnd = .;\n", seg->name);
|
||||
|
||||
fprintf(fout, " _%sSegmentTextSize = ABSOLUTE( _%sSegmentTextEnd - _%sSegmentTextStart );\n", seg->name, seg->name, seg->name);
|
||||
// Write .text
|
||||
fprintf(fout, " _%sSegmentTextStart = .;\n", seg->name);
|
||||
write_includes(seg, fout, segments_dir, ".text", true);
|
||||
fprintf(fout, " . = ALIGN(0x10);\n"
|
||||
" _%sSegmentTextEnd = .;\n"
|
||||
" _%sSegmentTextSize = ABSOLUTE( _%sSegmentTextEnd - _%sSegmentTextStart );\n"
|
||||
"\n", seg->name, seg->name, seg->name, seg->name);
|
||||
|
||||
// Write .data
|
||||
fprintf(fout, " _%sSegmentDataStart = .;\n", seg->name);
|
||||
write_includes(seg, fout, segments_dir, ".data", false);
|
||||
fprintf(fout, " . = ALIGN(0x10);\n"
|
||||
" _%sSegmentDataEnd = .;\n"
|
||||
" _%sSegmentDataSize = ABSOLUTE( _%sSegmentDataEnd - _%sSegmentDataStart );\n"
|
||||
"\n", seg->name, seg->name, seg->name, seg->name);
|
||||
|
||||
for (j = 0; j < seg->includesCount; j++)
|
||||
{
|
||||
fprintf(fout, " %s (.data)\n"
|
||||
" . = ALIGN(0x10);\n", seg->includes[j].fpath);
|
||||
}
|
||||
|
||||
fprintf(fout, " _%sSegmentDataEnd = .;\n", seg->name);
|
||||
|
||||
fprintf(fout, " _%sSegmentDataSize = ABSOLUTE( _%sSegmentDataEnd - _%sSegmentDataStart );\n", seg->name, seg->name, seg->name);
|
||||
|
||||
// Write .rodata
|
||||
fprintf(fout, " _%sSegmentRoDataStart = .;\n", seg->name);
|
||||
write_includes(seg, fout, segments_dir, ".rodata", false);
|
||||
fprintf(fout, " . = ALIGN(0x10);\n"
|
||||
" _%sSegmentRoDataEnd = .;\n"
|
||||
" _%sSegmentRoDataSize = ABSOLUTE( _%sSegmentRoDataEnd - _%sSegmentRoDataStart );\n"
|
||||
"\n", seg->name, seg->name, seg->name, seg->name);
|
||||
|
||||
for (j = 0; j < seg->includesCount; j++)
|
||||
{
|
||||
// Compilers other than IDO, such as GCC, produce different sections such as
|
||||
// the ones named directly below. These sections do not contain values that
|
||||
// need relocating, but we need to ensure that the base .rodata section
|
||||
// always comes first. The reason this is important is due to relocs assuming
|
||||
// the base of .rodata being the offset for the relocs and thus needs to remain
|
||||
// the beginning of the entire rodata area in order to remain consistent.
|
||||
// Inconsistencies will lead to various .rodata reloc crashes as a result of
|
||||
// either missing relocs or wrong relocs.
|
||||
fprintf(fout, " %s (.rodata)\n"
|
||||
" %s (.rodata.str*)\n"
|
||||
" %s (.rodata.cst*)\n"
|
||||
" . = ALIGN(0x10);\n",
|
||||
seg->includes[j].fpath, seg->includes[j].fpath, seg->includes[j].fpath);
|
||||
}
|
||||
|
||||
fprintf(fout, " _%sSegmentRoDataEnd = .;\n", seg->name);
|
||||
|
||||
fprintf(fout, " _%sSegmentRoDataSize = ABSOLUTE( _%sSegmentRoDataEnd - _%sSegmentRoDataStart );\n", seg->name, seg->name, seg->name);
|
||||
|
||||
fprintf(fout, " _%sSegmentSDataStart = .;\n", seg->name);
|
||||
|
||||
for (j = 0; j < seg->includesCount; j++)
|
||||
fprintf(fout, " %s (.sdata)\n"
|
||||
" . = ALIGN(0x10);\n", seg->includes[j].fpath);
|
||||
|
||||
fprintf(fout, " _%sSegmentSDataEnd = .;\n", seg->name);
|
||||
|
||||
fprintf(fout, " _%sSegmentOvlStart = .;\n", seg->name);
|
||||
|
||||
for (j = 0; j < seg->includesCount; j++)
|
||||
fprintf(fout, " %s (.ovl)\n", seg->includes[j].fpath);
|
||||
|
||||
fprintf(fout, " _%sSegmentOvlEnd = .;\n", seg->name);
|
||||
|
||||
// Write an address increment if requested
|
||||
if (seg->fields & (1 << STMT_increment))
|
||||
fprintf(fout, " . += 0x%08X;\n", seg->increment);
|
||||
|
||||
fputs(" }\n", fout);
|
||||
if (seg->flags & FLAG_OVL) {
|
||||
// Write .ovl if the segment is an overlay.
|
||||
fprintf(fout, " _%sSegmentOvlStart = .;\n"
|
||||
" %s/%s.reloc.o (.ovl)\n"
|
||||
" _%sSegmentOvlEnd = .;\n"
|
||||
" _%sSegmentOvlSize = ABSOLUTE( _%sSegmentOvlEnd - _%sSegmentOvlStart );\n"
|
||||
"\n", seg->name, segments_dir, seg->name, seg->name, seg->name, seg->name, seg->name);
|
||||
}
|
||||
|
||||
fprintf(fout, " _RomSize += ( _%sSegmentOvlEnd - _%sSegmentTextStart );\n", seg->name, seg->name);
|
||||
const char *last_loadable = (seg->flags & FLAG_OVL) ? "Ovl" : "RoData";
|
||||
|
||||
fprintf(fout, " _%sSegmentRomEndTemp = _RomSize;\n"
|
||||
"_%sSegmentRomEnd = _%sSegmentRomEndTemp;\n\n",
|
||||
seg->name, seg->name, seg->name);
|
||||
// End initialized data.
|
||||
fprintf(fout, " }\n"
|
||||
" _RomPos += ( _%sSegment%sEnd - _%sSegmentTextStart );\n"
|
||||
" _%sSegmentRomEndTemp = _RomPos;\n"
|
||||
" _%sSegmentRomEnd = _%sSegmentRomEndTemp;\n"
|
||||
" _%sSegmentRomSize = ABSOLUTE( _%sSegmentRomEnd - _%sSegmentRomStart );\n"
|
||||
"\n",
|
||||
seg->name, last_loadable, seg->name, seg->name, seg->name, seg->name,
|
||||
seg->name, seg->name, seg->name);
|
||||
|
||||
// align end of ROM segment
|
||||
// Align end of ROM segment
|
||||
if (seg->fields & (1 << STMT_romalign))
|
||||
fprintf(fout, " _RomSize = (_RomSize + %i) & ~ %i;\n", seg->romalign - 1, seg->romalign - 1);
|
||||
fprintf(fout, " _RomPos = ALIGN(_RomPos, %i);\n", seg->romalign);
|
||||
|
||||
// uninitialized data (.sbss, .scommon, .bss, COMMON)
|
||||
fprintf(fout, " ..%s.bss ADDR(..%s) + SIZEOF(..%s) (NOLOAD) :\n"
|
||||
/*" ..%s.bss :\n"*/
|
||||
// Begin uninitialized data (.bss, COMMON)
|
||||
// Note we must enforce a minimum alignment of at least 8 for
|
||||
// bss sections due to how bss is cleared in steps of 8 in
|
||||
// entry.s, and more widely it's more efficient.
|
||||
fprintf(fout, " ..%s.bss (NOLOAD) :\n"
|
||||
" {\n"
|
||||
" . = ALIGN(0x10);\n"
|
||||
" . = ALIGN(8);\n"
|
||||
" _%sSegmentBssStart = .;\n",
|
||||
seg->name, seg->name, seg->name, seg->name);
|
||||
seg->name, seg->name);
|
||||
|
||||
for (j = 0; j < seg->includesCount; j++)
|
||||
fprintf(fout, " %s (.sbss)\n"
|
||||
" . = ALIGN(0x10);\n", seg->includes[j].fpath);
|
||||
// Write .bss and COMMON
|
||||
write_includes(seg, fout, segments_dir, ".bss", false);
|
||||
write_includes(seg, fout, segments_dir, "COMMON", false);
|
||||
|
||||
for (j = 0; j < seg->includesCount; j++)
|
||||
fprintf(fout, " %s (.scommon)\n"
|
||||
" . = ALIGN(0x10);\n", seg->includes[j].fpath);
|
||||
|
||||
for (j = 0; j < seg->includesCount; j++)
|
||||
fprintf(fout, " %s (.bss)\n"
|
||||
" . = ALIGN(0x10);\n", seg->includes[j].fpath);
|
||||
|
||||
for (j = 0; j < seg->includesCount; j++)
|
||||
fprintf(fout, " %s (COMMON)\n"
|
||||
" . = ALIGN(0x10);\n", seg->includes[j].fpath);
|
||||
|
||||
fprintf(fout, " . = ALIGN(0x10);\n"
|
||||
// End uninitialized data
|
||||
fprintf(fout, " . = ALIGN(8);\n"
|
||||
" _%sSegmentBssEnd = .;\n"
|
||||
" _%sSegmentBssSize = ABSOLUTE( _%sSegmentBssEnd - _%sSegmentBssStart );\n"
|
||||
"\n"
|
||||
" _%sSegmentEnd = .;\n"
|
||||
" }\n"
|
||||
" _%sSegmentBssSize = ABSOLUTE( _%sSegmentBssEnd - _%sSegmentBssStart );\n\n",
|
||||
"\n",
|
||||
seg->name, seg->name, seg->name, seg->name, seg->name);
|
||||
}
|
||||
|
||||
fputs(" _RomEnd = _RomSize;\n\n", fout);
|
||||
fputs(" _RomSize = _RomPos;\n\n", fout);
|
||||
|
||||
// Debugging sections
|
||||
fputs(
|
||||
// mdebug sections
|
||||
" .pdr : { *(.pdr) }" "\n"
|
||||
" .mdebug : { *(.mdebug) }" "\n"
|
||||
" .mdebug.abi32 : { *(.mdebug.abi32) }" "\n"
|
||||
" .pdr : { *(.pdr) }" "\n"
|
||||
" .mdebug : { *(.mdebug) }" "\n"
|
||||
// Stabs debugging sections
|
||||
" .stab 0 : { *(.stab) }" "\n"
|
||||
" .stabstr 0 : { *(.stabstr) }" "\n"
|
||||
" .stab.excl 0 : { *(.stab.excl) }" "\n"
|
||||
" .stab.exclstr 0 : { *(.stab.exclstr) }" "\n"
|
||||
" .stab.index 0 : { *(.stab.index) }" "\n"
|
||||
" .stab.indexstr 0 : { *(.stab.indexstr) }" "\n"
|
||||
" .comment 0 : { *(.comment) }" "\n"
|
||||
" .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }" "\n"
|
||||
// DWARF debug sections
|
||||
// Symbols in the DWARF debugging sections are relative to the beginning of the section so we begin them at 0.
|
||||
// DWARF 1
|
||||
" .debug 0 : { *(.debug) }" "\n"
|
||||
" .line 0 : { *(.line) }" "\n"
|
||||
" .debug 0 : { *(.debug) }" "\n"
|
||||
" .line 0 : { *(.line) }" "\n"
|
||||
// GNU DWARF 1 extensions
|
||||
" .debug_srcinfo 0 : { *(.debug_srcinfo) }" "\n"
|
||||
" .debug_sfnames 0 : { *(.debug_sfnames) }" "\n"
|
||||
" .debug_srcinfo 0 : { *(.debug_srcinfo) }" "\n"
|
||||
" .debug_sfnames 0 : { *(.debug_sfnames) }" "\n"
|
||||
// DWARF 1.1 and DWARF 2
|
||||
" .debug_aranges 0 : { *(.debug_aranges) }" "\n"
|
||||
" .debug_pubnames 0 : { *(.debug_pubnames) }" "\n"
|
||||
" .debug_aranges 0 : { *(.debug_aranges) }" "\n"
|
||||
" .debug_pubnames 0 : { *(.debug_pubnames) }" "\n"
|
||||
// DWARF 2
|
||||
" .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }" "\n"
|
||||
" .debug_abbrev 0 : { *(.debug_abbrev) }" "\n"
|
||||
" .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }" "\n"
|
||||
" .debug_frame 0 : { *(.debug_frame) }" "\n"
|
||||
" .debug_str 0 : { *(.debug_str) }" "\n"
|
||||
" .debug_loc 0 : { *(.debug_loc) }" "\n"
|
||||
" .debug_macinfo 0 : { *(.debug_macinfo) }" "\n"
|
||||
" .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }" "\n"
|
||||
" .debug_abbrev 0 : { *(.debug_abbrev) }" "\n"
|
||||
" .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }" "\n"
|
||||
" .debug_frame 0 : { *(.debug_frame) }" "\n"
|
||||
" .debug_str 0 : { *(.debug_str) }" "\n"
|
||||
" .debug_loc 0 : { *(.debug_loc) }" "\n"
|
||||
" .debug_macinfo 0 : { *(.debug_macinfo) }" "\n"
|
||||
// SGI/MIPS DWARF 2 extensions
|
||||
" .debug_weaknames 0 : { *(.debug_weaknames) }" "\n"
|
||||
" .debug_funcnames 0 : { *(.debug_funcnames) }" "\n"
|
||||
" .debug_typenames 0 : { *(.debug_typenames) }" "\n"
|
||||
" .debug_varnames 0 : { *(.debug_varnames) }" "\n"
|
||||
" .debug_weaknames 0 : { *(.debug_weaknames) }" "\n"
|
||||
" .debug_funcnames 0 : { *(.debug_funcnames) }" "\n"
|
||||
" .debug_typenames 0 : { *(.debug_typenames) }" "\n"
|
||||
" .debug_varnames 0 : { *(.debug_varnames) }" "\n"
|
||||
// DWARF 3
|
||||
" .debug_pubtypes 0 : { *(.debug_pubtypes) }" "\n"
|
||||
" .debug_ranges 0 : { *(.debug_ranges) }" "\n"
|
||||
" .debug_pubtypes 0 : { *(.debug_pubtypes) }" "\n"
|
||||
" .debug_ranges 0 : { *(.debug_ranges) }" "\n"
|
||||
// DWARF 5
|
||||
" .debug_addr 0 : { *(.debug_addr) }" "\n"
|
||||
" .debug_line_str 0 : { *(.debug_line_str) }" "\n"
|
||||
" .debug_loclists 0 : { *(.debug_loclists) }" "\n"
|
||||
" .debug_macro 0 : { *(.debug_macro) }" "\n"
|
||||
" .debug_names 0 : { *(.debug_names) }" "\n"
|
||||
" .debug_rnglists 0 : { *(.debug_rnglists) }" "\n"
|
||||
" .debug_str_offsets 0 : { *(.debug_str_offsets) }" "\n"
|
||||
" .debug_sup 0 : { *(.debug_sup) }\n"
|
||||
" .debug_addr 0 : { *(.debug_addr) }" "\n"
|
||||
" .debug_line_str 0 : { *(.debug_line_str) }" "\n"
|
||||
" .debug_loclists 0 : { *(.debug_loclists) }" "\n"
|
||||
" .debug_macro 0 : { *(.debug_macro) }" "\n"
|
||||
" .debug_names 0 : { *(.debug_names) }" "\n"
|
||||
" .debug_rnglists 0 : { *(.debug_rnglists) }" "\n"
|
||||
" .debug_str_offsets 0 : { *(.debug_str_offsets) }" "\n"
|
||||
" .debug_sup 0 : { *(.debug_sup) }" "\n"
|
||||
// gnu attributes
|
||||
" .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }" "\n", fout);
|
||||
" .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }" "\n"
|
||||
// Sections generated by GCC to inform GDB about the ABI
|
||||
" .mdebug.abi32 0 : { KEEP (*(.mdebug.abi32)) }" "\n"
|
||||
" .mdebug.abiN32 0 : { KEEP (*(.mdebug.abiN32)) }" "\n"
|
||||
" .mdebug.abi64 0 : { KEEP (*(.mdebug.abi64)) }" "\n"
|
||||
" .mdebug.abiO64 0 : { KEEP (*(.mdebug.abiO64)) }" "\n"
|
||||
" .mdebug.eabi32 0 : { KEEP (*(.mdebug.eabi32)) }" "\n"
|
||||
" .mdebug.eabi64 0 : { KEEP (*(.mdebug.eabi64)) }" "\n"
|
||||
" .gcc_compiled_long32 0 : { KEEP (*(.gcc_compiled_long32)) }" "\n"
|
||||
" .gcc_compiled_long64 0 : { KEEP (*(.gcc_compiled_long64)) }" "\n\n", fout);
|
||||
|
||||
// Discard all other sections not mentioned above
|
||||
fputs(" /DISCARD/ :" "\n"
|
||||
" {" "\n"
|
||||
" *(*);" "\n"
|
||||
" }" "\n", fout);
|
||||
fputs("}\n", fout);
|
||||
fputs(" /DISCARD/ :" "\n"
|
||||
" {" "\n"
|
||||
" *(*);" "\n"
|
||||
" }" "\n"
|
||||
"}\n", fout);
|
||||
}
|
||||
|
||||
static void usage(const char *execname)
|
||||
{
|
||||
fprintf(stderr, "Nintendo 64 linker script generation tool v0.03\n"
|
||||
"usage: %s SPEC_FILE LD_SCRIPT\n"
|
||||
"SPEC_FILE file describing the organization of object files into segments\n"
|
||||
"LD_SCRIPT filename of output linker script\n",
|
||||
fprintf(stderr, "Nintendo 64 linker script generation tool v0.04\n"
|
||||
"usage: %s SPEC_FILE LD_SCRIPT SEGMENTS_DIR\n"
|
||||
"SPEC_FILE file describing the organization of object files into segments\n"
|
||||
"LD_SCRIPT filename of output linker script\n"
|
||||
"SEGMENTS_DIR dir name containing partially linked overlay segments\n",
|
||||
execname);
|
||||
}
|
||||
|
||||
|
|
@ -241,10 +247,9 @@ int main(int argc, char **argv)
|
|||
void *spec;
|
||||
size_t size;
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
if (argc != 4) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
spec = util_read_whole_file(argv[1], &size);
|
||||
|
|
@ -253,11 +258,10 @@ int main(int argc, char **argv)
|
|||
ldout = fopen(argv[2], "w");
|
||||
if (ldout == NULL)
|
||||
util_fatal_error("failed to open file '%s' for writing", argv[2]);
|
||||
write_ld_script(ldout);
|
||||
write_ld_script(ldout, argv[3]);
|
||||
fclose(ldout);
|
||||
|
||||
free_rom_spec(g_segments, g_segmentsCount);
|
||||
free(spec);
|
||||
|
||||
return 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "spec.h"
|
||||
#include "util.h"
|
||||
|
||||
struct Segment* g_segments;
|
||||
int g_segmentsCount;
|
||||
|
||||
static void write_overlay_rules(FILE *fout, const char *ovls_dir)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < g_segmentsCount; i++) {
|
||||
if (!(g_segments[i].flags & FLAG_OVL))
|
||||
continue;
|
||||
|
||||
/* Write rule for partial linkage of this segment */
|
||||
fprintf(fout, "%s/%s.plf:", ovls_dir, g_segments[i].name);
|
||||
for (j = 0; j < g_segments[i].includesCount; j++)
|
||||
fprintf(fout, " \\\n\t\t%s", g_segments[i].includes[j].fpath);
|
||||
fprintf(fout, "\n"
|
||||
"\t$(LD) $(OVLDFLAGS) $^ -o $@\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
/* List every expected plf in a variable */
|
||||
fprintf(fout, "OVL_SEGMENT_FILES :=");
|
||||
for (i = 0; i < g_segmentsCount; i++) {
|
||||
if (!(g_segments[i].flags & FLAG_OVL))
|
||||
continue;
|
||||
|
||||
fprintf(fout, " \\\n\t\t%s/%s.plf", ovls_dir, g_segments[i].name);
|
||||
}
|
||||
fprintf(fout, "\n");
|
||||
}
|
||||
|
||||
static void usage(const char *execname)
|
||||
{
|
||||
fprintf(stderr, "zelda64 overlay rules generator v0.01\n"
|
||||
"usage: %s SPEC_FILE OBJ_DIRECTORY MAKEFILE_OUT\n"
|
||||
"SPEC_FILE file describing the organization of object files into segments\n"
|
||||
"OBJ_DIRECTORY directory where object files will be stored\n"
|
||||
"MAKEFILE_OUT filename of output makefile to write linking rules\n",
|
||||
execname);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
FILE *makefile;
|
||||
void *spec;
|
||||
size_t size;
|
||||
|
||||
if (argc != 4) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
spec = util_read_whole_file(argv[1], &size);
|
||||
parse_rom_spec(spec, &g_segments, &g_segmentsCount);
|
||||
|
||||
makefile = fopen(argv[3], "w");
|
||||
if (makefile == NULL)
|
||||
util_fatal_error("failed to open file '%s' for writing", argv[2]);
|
||||
write_overlay_rules(makefile, argv[2]);
|
||||
fclose(makefile);
|
||||
|
||||
free_rom_spec(g_segments, g_segmentsCount);
|
||||
free(spec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -87,6 +87,8 @@ static bool parse_flags(char *str, unsigned int *flags)
|
|||
f |= FLAG_NOLOAD;
|
||||
else if (strcmp(str, "SYMS") == 0)
|
||||
f |= FLAG_SYMS;
|
||||
else if (strcmp(str, "OVERLAY") == 0)
|
||||
f |= FLAG_OVL;
|
||||
else
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ enum {
|
|||
FLAG_OBJECT = (1 << 1),
|
||||
FLAG_RAW = (1 << 2),
|
||||
FLAG_NOLOAD = (1 << 3),
|
||||
FLAG_SYMS = (1 << 4)
|
||||
FLAG_SYMS = (1 << 4),
|
||||
FLAG_OVL = (1 << 5)
|
||||
};
|
||||
|
||||
struct Include {
|
||||
|
|
|
|||
Loading…
Reference in New Issue