[PATCH] tools: relocate-rela: Fix ELF decoding on big-endian hosts
Michal Simek
michal.simek at amd.com
Fri Jul 15 09:35:15 CEST 2022
Hi,
On 7/15/22 08:40, Samuel Holland wrote:
> The new ELF decoding logic assumed that the target binary has the same
> endianness as the host, which broke building ARM64 firmware binaries on
> big-endian machines.
>
> This commit fixes the ELF64 decoding to be host-endianness-neutral, and
> applies the same changes to the ELF32 decoding. It does not fix the
> microblaze-specific dynamic symbol decoding.
>
> It also corrects the functions used for byte swapping in rela_elf64()
> and rela_elf32(). The result is the same, but semantically the code is
> converting bytes read from a foreign-endianness file to host byte order.
>
> Fixes: 4c9e2d643460 ("tools: relocate-rela: Read rela start/end directly from ELF")
> Fixes: a1405d9cfedb ("tools: relocate-rela: Check that relocation works only for EM_AARCH64")
> Signed-off-by: Samuel Holland <samuel at sholland.org>
> ---
>
> tools/relocate-rela.c | 125 +++++++++++++++++++++++-------------------
> 1 file changed, 68 insertions(+), 57 deletions(-)
>
> diff --git a/tools/relocate-rela.c b/tools/relocate-rela.c
> index 090fb1acb20c..fcf3fb201f07 100644
> --- a/tools/relocate-rela.c
> +++ b/tools/relocate-rela.c
> @@ -60,7 +60,9 @@ 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;
> + uint64_t section_header_base, section_header_size;
> + uint64_t sh_addr, sh_offset, sh_size;
> + Elf64_Half sh_index, sh_num;
> Elf64_Shdr *sh_table; /* Elf symbol table */
> int ret, i, machine;
> char *sh_str;
> @@ -76,7 +78,7 @@ static int decode_elf64(FILE *felf, char **argv)
> return 25;
> }
>
> - machine = header.e_machine;
> + machine = le16_to_cpu(header.e_machine);
> debug("Machine\t%d\n", machine);
>
> if (machine != EM_AARCH64) {
> @@ -84,9 +86,10 @@ static int decode_elf64(FILE *felf, char **argv)
> return 30;
> }
>
> - text_base = header.e_entry;
> - section_header_base = header.e_shoff;
> - section_header_size = header.e_shentsize * header.e_shnum;
> + text_base = le64_to_cpu(header.e_entry);
> + section_header_base = le64_to_cpu(header.e_shoff);
> + section_header_size = le16_to_cpu(header.e_shentsize) *
> + le16_to_cpu(header.e_shnum);
>
> sh_table = malloc(section_header_size);
> if (!sh_table) {
> @@ -114,9 +117,9 @@ static int decode_elf64(FILE *felf, char **argv)
> 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_index = le16_to_cpu(header.e_shstrndx);
> + sh_size = le64_to_cpu(sh_table[sh_index].sh_size);
> + debug("e_shstrndx %x, sh_size %lx\n", sh_index, sh_size);
>
> sh_str = malloc(sh_size);
> if (!sh_str) {
> @@ -130,9 +133,8 @@ static int decode_elf64(FILE *felf, char **argv)
> * 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);
> + sh_offset = le64_to_cpu(sh_table[sh_index].sh_offset);
> + sh_num = le16_to_cpu(header.e_shnum);
>
> ret = fseek(felf, sh_offset, SEEK_SET);
> if (ret) {
> @@ -153,18 +155,22 @@ static int decode_elf64(FILE *felf, char **argv)
> 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))) {
> + for (i = 0; i < sh_num; i++) {
> + char *sh_name = sh_str + le32_to_cpu(sh_table[i].sh_name);
> +
> + debug("%s\n", sh_name);
> +
> + sh_addr = le64_to_cpu(sh_table[i].sh_addr);
> + sh_offset = le64_to_cpu(sh_table[i].sh_offset);
> + sh_size = le64_to_cpu(sh_table[i].sh_size);
nit: We could move this below sh_name checking but that's fine.
> +
> + if (!strcmp(".rela.dyn", 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;
> + debug(" at addr\t0x%08x\n", sh_addr);
> + debug(" at offset\t0x%08x\n", sh_offset);
> + debug(" of size\t0x%08x\n", sh_size);
> + rela_start = sh_addr;
> + rela_end = rela_start + sh_size;
> break;
> }
> }
> @@ -188,7 +194,9 @@ static int decode_elf32(FILE *felf, char **argv)
> {
> size_t size;
> Elf32_Ehdr header;
> - uint64_t section_header_base, section_header_size, sh_offset, sh_size;
> + uint64_t section_header_base, section_header_size;
> + uint32_t sh_addr, sh_offset, sh_size;
> + Elf32_Half sh_index, sh_num;
> Elf32_Shdr *sh_table; /* Elf symbol table */
> int ret, i, machine;
> char *sh_str;
> @@ -204,7 +212,7 @@ static int decode_elf32(FILE *felf, char **argv)
> return 25;
> }
>
> - machine = header.e_machine;
> + machine = le16_to_cpu(header.e_machine);
> debug("Machine %d\n", machine);
>
> if (machine != EM_MICROBLAZE) {
> @@ -212,14 +220,10 @@ static int decode_elf32(FILE *felf, char **argv)
> return 30;
> }
>
> - text_base = header.e_entry;
> - section_header_base = header.e_shoff;
> -
> - debug("Section header base %x\n", section_header_base);
> -
> - section_header_size = header.e_shentsize * header.e_shnum;
> -
> - debug("Section header size %d\n", section_header_size);
> + text_base = le32_to_cpu(header.e_entry);
> + section_header_base = le32_to_cpu(header.e_shoff);
> + section_header_size = le16_to_cpu(header.e_shentsize) *
> + le16_to_cpu(header.e_shnum);
>
> sh_table = malloc(section_header_size);
> if (!sh_table) {
> @@ -247,8 +251,9 @@ static int decode_elf32(FILE *felf, char **argv)
> return 27;
> }
>
> - sh_size = sh_table[header.e_shstrndx].sh_size;
> - debug("e_shstrndx %x, sh_size %lx\n", header.e_shstrndx, sh_size);
> + sh_index = le16_to_cpu(header.e_shstrndx);
> + sh_size = le32_to_cpu(sh_table[sh_index].sh_size);
> + debug("e_shstrndx %x, sh_size %lx\n", sh_index, sh_size);
>
> sh_str = malloc(sh_size);
> if (!sh_str) {
> @@ -262,9 +267,8 @@ static int decode_elf32(FILE *felf, char **argv)
> * 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 %x\n", header.e_shnum);
> + sh_offset = le32_to_cpu(sh_table[sh_index].sh_offset);
> + sh_num = le16_to_cpu(header.e_shnum);
>
> ret = fseek(felf, sh_offset, SEEK_SET);
> if (ret) {
> @@ -277,7 +281,7 @@ static int decode_elf32(FILE *felf, char **argv)
>
> size = fread(sh_str, 1, sh_size, felf);
> if (size != sh_size) {
> - fprintf(stderr, "%s: Can't read section: %lx/%lx\n",
> + fprintf(stderr, "%s: Can't read section: %lx/%x\n",
> argv[0], size, sh_size);
> free(sh_str);
> free(sh_table);
> @@ -285,22 +289,29 @@ static int decode_elf32(FILE *felf, char **argv)
> return 30;
> }
>
> - for (i = 0; i < header.e_shnum; i++) {
> - debug("%s\n", sh_str + sh_table[i].sh_name);
> - if (!strcmp(".rela.dyn", (sh_str + sh_table[i].sh_name))) {
> + for (i = 0; i < sh_num; i++) {
> + char *sh_name = sh_str + le32_to_cpu(sh_table[i].sh_name);
> +
> + debug("%s\n", sh_name);
> +
> + sh_addr = le64_to_cpu(sh_table[i].sh_addr);
> + sh_offset = le64_to_cpu(sh_table[i].sh_offset);
> + sh_size = le64_to_cpu(sh_table[i].sh_size);
> +
> + if (!strcmp(".rela.dyn", 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;
> + debug(" at addr\t0x%08x\n", sh_addr);
> + debug(" at offset\t0x%08x\n", sh_offset);
> + debug(" of size\t0x%08x\n", sh_size);
> + rela_start = sh_addr;
> + rela_end = rela_start + sh_size;
> }
> - if (!strcmp(".dynsym", (sh_str + sh_table[i].sh_name))) {
> + if (!strcmp(".dynsym", sh_name)) {
> debug("Found section\t\".dynsym\"\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);
> - dyn_start = sh_table[i].sh_addr;
> + debug(" at addr\t0x%08x\n", sh_addr);
> + debug(" at offset\t0x%08x\n", sh_offset);
> + debug(" of size\t0x%08x\n", sh_size);
> + dyn_start = sh_addr;
> }
> }
>
> @@ -386,9 +397,9 @@ static int rela_elf64(char **argv, FILE *f)
> 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);
> + swrela.r_offset = le64_to_cpu(rela.r_offset);
> + swrela.r_info = le64_to_cpu(rela.r_info);
> + swrela.r_addend = le64_to_cpu(rela.r_addend);
>
> if (!supported_rela(&swrela))
> continue;
> @@ -487,9 +498,9 @@ static int rela_elf32(char **argv, FILE *f)
> PRIu32 " r_addend:\t%" PRIx32 "\n",
> rela.r_offset, rela.r_info, rela.r_addend);
>
> - swrela.r_offset = cpu_to_le32(rela.r_offset);
> - swrela.r_info = cpu_to_le32(rela.r_info);
> - swrela.r_addend = cpu_to_le32(rela.r_addend);
> + swrela.r_offset = le32_to_cpu(rela.r_offset);
> + swrela.r_info = le32_to_cpu(rela.r_info);
> + swrela.r_addend = le32_to_cpu(rela.r_addend);
>
> debug("SWRela:\toffset:\t%" PRIx32 " r_info:\t%"
> PRIu32 " r_addend:\t%" PRIx32 "\n",
I have tested it on arm64 and also microblaze and it works fine.
Applied.
M
More information about the U-Boot
mailing list