nuttx/tools/nxflat/nxflat_elf.c
Marco Casaroli 31acf10057 tools/nxflat: Split the ELF reader out of mknxflat.
The converter that follows reads the same objects as mknxflat, so the reader
goes into a file of its own before it gains a second user.  nxflat_elf.c
normalises the tables that describe the object -- the headers, the symbols,
the relocation entries -- into the host's order, and leaves the section
contents alone, because those are the target's bytes and the tools write them
out again.  A tool reads a field without knowing whose order it arrived in.

The -d option goes with it.  It chose a dynamic symbol table, which the ld -r
object these tools convert does not have, and which the NXFLAT loader cannot
use anyway: imports reach a module through the array mknxflat generates.

The output is unchanged.  The thunk mknxflat generates for each of the eleven
modules of apps/examples/nxflat/tests is byte identical to the one the
previous version generated.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-09-27 11:25:12 -03:00

263 lines
8 KiB
C

/****************************************************************************
* tools/nxflat/nxflat_elf.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Reading the object is the one thing mknxflat and ldnxflat both do, so they
* do it here. The two also used to disagree about byte order: one swapped
* the fields it read, the other refused anything but little endian.
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "nxflat_elf.h"
/****************************************************************************
* Private Data
****************************************************************************/
static bool g_swap; /* The object disagrees with this host */
/****************************************************************************
* Private Functions
****************************************************************************/
static uint16_t swap16(uint16_t v)
{
return g_swap ? (uint16_t)((v >> 8) | (v << 8)) : v;
}
static uint32_t swap32(uint32_t v)
{
if (!g_swap)
{
return v;
}
return ((v >> 24) & 0x000000ff) | ((v >> 8) & 0x0000ff00) |
((v << 8) & 0x00ff0000) | ((v << 24) & 0xff000000);
}
static void elf_fail(const char *program, const char *path, const char *why)
{
fprintf(stderr, "%s: %s: %s\n", program, path, why);
exit(2);
}
/****************************************************************************
* Name: normalise
*
* Description:
* Put every table that describes the object into the host's byte order.
* The section contents are not touched: they belong to the target.
*
****************************************************************************/
static void normalise(struct nxflat_elf_s *elf)
{
size_t i;
size_t j;
elf->ehdr->e_type = swap16(elf->ehdr->e_type);
elf->ehdr->e_machine = swap16(elf->ehdr->e_machine);
elf->ehdr->e_entry = swap32(elf->ehdr->e_entry);
elf->ehdr->e_shoff = swap32(elf->ehdr->e_shoff);
elf->ehdr->e_shentsize = swap16(elf->ehdr->e_shentsize);
elf->ehdr->e_shnum = swap16(elf->ehdr->e_shnum);
elf->ehdr->e_shstrndx = swap16(elf->ehdr->e_shstrndx);
elf->shdr = (struct elf32_shdr_s *)(elf->img + elf->ehdr->e_shoff);
for (i = 0; i < elf->ehdr->e_shnum; i++)
{
struct elf32_shdr_s *s = &elf->shdr[i];
s->sh_name = swap32(s->sh_name);
s->sh_type = swap32(s->sh_type);
s->sh_flags = swap32(s->sh_flags);
s->sh_addr = swap32(s->sh_addr);
s->sh_offset = swap32(s->sh_offset);
s->sh_size = swap32(s->sh_size);
s->sh_link = swap32(s->sh_link);
s->sh_info = swap32(s->sh_info);
s->sh_addralign = swap32(s->sh_addralign);
s->sh_entsize = swap32(s->sh_entsize);
}
/* The symbol and relocation tables describe the object too. Everything
* else is left alone.
*/
for (i = 0; i < elf->ehdr->e_shnum; i++)
{
struct elf32_shdr_s *s = &elf->shdr[i];
if (s->sh_type == SHT_SYMTAB && s->sh_entsize == sizeof(*elf->syms))
{
struct elf32_sym_s *syms =
(struct elf32_sym_s *)(elf->img + s->sh_offset);
for (j = 0; j < s->sh_size / sizeof(*syms); j++)
{
syms[j].st_name = swap32(syms[j].st_name);
syms[j].st_value = swap32(syms[j].st_value);
syms[j].st_size = swap32(syms[j].st_size);
syms[j].st_shndx = swap16(syms[j].st_shndx);
}
}
else if (s->sh_type == SHT_REL &&
s->sh_entsize == sizeof(struct elf32_rel_s))
{
struct elf32_rel_s *rel =
(struct elf32_rel_s *)(elf->img + s->sh_offset);
for (j = 0; j < s->sh_size / sizeof(*rel); j++)
{
rel[j].r_offset = swap32(rel[j].r_offset);
rel[j].r_info = swap32(rel[j].r_info);
}
}
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
void nxflat_elf_read(struct nxflat_elf_s *elf, const char *path,
const char *program)
{
struct stat st;
uint16_t probe = 1;
bool host_le;
int symidx = -1;
size_t i;
int fd;
memset(elf, 0, sizeof(*elf));
fd = open(path, O_RDONLY);
if (fd < 0 || fstat(fd, &st) < 0)
{
elf_fail(program, path, strerror(errno));
}
elf->size = (size_t)st.st_size;
elf->img = malloc(elf->size);
if (elf->img == NULL)
{
elf_fail(program, path, "out of memory");
}
if (read(fd, elf->img, elf->size) != (ssize_t)elf->size)
{
elf_fail(program, path, strerror(errno));
}
close(fd);
if (elf->size < sizeof(struct elf32_ehdr_s) ||
memcmp(elf->img, "\177ELF", 4) != 0)
{
elf_fail(program, path, "not an ELF file");
}
elf->ehdr = (struct elf32_ehdr_s *)elf->img;
if (elf->ehdr->e_ident[4] != ELFCLASS32)
{
elf_fail(program, path, "not a 32-bit ELF file");
}
if (elf->ehdr->e_ident[5] != ELFDATA2LSB &&
elf->ehdr->e_ident[5] != ELFDATA2MSB)
{
elf_fail(program, path, "has no byte order");
}
elf->littleendian = elf->ehdr->e_ident[5] == ELFDATA2LSB;
host_le = *(const unsigned char *)&probe != 0;
g_swap = host_le != elf->littleendian;
normalise(elf);
if (elf->ehdr->e_shnum == 0 ||
elf->ehdr->e_shentsize != sizeof(struct elf32_shdr_s))
{
elf_fail(program, path, "has no usable section header table");
}
elf->shstrtab = (const char *)
(elf->img + elf->shdr[elf->ehdr->e_shstrndx].sh_offset);
/* The static symbol table. There is no dynamic one: ld -r does not make
* .dynsym, and NXFLAT has no use for it -- imports reach a module through
* the array mknxflat generates.
*/
for (i = 0; i < elf->ehdr->e_shnum; i++)
{
if (elf->shdr[i].sh_type == SHT_SYMTAB)
{
symidx = (int)i;
break;
}
}
if (symidx < 0)
{
elf_fail(program, path, "has no symbol table");
}
if (elf->shdr[symidx].sh_entsize != sizeof(struct elf32_sym_s))
{
elf_fail(program, path, "has symbols of an unexpected size");
}
if (elf->shdr[symidx].sh_link >= elf->ehdr->e_shnum)
{
elf_fail(program, path, "has a symbol table with no names");
}
elf->syms = (struct elf32_sym_s *)
(elf->img + elf->shdr[symidx].sh_offset);
elf->nsyms = elf->shdr[symidx].sh_size / sizeof(struct elf32_sym_s);
elf->strtab = (const char *)
(elf->img + elf->shdr[elf->shdr[symidx].sh_link].sh_offset);
}
const char *nxflat_elf_symname(const struct nxflat_elf_s *elf,
const struct elf32_sym_s *sym)
{
return sym->st_name != 0 ? elf->strtab + sym->st_name : "";
}