xref: /openbmc/u-boot/tools/mips-relocs.c (revision 5d6fefa8051a29b26169983cbcf9378bb363f8b2)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2703ec9ddSPaul Burton /*
3703ec9ddSPaul Burton  * MIPS Relocation Data Generator
4703ec9ddSPaul Burton  *
5703ec9ddSPaul Burton  * Copyright (c) 2017 Imagination Technologies Ltd.
6703ec9ddSPaul Burton  */
7703ec9ddSPaul Burton 
8e94136bdSPaul Burton #include <assert.h>
9703ec9ddSPaul Burton #include <elf.h>
10703ec9ddSPaul Burton #include <errno.h>
11703ec9ddSPaul Burton #include <fcntl.h>
12703ec9ddSPaul Burton #include <limits.h>
13d2bf1152SMasahiro Yamada #include <stdbool.h>
14703ec9ddSPaul Burton #include <stdio.h>
15703ec9ddSPaul Burton #include <stdlib.h>
16703ec9ddSPaul Burton #include <sys/mman.h>
17703ec9ddSPaul Burton #include <sys/stat.h>
18703ec9ddSPaul Burton #include <unistd.h>
19703ec9ddSPaul Burton 
20703ec9ddSPaul Burton #include <asm/relocs.h>
21703ec9ddSPaul Burton 
22703ec9ddSPaul Burton #define hdr_field(pfx, idx, field) ({				\
23703ec9ddSPaul Burton 	uint64_t _val;						\
24703ec9ddSPaul Burton 	unsigned int _size;					\
25703ec9ddSPaul Burton 								\
26703ec9ddSPaul Burton 	if (is_64) {						\
27703ec9ddSPaul Burton 		_val = pfx##hdr64[idx].field;			\
28703ec9ddSPaul Burton 		_size = sizeof(pfx##hdr64[0].field);		\
29703ec9ddSPaul Burton 	} else {						\
30703ec9ddSPaul Burton 		_val = pfx##hdr32[idx].field;			\
31703ec9ddSPaul Burton 		_size = sizeof(pfx##hdr32[0].field);		\
32703ec9ddSPaul Burton 	}							\
33703ec9ddSPaul Burton 								\
34703ec9ddSPaul Burton 	switch (_size) {					\
35703ec9ddSPaul Burton 	case 1:							\
36703ec9ddSPaul Burton 		break;						\
37703ec9ddSPaul Burton 	case 2:							\
38703ec9ddSPaul Burton 		_val = is_be ? be16toh(_val) : le16toh(_val);	\
39703ec9ddSPaul Burton 		break;						\
40703ec9ddSPaul Burton 	case 4:							\
41703ec9ddSPaul Burton 		_val = is_be ? be32toh(_val) : le32toh(_val);	\
42703ec9ddSPaul Burton 		break;						\
43703ec9ddSPaul Burton 	case 8:							\
44703ec9ddSPaul Burton 		_val = is_be ? be64toh(_val) : le64toh(_val);	\
45703ec9ddSPaul Burton 		break;						\
46703ec9ddSPaul Burton 	}							\
47703ec9ddSPaul Burton 								\
48703ec9ddSPaul Burton 	_val;							\
49703ec9ddSPaul Burton })
50703ec9ddSPaul Burton 
51703ec9ddSPaul Burton #define set_hdr_field(pfx, idx, field, val) ({			\
52703ec9ddSPaul Burton 	uint64_t _val;						\
53703ec9ddSPaul Burton 	unsigned int _size;					\
54703ec9ddSPaul Burton 								\
55703ec9ddSPaul Burton 	if (is_64)						\
56703ec9ddSPaul Burton 		_size = sizeof(pfx##hdr64[0].field);		\
57703ec9ddSPaul Burton 	else							\
58703ec9ddSPaul Burton 		_size = sizeof(pfx##hdr32[0].field);		\
59703ec9ddSPaul Burton 								\
60703ec9ddSPaul Burton 	switch (_size) {					\
61703ec9ddSPaul Burton 	case 1:							\
62703ec9ddSPaul Burton 		_val = val;					\
63703ec9ddSPaul Burton 		break;						\
64703ec9ddSPaul Burton 	case 2:							\
65703ec9ddSPaul Burton 		_val = is_be ? htobe16(val) : htole16(val);	\
66703ec9ddSPaul Burton 		break;						\
67703ec9ddSPaul Burton 	case 4:							\
68703ec9ddSPaul Burton 		_val = is_be ? htobe32(val) : htole32(val);	\
69703ec9ddSPaul Burton 		break;						\
70703ec9ddSPaul Burton 	case 8:							\
71703ec9ddSPaul Burton 		_val = is_be ? htobe64(val) : htole64(val);	\
72703ec9ddSPaul Burton 		break;						\
73e94136bdSPaul Burton 	default:						\
74e94136bdSPaul Burton 		/* We should never reach here */		\
75e94136bdSPaul Burton 		_val = 0;					\
76e94136bdSPaul Burton 		assert(0);					\
77e94136bdSPaul Burton 		break;						\
78703ec9ddSPaul Burton 	}							\
79703ec9ddSPaul Burton 								\
80703ec9ddSPaul Burton 	if (is_64)						\
81703ec9ddSPaul Burton 		pfx##hdr64[idx].field = _val;			\
82703ec9ddSPaul Burton 	else							\
83703ec9ddSPaul Burton 		pfx##hdr32[idx].field = _val;			\
84703ec9ddSPaul Burton })
85703ec9ddSPaul Burton 
86703ec9ddSPaul Burton #define ehdr_field(field) \
87703ec9ddSPaul Burton 	hdr_field(e, 0, field)
88703ec9ddSPaul Burton #define phdr_field(idx, field) \
89703ec9ddSPaul Burton 	hdr_field(p, idx, field)
90703ec9ddSPaul Burton #define shdr_field(idx, field) \
91703ec9ddSPaul Burton 	hdr_field(s, idx, field)
92703ec9ddSPaul Burton 
93703ec9ddSPaul Burton #define set_phdr_field(idx, field, val) \
94703ec9ddSPaul Burton 	set_hdr_field(p, idx, field, val)
95703ec9ddSPaul Burton #define set_shdr_field(idx, field, val) \
96703ec9ddSPaul Burton 	set_hdr_field(s, idx, field, val)
97703ec9ddSPaul Burton 
98703ec9ddSPaul Burton #define shstr(idx) (&shstrtab[idx])
99703ec9ddSPaul Burton 
100703ec9ddSPaul Burton bool is_64, is_be;
101703ec9ddSPaul Burton uint64_t text_base;
102703ec9ddSPaul Burton 
103703ec9ddSPaul Burton struct mips_reloc {
104703ec9ddSPaul Burton 	uint8_t type;
105703ec9ddSPaul Burton 	uint64_t offset;
106703ec9ddSPaul Burton } *relocs;
107703ec9ddSPaul Burton size_t relocs_sz, relocs_idx;
108703ec9ddSPaul Burton 
add_reloc(unsigned int type,uint64_t off)109703ec9ddSPaul Burton static int add_reloc(unsigned int type, uint64_t off)
110703ec9ddSPaul Burton {
111703ec9ddSPaul Burton 	struct mips_reloc *new;
112703ec9ddSPaul Burton 	size_t new_sz;
113703ec9ddSPaul Burton 
114703ec9ddSPaul Burton 	switch (type) {
115703ec9ddSPaul Burton 	case R_MIPS_NONE:
116703ec9ddSPaul Burton 	case R_MIPS_LO16:
117703ec9ddSPaul Burton 	case R_MIPS_PC16:
118703ec9ddSPaul Burton 	case R_MIPS_HIGHER:
119703ec9ddSPaul Burton 	case R_MIPS_HIGHEST:
120703ec9ddSPaul Burton 	case R_MIPS_PC21_S2:
121703ec9ddSPaul Burton 	case R_MIPS_PC26_S2:
122703ec9ddSPaul Burton 		/* Skip these relocs */
123703ec9ddSPaul Burton 		return 0;
124703ec9ddSPaul Burton 
125703ec9ddSPaul Burton 	default:
126703ec9ddSPaul Burton 		break;
127703ec9ddSPaul Burton 	}
128703ec9ddSPaul Burton 
129703ec9ddSPaul Burton 	if (relocs_idx == relocs_sz) {
130703ec9ddSPaul Burton 		new_sz = relocs_sz ? relocs_sz * 2 : 128;
131703ec9ddSPaul Burton 		new = realloc(relocs, new_sz * sizeof(*relocs));
132703ec9ddSPaul Burton 		if (!new) {
133703ec9ddSPaul Burton 			fprintf(stderr, "Out of memory\n");
134703ec9ddSPaul Burton 			return -ENOMEM;
135703ec9ddSPaul Burton 		}
136703ec9ddSPaul Burton 
137703ec9ddSPaul Burton 		relocs = new;
138703ec9ddSPaul Burton 		relocs_sz = new_sz;
139703ec9ddSPaul Burton 	}
140703ec9ddSPaul Burton 
141703ec9ddSPaul Burton 	relocs[relocs_idx++] = (struct mips_reloc){
142703ec9ddSPaul Burton 		.type = type,
143703ec9ddSPaul Burton 		.offset = off,
144703ec9ddSPaul Burton 	};
145703ec9ddSPaul Burton 
146703ec9ddSPaul Burton 	return 0;
147703ec9ddSPaul Burton }
148703ec9ddSPaul Burton 
parse_mips32_rel(const void * _rel)149703ec9ddSPaul Burton static int parse_mips32_rel(const void *_rel)
150703ec9ddSPaul Burton {
151703ec9ddSPaul Burton 	const Elf32_Rel *rel = _rel;
152703ec9ddSPaul Burton 	uint32_t off, type;
153703ec9ddSPaul Burton 
154703ec9ddSPaul Burton 	off = is_be ? be32toh(rel->r_offset) : le32toh(rel->r_offset);
155703ec9ddSPaul Burton 	off -= text_base;
156703ec9ddSPaul Burton 
157703ec9ddSPaul Burton 	type = is_be ? be32toh(rel->r_info) : le32toh(rel->r_info);
158703ec9ddSPaul Burton 	type = ELF32_R_TYPE(type);
159703ec9ddSPaul Burton 
160703ec9ddSPaul Burton 	return add_reloc(type, off);
161703ec9ddSPaul Burton }
162703ec9ddSPaul Burton 
parse_mips64_rela(const void * _rel)163703ec9ddSPaul Burton static int parse_mips64_rela(const void *_rel)
164703ec9ddSPaul Burton {
165703ec9ddSPaul Burton 	const Elf64_Rela *rel = _rel;
166703ec9ddSPaul Burton 	uint64_t off, type;
167703ec9ddSPaul Burton 
168703ec9ddSPaul Burton 	off = is_be ? be64toh(rel->r_offset) : le64toh(rel->r_offset);
169703ec9ddSPaul Burton 	off -= text_base;
170703ec9ddSPaul Burton 
171703ec9ddSPaul Burton 	type = rel->r_info >> (64 - 8);
172703ec9ddSPaul Burton 
173703ec9ddSPaul Burton 	return add_reloc(type, off);
174703ec9ddSPaul Burton }
175703ec9ddSPaul Burton 
output_uint(uint8_t ** buf,uint64_t val)176703ec9ddSPaul Burton static void output_uint(uint8_t **buf, uint64_t val)
177703ec9ddSPaul Burton {
178703ec9ddSPaul Burton 	uint64_t tmp;
179703ec9ddSPaul Burton 
180703ec9ddSPaul Burton 	do {
181703ec9ddSPaul Burton 		tmp = val & 0x7f;
182703ec9ddSPaul Burton 		val >>= 7;
183703ec9ddSPaul Burton 		tmp |= !!val << 7;
184703ec9ddSPaul Burton 		*(*buf)++ = tmp;
185703ec9ddSPaul Burton 	} while (val);
186703ec9ddSPaul Burton }
187703ec9ddSPaul Burton 
compare_relocs(const void * a,const void * b)188703ec9ddSPaul Burton static int compare_relocs(const void *a, const void *b)
189703ec9ddSPaul Burton {
190703ec9ddSPaul Burton 	const struct mips_reloc *ra = a, *rb = b;
191703ec9ddSPaul Burton 
192703ec9ddSPaul Burton 	return ra->offset - rb->offset;
193703ec9ddSPaul Burton }
194703ec9ddSPaul Burton 
main(int argc,char * argv[])195703ec9ddSPaul Burton int main(int argc, char *argv[])
196703ec9ddSPaul Burton {
197703ec9ddSPaul Burton 	unsigned int i, j, i_rel_shdr, sh_type, sh_entsize, sh_entries;
198*96301464SDaniel Schwierzeck 	size_t rel_size, rel_actual_size;
199703ec9ddSPaul Burton 	const char *shstrtab, *sh_name, *rel_pfx;
200703ec9ddSPaul Burton 	int (*parse_fn)(const void *rel);
201703ec9ddSPaul Burton 	uint8_t *buf_start, *buf;
202703ec9ddSPaul Burton 	const Elf32_Ehdr *ehdr32;
203703ec9ddSPaul Burton 	const Elf64_Ehdr *ehdr64;
204703ec9ddSPaul Burton 	uintptr_t sh_offset;
205703ec9ddSPaul Burton 	Elf32_Shdr *shdr32;
206703ec9ddSPaul Burton 	Elf64_Shdr *shdr64;
207703ec9ddSPaul Burton 	struct stat st;
208703ec9ddSPaul Burton 	int err, fd;
209703ec9ddSPaul Burton 	void *elf;
210703ec9ddSPaul Burton 	bool skip;
211703ec9ddSPaul Burton 
212703ec9ddSPaul Burton 	fd = open(argv[1], O_RDWR);
213703ec9ddSPaul Burton 	if (fd == -1) {
214703ec9ddSPaul Burton 		fprintf(stderr, "Unable to open input file %s\n", argv[1]);
215703ec9ddSPaul Burton 		err = errno;
216703ec9ddSPaul Burton 		goto out_ret;
217703ec9ddSPaul Burton 	}
218703ec9ddSPaul Burton 
219703ec9ddSPaul Burton 	err = fstat(fd, &st);
220703ec9ddSPaul Burton 	if (err) {
221703ec9ddSPaul Burton 		fprintf(stderr, "Unable to fstat() input file\n");
222703ec9ddSPaul Burton 		goto out_close_fd;
223703ec9ddSPaul Burton 	}
224703ec9ddSPaul Burton 
225703ec9ddSPaul Burton 	elf = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
226703ec9ddSPaul Burton 	if (elf == MAP_FAILED) {
227703ec9ddSPaul Burton 		fprintf(stderr, "Unable to mmap() input file\n");
228703ec9ddSPaul Burton 		err = errno;
229703ec9ddSPaul Burton 		goto out_close_fd;
230703ec9ddSPaul Burton 	}
231703ec9ddSPaul Burton 
232703ec9ddSPaul Burton 	ehdr32 = elf;
233703ec9ddSPaul Burton 	ehdr64 = elf;
234703ec9ddSPaul Burton 
235703ec9ddSPaul Burton 	if (memcmp(&ehdr32->e_ident[EI_MAG0], ELFMAG, SELFMAG)) {
236703ec9ddSPaul Burton 		fprintf(stderr, "Input file is not an ELF\n");
237703ec9ddSPaul Burton 		err = -EINVAL;
238703ec9ddSPaul Burton 		goto out_free_relocs;
239703ec9ddSPaul Burton 	}
240703ec9ddSPaul Burton 
241703ec9ddSPaul Burton 	if (ehdr32->e_ident[EI_VERSION] != EV_CURRENT) {
242703ec9ddSPaul Burton 		fprintf(stderr, "Unrecognised ELF version\n");
243703ec9ddSPaul Burton 		err = -EINVAL;
244703ec9ddSPaul Burton 		goto out_free_relocs;
245703ec9ddSPaul Burton 	}
246703ec9ddSPaul Burton 
247703ec9ddSPaul Burton 	switch (ehdr32->e_ident[EI_CLASS]) {
248703ec9ddSPaul Burton 	case ELFCLASS32:
249703ec9ddSPaul Burton 		is_64 = false;
250703ec9ddSPaul Burton 		break;
251703ec9ddSPaul Burton 	case ELFCLASS64:
252703ec9ddSPaul Burton 		is_64 = true;
253703ec9ddSPaul Burton 		break;
254703ec9ddSPaul Burton 	default:
255703ec9ddSPaul Burton 		fprintf(stderr, "Unrecognised ELF class\n");
256703ec9ddSPaul Burton 		err = -EINVAL;
257703ec9ddSPaul Burton 		goto out_free_relocs;
258703ec9ddSPaul Burton 	}
259703ec9ddSPaul Burton 
260703ec9ddSPaul Burton 	switch (ehdr32->e_ident[EI_DATA]) {
261703ec9ddSPaul Burton 	case ELFDATA2LSB:
262703ec9ddSPaul Burton 		is_be = false;
263703ec9ddSPaul Burton 		break;
264703ec9ddSPaul Burton 	case ELFDATA2MSB:
265703ec9ddSPaul Burton 		is_be = true;
266703ec9ddSPaul Burton 		break;
267703ec9ddSPaul Burton 	default:
268703ec9ddSPaul Burton 		fprintf(stderr, "Unrecognised ELF data encoding\n");
269703ec9ddSPaul Burton 		err = -EINVAL;
270703ec9ddSPaul Burton 		goto out_free_relocs;
271703ec9ddSPaul Burton 	}
272703ec9ddSPaul Burton 
273703ec9ddSPaul Burton 	if (ehdr_field(e_type) != ET_EXEC) {
274703ec9ddSPaul Burton 		fprintf(stderr, "Input ELF is not an executable\n");
275703ec9ddSPaul Burton 		printf("type 0x%lx\n", ehdr_field(e_type));
276703ec9ddSPaul Burton 		err = -EINVAL;
277703ec9ddSPaul Burton 		goto out_free_relocs;
278703ec9ddSPaul Burton 	}
279703ec9ddSPaul Burton 
280703ec9ddSPaul Burton 	if (ehdr_field(e_machine) != EM_MIPS) {
281703ec9ddSPaul Burton 		fprintf(stderr, "Input ELF does not target MIPS\n");
282703ec9ddSPaul Burton 		err = -EINVAL;
283703ec9ddSPaul Burton 		goto out_free_relocs;
284703ec9ddSPaul Burton 	}
285703ec9ddSPaul Burton 
286703ec9ddSPaul Burton 	shdr32 = elf + ehdr_field(e_shoff);
287703ec9ddSPaul Burton 	shdr64 = elf + ehdr_field(e_shoff);
288703ec9ddSPaul Burton 	shstrtab = elf + shdr_field(ehdr_field(e_shstrndx), sh_offset);
289703ec9ddSPaul Burton 
290703ec9ddSPaul Burton 	i_rel_shdr = UINT_MAX;
291703ec9ddSPaul Burton 	for (i = 0; i < ehdr_field(e_shnum); i++) {
292703ec9ddSPaul Burton 		sh_name = shstr(shdr_field(i, sh_name));
293703ec9ddSPaul Burton 
294*96301464SDaniel Schwierzeck 		if (!strcmp(sh_name, ".data.reloc")) {
295703ec9ddSPaul Burton 			i_rel_shdr = i;
296703ec9ddSPaul Burton 			continue;
297703ec9ddSPaul Burton 		}
298703ec9ddSPaul Burton 
299703ec9ddSPaul Burton 		if (!strcmp(sh_name, ".text")) {
300703ec9ddSPaul Burton 			text_base = shdr_field(i, sh_addr);
301703ec9ddSPaul Burton 			continue;
302703ec9ddSPaul Burton 		}
303703ec9ddSPaul Burton 	}
304703ec9ddSPaul Burton 	if (i_rel_shdr == UINT_MAX) {
305703ec9ddSPaul Burton 		fprintf(stderr, "Unable to find .rel section\n");
306703ec9ddSPaul Burton 		err = -EINVAL;
307703ec9ddSPaul Burton 		goto out_free_relocs;
308703ec9ddSPaul Burton 	}
309703ec9ddSPaul Burton 	if (!text_base) {
310703ec9ddSPaul Burton 		fprintf(stderr, "Unable to find .text base address\n");
311703ec9ddSPaul Burton 		err = -EINVAL;
312703ec9ddSPaul Burton 		goto out_free_relocs;
313703ec9ddSPaul Burton 	}
314703ec9ddSPaul Burton 
315703ec9ddSPaul Burton 	rel_pfx = is_64 ? ".rela." : ".rel.";
316703ec9ddSPaul Burton 
317703ec9ddSPaul Burton 	for (i = 0; i < ehdr_field(e_shnum); i++) {
318703ec9ddSPaul Burton 		sh_type = shdr_field(i, sh_type);
319703ec9ddSPaul Burton 		if ((sh_type != SHT_REL) && (sh_type != SHT_RELA))
320703ec9ddSPaul Burton 			continue;
321703ec9ddSPaul Burton 
322703ec9ddSPaul Burton 		sh_name = shstr(shdr_field(i, sh_name));
323703ec9ddSPaul Burton 		if (strncmp(sh_name, rel_pfx, strlen(rel_pfx))) {
324703ec9ddSPaul Burton 			if (strcmp(sh_name, ".rel") && strcmp(sh_name, ".rel.dyn"))
325703ec9ddSPaul Burton 				fprintf(stderr, "WARNING: Unexpected reloc section name '%s'\n", sh_name);
326703ec9ddSPaul Burton 			continue;
327703ec9ddSPaul Burton 		}
328703ec9ddSPaul Burton 
329703ec9ddSPaul Burton 		/*
330703ec9ddSPaul Burton 		 * Skip reloc sections which either don't correspond to another
331703ec9ddSPaul Burton 		 * section in the ELF, or whose corresponding section isn't
332703ec9ddSPaul Burton 		 * loaded as part of the U-Boot binary (ie. doesn't have the
333703ec9ddSPaul Burton 		 * alloc flags set).
334703ec9ddSPaul Burton 		 */
335703ec9ddSPaul Burton 		skip = true;
336703ec9ddSPaul Burton 		for (j = 0; j < ehdr_field(e_shnum); j++) {
337703ec9ddSPaul Burton 			if (strcmp(&sh_name[strlen(rel_pfx) - 1], shstr(shdr_field(j, sh_name))))
338703ec9ddSPaul Burton 				continue;
339703ec9ddSPaul Burton 
340703ec9ddSPaul Burton 			skip = !(shdr_field(j, sh_flags) & SHF_ALLOC);
341703ec9ddSPaul Burton 			break;
342703ec9ddSPaul Burton 		}
343703ec9ddSPaul Burton 		if (skip)
344703ec9ddSPaul Burton 			continue;
345703ec9ddSPaul Burton 
346703ec9ddSPaul Burton 		sh_offset = shdr_field(i, sh_offset);
347703ec9ddSPaul Burton 		sh_entsize = shdr_field(i, sh_entsize);
348703ec9ddSPaul Burton 		sh_entries = shdr_field(i, sh_size) / sh_entsize;
349703ec9ddSPaul Burton 
350703ec9ddSPaul Burton 		if (sh_type == SHT_REL) {
351703ec9ddSPaul Burton 			if (is_64) {
352703ec9ddSPaul Burton 				fprintf(stderr, "REL-style reloc in MIPS64 ELF?\n");
353703ec9ddSPaul Burton 				err = -EINVAL;
354703ec9ddSPaul Burton 				goto out_free_relocs;
355703ec9ddSPaul Burton 			} else {
356703ec9ddSPaul Burton 				parse_fn = parse_mips32_rel;
357703ec9ddSPaul Burton 			}
358703ec9ddSPaul Burton 		} else {
359703ec9ddSPaul Burton 			if (is_64) {
360703ec9ddSPaul Burton 				parse_fn = parse_mips64_rela;
361703ec9ddSPaul Burton 			} else {
362703ec9ddSPaul Burton 				fprintf(stderr, "RELA-style reloc in MIPS32 ELF?\n");
363703ec9ddSPaul Burton 				err = -EINVAL;
364703ec9ddSPaul Burton 				goto out_free_relocs;
365703ec9ddSPaul Burton 			}
366703ec9ddSPaul Burton 		}
367703ec9ddSPaul Burton 
368703ec9ddSPaul Burton 		for (j = 0; j < sh_entries; j++) {
369703ec9ddSPaul Burton 			err = parse_fn(elf + sh_offset + (j * sh_entsize));
370703ec9ddSPaul Burton 			if (err)
371703ec9ddSPaul Burton 				goto out_free_relocs;
372703ec9ddSPaul Burton 		}
373703ec9ddSPaul Burton 	}
374703ec9ddSPaul Burton 
375703ec9ddSPaul Burton 	/* Sort relocs in ascending order of offset */
376703ec9ddSPaul Burton 	qsort(relocs, relocs_idx, sizeof(*relocs), compare_relocs);
377703ec9ddSPaul Burton 
378703ec9ddSPaul Burton 	/* Make reloc offsets relative to their predecessor */
379703ec9ddSPaul Burton 	for (i = relocs_idx - 1; i > 0; i--)
380703ec9ddSPaul Burton 		relocs[i].offset -= relocs[i - 1].offset;
381703ec9ddSPaul Burton 
382703ec9ddSPaul Burton 	/* Write the relocations to the .rel section */
383703ec9ddSPaul Burton 	buf = buf_start = elf + shdr_field(i_rel_shdr, sh_offset);
384703ec9ddSPaul Burton 	for (i = 0; i < relocs_idx; i++) {
385703ec9ddSPaul Burton 		output_uint(&buf, relocs[i].type);
386703ec9ddSPaul Burton 		output_uint(&buf, relocs[i].offset >> 2);
387703ec9ddSPaul Burton 	}
388703ec9ddSPaul Burton 
389703ec9ddSPaul Burton 	/* Write a terminating R_MIPS_NONE (0) */
390703ec9ddSPaul Burton 	output_uint(&buf, R_MIPS_NONE);
391703ec9ddSPaul Burton 
392703ec9ddSPaul Burton 	/* Ensure the relocs didn't overflow the .rel section */
393703ec9ddSPaul Burton 	rel_size = shdr_field(i_rel_shdr, sh_size);
394703ec9ddSPaul Burton 	rel_actual_size = buf - buf_start;
395703ec9ddSPaul Burton 	if (rel_actual_size > rel_size) {
396*96301464SDaniel Schwierzeck 		fprintf(stderr, "Relocations overflow available space of 0x%lx (required 0x%lx)!\n",
397*96301464SDaniel Schwierzeck 			rel_size, rel_actual_size);
398*96301464SDaniel Schwierzeck 		fprintf(stderr, "Please adjust CONFIG_MIPS_RELOCATION_TABLE_SIZE to at least 0x%lx\n",
399*96301464SDaniel Schwierzeck 			(rel_actual_size + 0x100) & ~0xFF);
400*96301464SDaniel Schwierzeck 		err = -ENOMEM;
401*96301464SDaniel Schwierzeck 		goto out_free_relocs;
402703ec9ddSPaul Burton 	}
403703ec9ddSPaul Burton 
404703ec9ddSPaul Burton 	/* Make sure data is written back to the file */
405703ec9ddSPaul Burton 	err = msync(elf, st.st_size, MS_SYNC);
406703ec9ddSPaul Burton 	if (err) {
407703ec9ddSPaul Burton 		fprintf(stderr, "Failed to msync: %d\n", errno);
408703ec9ddSPaul Burton 		goto out_free_relocs;
409703ec9ddSPaul Burton 	}
410703ec9ddSPaul Burton 
411703ec9ddSPaul Burton out_free_relocs:
412703ec9ddSPaul Burton 	free(relocs);
413703ec9ddSPaul Burton 	munmap(elf, st.st_size);
414703ec9ddSPaul Burton out_close_fd:
415703ec9ddSPaul Burton 	close(fd);
416703ec9ddSPaul Burton out_ret:
417703ec9ddSPaul Burton 	return err;
418703ec9ddSPaul Burton }
419