]>
git.ipfire.org Git - thirdparty/u-boot.git/blob - tools/mips-relocs.c
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
, load_sz
;
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
;
214 fd
= open(argv
[1], O_RDWR
);
216 fprintf(stderr
, "Unable to open input file %s\n", argv
[1]);
221 err
= fstat(fd
, &st
);
223 fprintf(stderr
, "Unable to fstat() input file\n");
227 elf
= mmap(NULL
, st
.st_size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
228 if (elf
== MAP_FAILED
) {
229 fprintf(stderr
, "Unable to mmap() input file\n");
237 if (memcmp(&ehdr32
->e_ident
[EI_MAG0
], ELFMAG
, SELFMAG
)) {
238 fprintf(stderr
, "Input file is not an ELF\n");
240 goto out_free_relocs
;
243 if (ehdr32
->e_ident
[EI_VERSION
] != EV_CURRENT
) {
244 fprintf(stderr
, "Unrecognised ELF version\n");
246 goto out_free_relocs
;
249 switch (ehdr32
->e_ident
[EI_CLASS
]) {
257 fprintf(stderr
, "Unrecognised ELF class\n");
259 goto out_free_relocs
;
262 switch (ehdr32
->e_ident
[EI_DATA
]) {
270 fprintf(stderr
, "Unrecognised ELF data encoding\n");
272 goto out_free_relocs
;
275 if (ehdr_field(e_type
) != ET_EXEC
) {
276 fprintf(stderr
, "Input ELF is not an executable\n");
277 printf("type 0x%lx\n", ehdr_field(e_type
));
279 goto out_free_relocs
;
282 if (ehdr_field(e_machine
) != EM_MIPS
) {
283 fprintf(stderr
, "Input ELF does not target MIPS\n");
285 goto out_free_relocs
;
288 phdr32
= elf
+ ehdr_field(e_phoff
);
289 phdr64
= elf
+ ehdr_field(e_phoff
);
290 shdr32
= elf
+ ehdr_field(e_shoff
);
291 shdr64
= elf
+ ehdr_field(e_shoff
);
292 shstrtab
= elf
+ shdr_field(ehdr_field(e_shstrndx
), sh_offset
);
294 i_rel_shdr
= UINT_MAX
;
295 for (i
= 0; i
< ehdr_field(e_shnum
); i
++) {
296 sh_name
= shstr(shdr_field(i
, sh_name
));
298 if (!strcmp(sh_name
, ".rel")) {
303 if (!strcmp(sh_name
, ".text")) {
304 text_base
= shdr_field(i
, sh_addr
);
308 if (i_rel_shdr
== UINT_MAX
) {
309 fprintf(stderr
, "Unable to find .rel section\n");
311 goto out_free_relocs
;
314 fprintf(stderr
, "Unable to find .text base address\n");
316 goto out_free_relocs
;
319 rel_pfx
= is_64
? ".rela." : ".rel.";
321 for (i
= 0; i
< ehdr_field(e_shnum
); i
++) {
322 sh_type
= shdr_field(i
, sh_type
);
323 if ((sh_type
!= SHT_REL
) && (sh_type
!= SHT_RELA
))
326 sh_name
= shstr(shdr_field(i
, sh_name
));
327 if (strncmp(sh_name
, rel_pfx
, strlen(rel_pfx
))) {
328 if (strcmp(sh_name
, ".rel") && strcmp(sh_name
, ".rel.dyn"))
329 fprintf(stderr
, "WARNING: Unexpected reloc section name '%s'\n", sh_name
);
334 * Skip reloc sections which either don't correspond to another
335 * section in the ELF, or whose corresponding section isn't
336 * loaded as part of the U-Boot binary (ie. doesn't have the
340 for (j
= 0; j
< ehdr_field(e_shnum
); j
++) {
341 if (strcmp(&sh_name
[strlen(rel_pfx
) - 1], shstr(shdr_field(j
, sh_name
))))
344 skip
= !(shdr_field(j
, sh_flags
) & SHF_ALLOC
);
350 sh_offset
= shdr_field(i
, sh_offset
);
351 sh_entsize
= shdr_field(i
, sh_entsize
);
352 sh_entries
= shdr_field(i
, sh_size
) / sh_entsize
;
354 if (sh_type
== SHT_REL
) {
356 fprintf(stderr
, "REL-style reloc in MIPS64 ELF?\n");
358 goto out_free_relocs
;
360 parse_fn
= parse_mips32_rel
;
364 parse_fn
= parse_mips64_rela
;
366 fprintf(stderr
, "RELA-style reloc in MIPS32 ELF?\n");
368 goto out_free_relocs
;
372 for (j
= 0; j
< sh_entries
; j
++) {
373 err
= parse_fn(elf
+ sh_offset
+ (j
* sh_entsize
));
375 goto out_free_relocs
;
379 /* Sort relocs in ascending order of offset */
380 qsort(relocs
, relocs_idx
, sizeof(*relocs
), compare_relocs
);
382 /* Make reloc offsets relative to their predecessor */
383 for (i
= relocs_idx
- 1; i
> 0; i
--)
384 relocs
[i
].offset
-= relocs
[i
- 1].offset
;
386 /* Write the relocations to the .rel section */
387 buf
= buf_start
= elf
+ shdr_field(i_rel_shdr
, sh_offset
);
388 for (i
= 0; i
< relocs_idx
; i
++) {
389 output_uint(&buf
, relocs
[i
].type
);
390 output_uint(&buf
, relocs
[i
].offset
>> 2);
393 /* Write a terminating R_MIPS_NONE (0) */
394 output_uint(&buf
, R_MIPS_NONE
);
396 /* Ensure the relocs didn't overflow the .rel section */
397 rel_size
= shdr_field(i_rel_shdr
, sh_size
);
398 rel_actual_size
= buf
- buf_start
;
399 if (rel_actual_size
> rel_size
) {
400 fprintf(stderr
, "Relocs overflowed .rel section\n");
404 /* Update the .rel section's size */
405 set_shdr_field(i_rel_shdr
, sh_size
, rel_actual_size
);
407 /* Shrink the PT_LOAD program header filesz (ie. shrink u-boot.bin) */
408 for (i
= 0; i
< ehdr_field(e_phnum
); i
++) {
409 if (phdr_field(i
, p_type
) != PT_LOAD
)
412 load_sz
= phdr_field(i
, p_filesz
);
413 load_sz
-= rel_size
- rel_actual_size
;
414 set_phdr_field(i
, p_filesz
, load_sz
);
418 /* Make sure data is written back to the file */
419 err
= msync(elf
, st
.st_size
, MS_SYNC
);
421 fprintf(stderr
, "Failed to msync: %d\n", errno
);
422 goto out_free_relocs
;
427 munmap(elf
, st
.st_size
);