mirror of
https://github.com/zeldaret/oot
synced 2026-05-23 06:54:24 -04:00
2c481eaeeb
* Partial linking of overlay segments, relax linker script alignment * Partial linking of all spec segments * Fix update, remove _RomPos from linker script * iQue version working pending COM-plugin update * Add plf map file resolution to sym_info.py, local symbol merging is broken * git subrepo pull tools/com-plugin subrepo: subdir: "tools/com-plugin" merged: "c4f3ba845" upstream: origin: "git@github.com:Thar0/com-plugin.git" branch: "main" commit: "c4f3ba845" git-subrepo: version: "0.4.6" origin: "https://github.com/ingydotnet/git-subrepo" commit: "110b9eb" * Make tools compatible with partial linking Co-authored-by: Anghelo Carvajal <angheloalf95@gmail.com> * Remove unused files * Fix some makefile bits * mkspecrules cleanup * Comment on the makerom linker layout in mkldscript * Revert linker padding strategy back to pad_text spec directives * Comment on objcopy elf -> rom step * Adjust tool descriptions * Fix compressed builds * rm reloc_prereq, no longer used * Makefile merge fix Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com> --------- Co-authored-by: Anghelo Carvajal <angheloalf95@gmail.com> Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com>
184 lines
5.7 KiB
C
184 lines
5.7 KiB
C
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "spec.h"
|
|
#include "util.h"
|
|
|
|
struct Segment* g_segments;
|
|
int g_segmentsCount;
|
|
|
|
static void write_progbits_section(FILE *fout, struct Segment *seg, const char *section_name, bool with_padding)
|
|
{
|
|
fprintf(fout, " .%s :\n {\n", section_name);
|
|
|
|
for (int i = 0; i < seg->includesCount; i++) {
|
|
fprintf(fout, " %s(.%s*)\n", seg->includes[i].fpath, section_name);
|
|
if (with_padding && seg->includes[i].linkerPadding != 0)
|
|
fprintf(fout, " . += 0x%X;\n", seg->includes[i].linkerPadding);
|
|
}
|
|
|
|
fprintf(fout, " }\n");
|
|
}
|
|
|
|
static void write_noload_section(FILE *fout, struct Segment *seg, const char *section_name)
|
|
{
|
|
fprintf(fout, " .%s (NOLOAD) :\n {\n", section_name);
|
|
|
|
for (int i = 0; i < seg->includesCount; i++)
|
|
fprintf(fout, " %s(.%s*)\n", seg->includes[i].fpath, section_name);
|
|
|
|
fprintf(fout, " }\n");
|
|
}
|
|
|
|
/**
|
|
* Write a linker script for partial linking of a single segment.
|
|
*
|
|
* The original plan was to use a single linker script and pass the files over the command line to ld,
|
|
* however in the iQue version we have bss files that are created partway through linking that need
|
|
* special handling in the linker script.
|
|
*/
|
|
static void write_linker_script(FILE *fout, struct Segment *seg)
|
|
{
|
|
fprintf(fout,
|
|
"OUTPUT_ARCH (mips)\n"
|
|
"SECTIONS {\n"
|
|
);
|
|
|
|
write_progbits_section(fout, seg, "text", true);
|
|
write_progbits_section(fout, seg, "data", false);
|
|
write_progbits_section(fout, seg, "rodata", false);
|
|
write_noload_section(fout, seg, "bss");
|
|
|
|
/* 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.
|
|
*/
|
|
fprintf(fout,
|
|
" /DISCARD/ :\n"
|
|
" {\n"
|
|
" *(.gptab.*)\n"
|
|
" }\n"
|
|
"}\n"
|
|
);
|
|
}
|
|
|
|
static void write_overlay_rules(FILE *fout, const char *ovls_dir)
|
|
{
|
|
for (int i = 0; i < g_segmentsCount; i++) {
|
|
Segment *seg = &g_segments[i];
|
|
|
|
/* Write rule for partial linkage of this segment */
|
|
fprintf(fout, "%s/%s.plf:", ovls_dir, seg->name);
|
|
for (int j = 0; j < seg->includesCount; j++) {
|
|
if (seg->includes[j].fpath[0] != '*') /* Skip iQue bss files */
|
|
fprintf(fout, " \\\n\t\t%s", seg->includes[j].fpath);
|
|
}
|
|
fprintf(fout, " \\\n\t\t$(SEGMENTS_DIR)/Makefile");
|
|
fprintf(fout, "\n"
|
|
"\t@echo Linking \"%s\"\n"
|
|
"\t$(SEG_VERBOSE)$(LD) $(SEG_LDFLAGS) -o $@\n"
|
|
"\n", seg->name);
|
|
}
|
|
|
|
/* List every expected plf in a variable */
|
|
fprintf(fout, "SEGMENT_FILES :=");
|
|
for (int i = 0; i < g_segmentsCount; i++)
|
|
fprintf(fout, " \\\n\t\t%s/%s.plf", ovls_dir, g_segments[i].name);
|
|
|
|
/* List overlay plfs in a variable */
|
|
fprintf(fout, "\n\nOVL_SEGMENT_FILES :=");
|
|
for (int 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, "spec segment makefile 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;
|
|
}
|
|
|
|
const char *spec_path = argv[1];
|
|
const char *seg_dir = argv[2];
|
|
const char *makefile_path = argv[3];
|
|
|
|
if (seg_dir[0] == '\0') {
|
|
usage(argv[0]);
|
|
return 2;
|
|
}
|
|
|
|
spec = util_read_whole_file(spec_path, &size);
|
|
parse_rom_spec(spec, &g_segments, &g_segmentsCount);
|
|
|
|
makefile = fopen(makefile_path, "w");
|
|
if (makefile == NULL)
|
|
util_fatal_error("failed to open file '%s' for writing", makefile_path);
|
|
write_overlay_rules(makefile, seg_dir);
|
|
fclose(makefile);
|
|
|
|
// Find the longest segment name
|
|
size_t max_name = 1;
|
|
for (int i = 0; i < g_segmentsCount; i++) {
|
|
size_t namelen = strlen(g_segments[i].name);
|
|
if (namelen > max_name)
|
|
max_name = namelen;
|
|
}
|
|
|
|
size_t seg_dir_len = strlen(seg_dir);
|
|
|
|
// Check if segments dir path needs a path separator appended
|
|
bool needs_sep = seg_dir[seg_dir_len - 1] != '/';
|
|
// Allocate a buffer large enough for the longest path
|
|
char *ld_outpath = malloc(seg_dir_len + needs_sep + max_name + strlen(".ld") + 1);
|
|
|
|
// Write segments dir path now since it's always the same
|
|
strcpy(ld_outpath, seg_dir);
|
|
if (needs_sep) {
|
|
ld_outpath[seg_dir_len] = '/';
|
|
seg_dir_len++;
|
|
}
|
|
|
|
for (int i = 0; i < g_segmentsCount; i++) {
|
|
Segment *seg = &g_segments[i];
|
|
|
|
// Form the full path for this segment's linker script
|
|
strcpy(ld_outpath + seg_dir_len, seg->name);
|
|
ld_outpath[seg_dir_len + strlen(seg->name) + 0] = '.';
|
|
ld_outpath[seg_dir_len + strlen(seg->name) + 1] = 'l';
|
|
ld_outpath[seg_dir_len + strlen(seg->name) + 2] = 'd';
|
|
ld_outpath[seg_dir_len + strlen(seg->name) + 3] = '\0';
|
|
|
|
FILE *ldf = fopen(ld_outpath, "w");
|
|
write_linker_script(ldf, seg);
|
|
fclose(ldf);
|
|
}
|
|
|
|
free(ld_outpath);
|
|
|
|
|
|
free_rom_spec(g_segments, g_segmentsCount);
|
|
free(spec);
|
|
|
|
return 0;
|
|
}
|