1 // SPDX-License-Identifier: GPL-2.0+
3 * MIPS Relocation Data Generator
5 * Copyright (c) 2017 Imagination Technologies Ltd.
20 #include <asm/relocs.h>
22 #define hdr_field(pfx, idx, field) ({ \
27 _val = pfx##hdr64[idx].field; \
28 _size = sizeof(pfx##hdr64[0].field); \
30 _val = pfx##hdr32[idx].field; \
31 _size = sizeof(pfx##hdr32[0].field); \
38 _val = is_be ? be16toh(_val) : le16toh(_val); \
41 _val = is_be ? be32toh(_val) : le32toh(_val); \
44 _val = is_be ? be64toh(_val) : le64toh(_val); \
51 #define set_hdr_field(pfx, idx, field, val) ({ \
56 _size = sizeof(pfx##hdr64[0].field); \
58 _size = sizeof(pfx##hdr32[0].field); \
65 _val = is_be ? htobe16(val) : htole16(val); \
68 _val = is_be ? htobe32(val) : htole32(val); \
71 _val = is_be ? htobe64(val) : htole64(val); \
74 /* We should never reach here */ \
81 pfx##hdr64[idx].field = _val; \
83 pfx##hdr32[idx].field = _val; \
86 #define ehdr_field(field) \
87 hdr_field(e, 0, field)
88 #define phdr_field(idx, field) \
89 hdr_field(p, idx, field)
90 #define shdr_field(idx, field) \
91 hdr_field(s, idx, field)
93 #define set_phdr_field(idx, field, val) \
94 set_hdr_field(p, idx, field, val)
95 #define set_shdr_field(idx, field, val) \
96 set_hdr_field(s, idx, field, val)
98 #define shstr(idx) (&shstrtab[idx])
107 size_t relocs_sz
, relocs_idx
;
109 static int add_reloc(unsigned int type
, uint64_t off
)
111 struct mips_reloc
*new;
122 /* Skip these relocs */
129 if (relocs_idx
== relocs_sz
) {
130 new_sz
= relocs_sz
? relocs_sz
* 2 : 128;
131 new = realloc(relocs
, new_sz
* sizeof(*relocs
));
133 fprintf(stderr
, "Out of memory\n");
141 relocs
[relocs_idx
++] = (struct mips_reloc
){
149 static int parse_mips32_rel(const void *_rel
)
151 const Elf32_Rel
*rel
= _rel
;
154 off
= is_be
? be32toh(rel
->r_offset
) : le32toh(rel
->r_offset
);
157 type
= is_be
? be32toh(rel
->r_info
) : le32toh(rel
->r_info
);
158 type
= ELF32_R_TYPE(type
);
160 return add_reloc(type
, off
);
163 static int parse_mips64_rela(const void *_rel
)
165 const Elf64_Rela
*rel
= _rel
;
168 off
= is_be
? be64toh(rel
->r_offset
) : le64toh(rel
->r_offset
);
171 type
= rel
->r_info
>> (64 - 8);
173 return add_reloc(type
, off
);
176 static void output_uint(uint8_t **buf
, uint64_t val
)
188 static int compare_relocs(const void *a
, const void *b
)
190 const struct mips_reloc
*ra
= a
, *rb
= b
;
192 return ra
->offset
- rb
->offset
;
195 int main(int argc
, char *argv
[])
197 unsigned int i
, j
, i_rel_shdr
, sh_type
, sh_entsize
, sh_entries
;
198 size_t rel_size
, rel_actual_size
;
199 const char *shstrtab
, *sh_name
, *rel_pfx
;
200 int (*parse_fn
)(const void *rel
);
201 uint8_t *buf_start
, *buf
;
202 const Elf32_Ehdr
*ehdr32
;
203 const Elf64_Ehdr
*ehdr64
;
212 fd
= open(argv
[1], O_RDWR
);
214 fprintf(stderr
, "Unable to open input file %s\n", argv
[1]);
219 err
= fstat(fd
, &st
);
221 fprintf(stderr
, "Unable to fstat() input file\n");
225 elf
= mmap(NULL
, st
.st_size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
226 if (elf
== MAP_FAILED
) {
227 fprintf(stderr
, "Unable to mmap() input file\n");
235 if (memcmp(&ehdr32
->e_ident
[EI_MAG0
], ELFMAG
, SELFMAG
)) {
236 fprintf(stderr
, "Input file is not an ELF\n");
238 goto out_free_relocs
;
241 if (ehdr32
->e_ident
[EI_VERSION
] != EV_CURRENT
) {
242 fprintf(stderr
, "Unrecognised ELF version\n");
244 goto out_free_relocs
;
247 switch (ehdr32
->e_ident
[EI_CLASS
]) {
255 fprintf(stderr
, "Unrecognised ELF class\n");
257 goto out_free_relocs
;
260 switch (ehdr32
->e_ident
[EI_DATA
]) {
268 fprintf(stderr
, "Unrecognised ELF data encoding\n");
270 goto out_free_relocs
;
273 if (ehdr_field(e_type
) != ET_EXEC
) {
274 fprintf(stderr
, "Input ELF is not an executable\n");
275 printf("type 0x%lx\n", ehdr_field(e_type
));
277 goto out_free_relocs
;
280 if (ehdr_field(e_machine
) != EM_MIPS
) {
281 fprintf(stderr
, "Input ELF does not target MIPS\n");
283 goto out_free_relocs
;
286 shdr32
= elf
+ ehdr_field(e_shoff
);
287 shdr64
= elf
+ ehdr_field(e_shoff
);
288 shstrtab
= elf
+ shdr_field(ehdr_field(e_shstrndx
), sh_offset
);
290 i_rel_shdr
= UINT_MAX
;
291 for (i
= 0; i
< ehdr_field(e_shnum
); i
++) {
292 sh_name
= shstr(shdr_field(i
, sh_name
));
294 if (!strcmp(sh_name
, ".data.reloc")) {
299 if (!strcmp(sh_name
, ".text")) {
300 text_base
= shdr_field(i
, sh_addr
);
304 if (i_rel_shdr
== UINT_MAX
) {
305 fprintf(stderr
, "Unable to find .rel section\n");
307 goto out_free_relocs
;
310 fprintf(stderr
, "Unable to find .text base address\n");
312 goto out_free_relocs
;
315 rel_pfx
= is_64
? ".rela." : ".rel.";
317 for (i
= 0; i
< ehdr_field(e_shnum
); i
++) {
318 sh_type
= shdr_field(i
, sh_type
);
319 if ((sh_type
!= SHT_REL
) && (sh_type
!= SHT_RELA
))
322 sh_name
= shstr(shdr_field(i
, sh_name
));
323 if (strncmp(sh_name
, rel_pfx
, strlen(rel_pfx
))) {
324 if (strcmp(sh_name
, ".rel") && strcmp(sh_name
, ".rel.dyn"))
325 fprintf(stderr
, "WARNING: Unexpected reloc section name '%s'\n", sh_name
);
330 * Skip reloc sections which either don't correspond to another
331 * section in the ELF, or whose corresponding section isn't
332 * loaded as part of the U-Boot binary (ie. doesn't have the
336 for (j
= 0; j
< ehdr_field(e_shnum
); j
++) {
337 if (strcmp(&sh_name
[strlen(rel_pfx
) - 1], shstr(shdr_field(j
, sh_name
))))
340 skip
= !(shdr_field(j
, sh_flags
) & SHF_ALLOC
);
346 sh_offset
= shdr_field(i
, sh_offset
);
347 sh_entsize
= shdr_field(i
, sh_entsize
);
348 sh_entries
= shdr_field(i
, sh_size
) / sh_entsize
;
350 if (sh_type
== SHT_REL
) {
352 fprintf(stderr
, "REL-style reloc in MIPS64 ELF?\n");
354 goto out_free_relocs
;
356 parse_fn
= parse_mips32_rel
;
360 parse_fn
= parse_mips64_rela
;
362 fprintf(stderr
, "RELA-style reloc in MIPS32 ELF?\n");
364 goto out_free_relocs
;
368 for (j
= 0; j
< sh_entries
; j
++) {
369 err
= parse_fn(elf
+ sh_offset
+ (j
* sh_entsize
));
371 goto out_free_relocs
;
375 /* Sort relocs in ascending order of offset */
376 qsort(relocs
, relocs_idx
, sizeof(*relocs
), compare_relocs
);
378 /* Make reloc offsets relative to their predecessor */
379 for (i
= relocs_idx
- 1; i
> 0; i
--)
380 relocs
[i
].offset
-= relocs
[i
- 1].offset
;
382 /* Write the relocations to the .rel section */
383 buf
= buf_start
= elf
+ shdr_field(i_rel_shdr
, sh_offset
);
384 for (i
= 0; i
< relocs_idx
; i
++) {
385 output_uint(&buf
, relocs
[i
].type
);
386 output_uint(&buf
, relocs
[i
].offset
>> 2);
389 /* Write a terminating R_MIPS_NONE (0) */
390 output_uint(&buf
, R_MIPS_NONE
);
392 /* Ensure the relocs didn't overflow the .rel section */
393 rel_size
= shdr_field(i_rel_shdr
, sh_size
);
394 rel_actual_size
= buf
- buf_start
;
395 if (rel_actual_size
> rel_size
) {
396 fprintf(stderr
, "Relocations overflow available space of 0x%lx (required 0x%lx)!\n",
397 rel_size
, rel_actual_size
);
398 fprintf(stderr
, "Please adjust CONFIG_MIPS_RELOCATION_TABLE_SIZE to at least 0x%lx\n",
399 (rel_actual_size
+ 0x100) & ~0xFF);
401 goto out_free_relocs
;
404 /* Make sure data is written back to the file */
405 err
= msync(elf
, st
.st_size
, MS_SYNC
);
407 fprintf(stderr
, "Failed to msync: %d\n", errno
);
408 goto out_free_relocs
;
413 munmap(elf
, st
.st_size
);