1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
#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;
}
|