Adding support for new type requires to change code layout that's why move elf64 code to own function for easier maintenance. It also solves the problem with not calling fclose in case of error. Return value from rela_elf64 is saved to variable that's why fclose() is called all the time. Signed-off-by: Michal Simek <michal.simek@amd.com> Link: https://lore.kernel.org/r/21763b80527521c85ca7d4ac64ad6ff4885409c8.1655299267.git.michal.simek@amd.com
337 lines
7.1 KiB
C
337 lines
7.1 KiB
C
// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause
|
|
/*
|
|
* Copyright 2013 Freescale Semiconductor, Inc.
|
|
*
|
|
* 64-bit and little-endian target only until we need to support a different
|
|
* arch that needs this.
|
|
*/
|
|
|
|
#include <elf.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "compiler.h"
|
|
|
|
#ifndef R_AARCH64_RELATIVE
|
|
#define R_AARCH64_RELATIVE 1027
|
|
#endif
|
|
|
|
static int ei_class;
|
|
|
|
static uint64_t rela_start, rela_end, text_base;
|
|
|
|
static const bool debug_en;
|
|
|
|
static void debug(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (debug_en) {
|
|
va_start(args, fmt);
|
|
vprintf(fmt, args);
|
|
va_end(args);
|
|
}
|
|
}
|
|
|
|
static bool supported_rela(Elf64_Rela *rela)
|
|
{
|
|
uint64_t mask = 0xffffffffULL; /* would be different on 32-bit */
|
|
uint32_t type = rela->r_info & mask;
|
|
|
|
switch (type) {
|
|
#ifdef R_AARCH64_RELATIVE
|
|
case R_AARCH64_RELATIVE:
|
|
return true;
|
|
#endif
|
|
default:
|
|
fprintf(stderr, "warning: unsupported relocation type %"
|
|
PRIu32 " at %" PRIx64 "\n",
|
|
type, rela->r_offset);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static int decode_elf64(FILE *felf, char **argv)
|
|
{
|
|
size_t size;
|
|
Elf64_Ehdr header;
|
|
uint64_t section_header_base, section_header_size, sh_offset, sh_size;
|
|
Elf64_Shdr *sh_table; /* Elf symbol table */
|
|
int ret, i, machine;
|
|
char *sh_str;
|
|
|
|
debug("64bit version\n");
|
|
|
|
/* Make sure we are at start */
|
|
rewind(felf);
|
|
|
|
size = fread(&header, 1, sizeof(header), felf);
|
|
if (size != sizeof(header)) {
|
|
fclose(felf);
|
|
return 25;
|
|
}
|
|
|
|
machine = header.e_machine;
|
|
debug("Machine\t%d\n", machine);
|
|
|
|
text_base = header.e_entry;
|
|
section_header_base = header.e_shoff;
|
|
section_header_size = header.e_shentsize * header.e_shnum;
|
|
|
|
sh_table = malloc(section_header_size);
|
|
if (!sh_table) {
|
|
fprintf(stderr, "%s: Cannot allocate space for section header\n",
|
|
argv[0]);
|
|
fclose(felf);
|
|
return 26;
|
|
}
|
|
|
|
ret = fseek(felf, section_header_base, SEEK_SET);
|
|
if (ret) {
|
|
fprintf(stderr, "%s: Can't set pointer to section header: %x/%lx\n",
|
|
argv[0], ret, section_header_base);
|
|
free(sh_table);
|
|
fclose(felf);
|
|
return 26;
|
|
}
|
|
|
|
size = fread(sh_table, 1, section_header_size, felf);
|
|
if (size != section_header_size) {
|
|
fprintf(stderr, "%s: Can't read section header: %lx/%lx\n",
|
|
argv[0], size, section_header_size);
|
|
free(sh_table);
|
|
fclose(felf);
|
|
return 27;
|
|
}
|
|
|
|
sh_size = sh_table[header.e_shstrndx].sh_size;
|
|
debug("e_shstrndx\t0x%08x\n", header.e_shstrndx);
|
|
debug("sh_size\t\t0x%08lx\n", sh_size);
|
|
|
|
sh_str = malloc(sh_size);
|
|
if (!sh_str) {
|
|
fprintf(stderr, "malloc failed\n");
|
|
free(sh_table);
|
|
fclose(felf);
|
|
return 28;
|
|
}
|
|
|
|
/*
|
|
* Specifies the byte offset from the beginning of the file
|
|
* to the first byte in the section.
|
|
*/
|
|
sh_offset = sh_table[header.e_shstrndx].sh_offset;
|
|
|
|
debug("sh_offset\t0x%08x\n", header.e_shnum);
|
|
|
|
ret = fseek(felf, sh_offset, SEEK_SET);
|
|
if (ret) {
|
|
fprintf(stderr, "Setting up sh_offset failed\n");
|
|
free(sh_str);
|
|
free(sh_table);
|
|
fclose(felf);
|
|
return 29;
|
|
}
|
|
|
|
size = fread(sh_str, 1, sh_size, felf);
|
|
if (size != sh_size) {
|
|
fprintf(stderr, "%s: Can't read section: %lx/%lx\n",
|
|
argv[0], size, sh_size);
|
|
free(sh_str);
|
|
free(sh_table);
|
|
fclose(felf);
|
|
return 30;
|
|
}
|
|
|
|
for (i = 0; i < header.e_shnum; i++) {
|
|
/* fprintf(stderr, "%s\n", sh_str + sh_table[i].sh_name); Debug only */
|
|
if (!strcmp(".rela.dyn", (sh_str + sh_table[i].sh_name))) {
|
|
debug("Found section\t\".rela_dyn\"\n");
|
|
debug(" at addr\t0x%08x\n",
|
|
(unsigned int)sh_table[i].sh_addr);
|
|
debug(" at offset\t0x%08x\n",
|
|
(unsigned int)sh_table[i].sh_offset);
|
|
debug(" of size\t0x%08x\n",
|
|
(unsigned int)sh_table[i].sh_size);
|
|
rela_start = sh_table[i].sh_addr;
|
|
rela_end = rela_start + sh_table[i].sh_size;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Clean up */
|
|
free(sh_str);
|
|
free(sh_table);
|
|
fclose(felf);
|
|
|
|
debug("text_base\t0x%08lx\n", text_base);
|
|
debug("rela_start\t0x%08lx\n", rela_start);
|
|
debug("rela_end\t0x%08lx\n", rela_end);
|
|
|
|
if (!rela_start)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int decode_elf(char **argv)
|
|
{
|
|
FILE *felf;
|
|
size_t size;
|
|
unsigned char e_ident[EI_NIDENT];
|
|
|
|
felf = fopen(argv[2], "r+b");
|
|
if (!felf) {
|
|
fprintf(stderr, "%s: Cannot open %s: %s\n",
|
|
argv[0], argv[5], strerror(errno));
|
|
return 2;
|
|
}
|
|
|
|
size = fread(e_ident, 1, EI_NIDENT, felf);
|
|
if (size != EI_NIDENT) {
|
|
fclose(felf);
|
|
return 25;
|
|
}
|
|
|
|
/* Check if this is really ELF file */
|
|
if (e_ident[0] != 0x7f &&
|
|
e_ident[1] != 'E' &&
|
|
e_ident[2] != 'L' &&
|
|
e_ident[3] != 'F') {
|
|
fclose(felf);
|
|
return 1;
|
|
}
|
|
|
|
ei_class = e_ident[4];
|
|
debug("EI_CLASS(1=32bit, 2=64bit) %d\n", ei_class);
|
|
|
|
if (ei_class == 2)
|
|
return decode_elf64(felf, argv);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int rela_elf64(char **argv, FILE *f)
|
|
{
|
|
int i, num;
|
|
|
|
if ((rela_end - rela_start) % sizeof(Elf64_Rela)) {
|
|
fprintf(stderr, "%s: rela size isn't a multiple of Elf64_Rela\n", argv[0]);
|
|
return 3;
|
|
}
|
|
|
|
num = (rela_end - rela_start) / sizeof(Elf64_Rela);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
Elf64_Rela rela, swrela;
|
|
uint64_t pos = rela_start + sizeof(Elf64_Rela) * i;
|
|
uint64_t addr;
|
|
|
|
if (fseek(f, pos, SEEK_SET) < 0) {
|
|
fprintf(stderr, "%s: %s: seek to %" PRIx64
|
|
" failed: %s\n",
|
|
argv[0], argv[1], pos, strerror(errno));
|
|
}
|
|
|
|
if (fread(&rela, sizeof(rela), 1, f) != 1) {
|
|
fprintf(stderr, "%s: %s: read rela failed at %"
|
|
PRIx64 "\n",
|
|
argv[0], argv[1], pos);
|
|
return 4;
|
|
}
|
|
|
|
swrela.r_offset = cpu_to_le64(rela.r_offset);
|
|
swrela.r_info = cpu_to_le64(rela.r_info);
|
|
swrela.r_addend = cpu_to_le64(rela.r_addend);
|
|
|
|
if (!supported_rela(&swrela))
|
|
continue;
|
|
|
|
debug("Rela %" PRIx64 " %" PRIu64 " %" PRIx64 "\n",
|
|
swrela.r_offset, swrela.r_info, swrela.r_addend);
|
|
|
|
if (swrela.r_offset < text_base) {
|
|
fprintf(stderr, "%s: %s: bad rela at %" PRIx64 "\n",
|
|
argv[0], argv[1], pos);
|
|
return 4;
|
|
}
|
|
|
|
addr = swrela.r_offset - text_base;
|
|
|
|
if (fseek(f, addr, SEEK_SET) < 0) {
|
|
fprintf(stderr, "%s: %s: seek to %"
|
|
PRIx64 " failed: %s\n",
|
|
argv[0], argv[1], addr, strerror(errno));
|
|
}
|
|
|
|
if (fwrite(&rela.r_addend, sizeof(rela.r_addend), 1, f) != 1) {
|
|
fprintf(stderr, "%s: %s: write failed at %" PRIx64 "\n",
|
|
argv[0], argv[1], addr);
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
FILE *f;
|
|
int ret;
|
|
uint64_t file_size;
|
|
|
|
if (argc != 3) {
|
|
fprintf(stderr, "Statically apply ELF rela relocations\n");
|
|
fprintf(stderr, "Usage: %s <bin file> <u-boot ELF>\n",
|
|
argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
ret = decode_elf(argv);
|
|
if (ret) {
|
|
fprintf(stderr, "ELF decoding failed\n");
|
|
return ret;
|
|
}
|
|
|
|
if (rela_start > rela_end || rela_start < text_base) {
|
|
fprintf(stderr, "%s: bad rela bounds\n", argv[0]);
|
|
return 3;
|
|
}
|
|
|
|
rela_start -= text_base;
|
|
rela_end -= text_base;
|
|
|
|
f = fopen(argv[1], "r+b");
|
|
if (!f) {
|
|
fprintf(stderr, "%s: Cannot open %s: %s\n",
|
|
argv[0], argv[1], strerror(errno));
|
|
return 2;
|
|
}
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
file_size = ftell(f);
|
|
rewind(f);
|
|
|
|
if (rela_end > file_size) {
|
|
// Most likely compiler inserted some section that didn't get
|
|
// objcopy-ed into the final binary
|
|
rela_end = file_size;
|
|
}
|
|
|
|
if (ei_class == 2)
|
|
ret = rela_elf64(argv, f);
|
|
|
|
if (fclose(f) < 0) {
|
|
fprintf(stderr, "%s: %s: close failed: %s\n",
|
|
argv[0], argv[1], strerror(errno));
|
|
return 4;
|
|
}
|
|
|
|
return ret;
|
|
}
|