2015-12-10 09:53:31 -06:00
|
|
|
/****************************************************************************
|
2025-04-10 09:51:25 +08:00
|
|
|
* libs/libc/elf/elf_bind.c
|
2015-12-10 09:53:31 -06:00
|
|
|
*
|
2024-09-25 14:05:00 +02:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*
|
2020-04-14 00:03:46 +09:00
|
|
|
* 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
|
2015-12-10 09:53:31 -06:00
|
|
|
*
|
2020-04-14 00:03:46 +09:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2015-12-10 09:53:31 -06:00
|
|
|
*
|
2020-04-14 00:03:46 +09:00
|
|
|
* 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.
|
2015-12-10 09:53:31 -06:00
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
|
* Included Files
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <assert.h>
|
2026-04-01 05:11:15 +05:30
|
|
|
#include <nuttx/debug.h>
|
2015-12-10 09:53:31 -06:00
|
|
|
|
2025-08-14 14:33:03 -03:00
|
|
|
#include <nuttx/arch.h>
|
2024-10-16 10:49:33 +08:00
|
|
|
#include <nuttx/cache.h>
|
2019-01-26 11:18:45 -06:00
|
|
|
#include <nuttx/elf.h>
|
libs/libc/elf: Fix two ways an FDPIC module failed to relocate.
Running one for the first time turned up two holes in the ET_DYN path.
Neither shows up in a build.
An undefined symbol is resolved with libelf_findglobal(), which searches
only the table of globally registered symbols. The export table that
exec() hands its caller went no further than the ET_REL path, so an
ET_DYN module could not import anything the caller supplied. Invisible
while such modules resolved everything internally; an FDPIC module
imports its libc, and every import failed with "Unable to resolve addr of
ext ref printf" although the caller had passed a table containing printf.
The export table is now threaded into libelf_relocatedyn() and consulted
when the global table has no answer, leaving the existing lookup order
intact.
A relocation naming a symbol defined inside the object was dropped
silently. The code handles a relocation with no symbol, and one against
an undefined symbol, but a defined symbol fell through both. That was
harmless while every dynamic relocation arriving here had symbol index
zero, which is the case for R_ARM_RELATIVE. FDPIC brings the first ones
that do not: a pointer to a static function is emitted against the
*section* symbol, so the value is the section base and the offset within
it -- including the Thumb bit -- is carried as the addend. Deriving a
value from the word being patched, as the no-symbol case does, would
translate that addend as though it were an address. Confirmed against a
real module: .text at 0x23c plus an addend of 0x95 gives 0x2d1, which is
the function with its Thumb bit.
Also stop libelf_symname() reporting a nameless symbol as an error. A
section symbol has no name, and libelf_findsymbol() walks the whole table
looking for optional entries such as nx_stacksize, so it meets these
routinely and checks for -ESRCH itself. At error level it printed ten or
more lines per module load and buried the diagnostics that matter.
Built and run on lm3s6965-ek with the examples/elf ROMFS. The ET_REL
test modules load as before, and an FDPIC module now loads, relocates,
resolves printf and puts from the table exec() supplied, and calls
through a function descriptor of its own.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 11:28:49 +02:00
|
|
|
#include <nuttx/symtab.h>
|
2025-04-10 09:51:25 +08:00
|
|
|
#include <nuttx/lib/elf.h>
|
2015-12-10 09:53:31 -06:00
|
|
|
|
2019-03-19 08:57:13 -06:00
|
|
|
#include "libc.h"
|
2025-04-10 09:51:25 +08:00
|
|
|
#include "elf/elf.h"
|
2017-01-29 11:17:29 -06:00
|
|
|
|
2022-09-26 16:22:03 +10:00
|
|
|
/****************************************************************************
|
|
|
|
|
* Pre-processor Definitions
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define I_REL 0 /* Index into relxxx[] arrays for relocations */
|
|
|
|
|
#define I_PLT 1 /* ... for PLTs */
|
|
|
|
|
#define N_RELS 2 /* Number of relxxx[] indexes */
|
|
|
|
|
|
riscv/arch_elf.c: Handle PCREL_HI20/LO12_I/S relocations correctly
There is a problem with the current elf loader for risc-v: when a pair of
PCREL_HI20 / LO12 relocations are encountered, it is assumed that these
will follow each other immediately, as follows:
label:
auipc a0, %pcrel_hi(symbol) // R_RISCV_PCREL_HI20
load/store a0, %pcrel_lo(label)(a0) // R_RISCV_PCREL_LO12_I/S
With this assumption, the hi/lo relocations are both done when a hi20
relocation entry is encountered, first to the current instruction (addr)
and to the next instruction (addr + 4).
However, this assumption is wrong. There is nothing in the elf relocation
specification[1] that mandates this. Thus, the hi/lo relocation always
needs to first fixup the hi-part, and when the lo-part is encountered, it
needs to find the corresponding hi relocation entry, via the given "label".
This necessitates (re-)visiting the relocation entries for the current
section as well as looking for "label" in the symbol table.
The NuttX elf loader does not allow such operations to be done in the
machine specific part, so this patch fixes the relocation issue by
introducing an architecture specific cache for the hi20 relocation and
symbol table entries. When a lo12 relocation is encountered, the cache
can be consulted to find the hi20 part.
[1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
2023-12-05 12:30:46 +02:00
|
|
|
#ifdef ARCH_ELFDATA
|
|
|
|
|
# define ARCH_ELFDATA_DEF arch_elfdata_t arch_data; \
|
|
|
|
|
memset(&arch_data, 0, sizeof(arch_elfdata_t))
|
|
|
|
|
# define ARCH_ELFDATA_PARM &arch_data
|
|
|
|
|
#else
|
|
|
|
|
# define ARCH_ELFDATA_DEF
|
|
|
|
|
# define ARCH_ELFDATA_PARM NULL
|
|
|
|
|
#endif
|
|
|
|
|
|
libs/libc/machine/arm: Relocate FDPIC function descriptors.
A function pointer under FDPIC is not a code address. Because each
PT_LOAD segment is placed independently, a pointer has to carry the data
base its callee will need, so it is a two-word descriptor: the entry
point, and the base to install in the PIC register before branching.
R_ARM_FUNCDESC_VALUE says "the thing you are patching is such a
descriptor", and R_ARM_FUNCDESC says "manufacture one and give me its
address".
Both need state a relocation cannot carry. A descriptor's second word is
the *object's* data base, from DT_PLTGOT, and R_ARM_FUNCDESC carves
descriptors from a pool whose cursor has to survive from one relocation
to the next. up_relocate() is handed only a relocation, a resolved
symbol and an address to patch.
arch_data is the existing channel for exactly this -- RISC-V already uses
it to remember a HI20 relocation while its LO12 partner is processed --
but nothing has ever put loader state into it: it is declared zeroed and
written only by up_relocate() itself. So ARCH_ELFDATA_INIT and
ARCH_ELFDATA_FINI are added, seeding the block from the loadinfo before
the relocation loop and reading the cursor back after. Both default to
nothing, so an architecture that does not define them is unaffected, and
RISC-V's use of arch_data is untouched. libelf_relocatedyn() walks both
dynamic tables under one arch_data, so the cursor spans the whole object.
The addend handling is the part that is easy to get wrong. REL format
keeps the addend in place, in the word about to become the entry point,
and a pointer to a static function is referenced through its *section*
symbol -- the value is the section base and the offset, including the
Thumb bit, is entirely in the addend. Dropping it yields an even address
and the core faults trying to execute it as ARM code.
The GOT written into a descriptor is the loading object's own, even for
an imported function, which is what makes a callback work: when the base
firmware's qsort() calls back into a module's comparison function, the
module needs its own data base in the PIC register.
libelf_relocatedyn()'s imported-symbol path needed a change to suit. It
stores the resolved address directly and never calls up_relocate(), which
cannot produce a two-word descriptor, so under FDPIC the resolved value
now goes through up_relocate() and the relocation type decides what to
write.
Implemented for armv7-m and armv8-m, the profiles FDPIC targets; the
other ARM variants gain the arch_data block but no new relocations.
Built and booted mps3-an547:picostest and lm3s6965-ek:qemu-nxflat, the
ELF PIC and NXFLAT users of this code, both unchanged.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 08:58:41 +02:00
|
|
|
/* Move loader state in and out of the arch_data block, and say which
|
|
|
|
|
* relocation table is being walked. Nothing for an architecture whose
|
|
|
|
|
* relocations do not need any of it.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#if defined(ARCH_ELFDATA) && defined(ARCH_ELFDATA_SET_PLTREL)
|
|
|
|
|
# define ARCH_ELFDATA_PLTREL(v) ARCH_ELFDATA_SET_PLTREL(&arch_data, v)
|
|
|
|
|
#else
|
|
|
|
|
# define ARCH_ELFDATA_PLTREL(v)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(ARCH_ELFDATA) && defined(ARCH_ELFDATA_INIT)
|
|
|
|
|
# define ARCH_ELFDATA_SETUP(l) ARCH_ELFDATA_INIT(&arch_data, l)
|
|
|
|
|
# define ARCH_ELFDATA_TEARDOWN(l) ARCH_ELFDATA_FINI(&arch_data, l)
|
|
|
|
|
#else
|
|
|
|
|
# define ARCH_ELFDATA_SETUP(l)
|
|
|
|
|
# define ARCH_ELFDATA_TEARDOWN(l)
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-03-19 09:13:50 -06:00
|
|
|
/****************************************************************************
|
|
|
|
|
* Private Types
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/* REVISIT: This naming breaks the NuttX coding standard, but is consistent
|
2020-02-07 17:10:23 -06:00
|
|
|
* with legacy naming of other ELF types.
|
2019-03-19 09:13:50 -06:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
2023-09-15 21:59:06 +03:00
|
|
|
dq_entry_t entry;
|
|
|
|
|
Elf_Sym sym;
|
|
|
|
|
int idx;
|
2020-02-07 17:10:23 -06:00
|
|
|
} Elf_SymCache;
|
2019-03-19 09:13:50 -06:00
|
|
|
|
2022-09-26 16:22:03 +10:00
|
|
|
struct
|
|
|
|
|
{
|
2023-07-24 21:06:59 -03:00
|
|
|
int stroff; /* offset to string table */
|
|
|
|
|
int symoff; /* offset to symbol table */
|
|
|
|
|
int lsymtab; /* size of symbol table */
|
2023-09-04 10:37:48 +10:00
|
|
|
int relentsz[2]; /* size of relocation entry */
|
2023-07-24 21:06:59 -03:00
|
|
|
int reloff[2]; /* offset to the relocation section */
|
|
|
|
|
int relsz[2]; /* size of relocation table */
|
2023-09-04 10:37:48 +10:00
|
|
|
int relrela[2]; /* type of relocation type - 0: DT_REL / 1: DT_RELA */
|
2023-07-24 21:06:59 -03:00
|
|
|
} reldata;
|
2022-09-26 16:22:03 +10:00
|
|
|
|
2015-12-10 09:53:31 -06:00
|
|
|
/****************************************************************************
|
|
|
|
|
* Private Functions
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
2025-04-10 09:51:25 +08:00
|
|
|
* Name: libelf_readrels
|
2015-12-10 09:53:31 -06:00
|
|
|
*
|
|
|
|
|
* Description:
|
2020-02-07 17:10:23 -06:00
|
|
|
* Read the (ELF_Rel structure * buffer count) into memory.
|
2015-12-10 09:53:31 -06:00
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
static inline int libelf_readrels(FAR struct mod_loadinfo_s *loadinfo,
|
2020-02-07 17:10:23 -06:00
|
|
|
FAR const Elf_Shdr *relsec,
|
|
|
|
|
int index, FAR Elf_Rel *rels,
|
2019-03-19 09:13:50 -06:00
|
|
|
int count)
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
|
|
|
|
off_t offset;
|
2019-03-19 08:57:13 -06:00
|
|
|
int size;
|
2015-12-10 09:53:31 -06:00
|
|
|
|
|
|
|
|
/* Verify that the symbol table index lies within symbol table */
|
|
|
|
|
|
2020-02-07 17:10:23 -06:00
|
|
|
if (index < 0 || index > (relsec->sh_size / sizeof(Elf_Rel)))
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
2018-06-01 10:10:17 -06:00
|
|
|
berr("ERROR: Bad relocation symbol index: %d\n", index);
|
2015-12-10 09:53:31 -06:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the file offset to the symbol table entry */
|
|
|
|
|
|
2020-02-07 17:10:23 -06:00
|
|
|
offset = sizeof(Elf_Rel) * index;
|
|
|
|
|
size = sizeof(Elf_Rel) * count;
|
2019-03-19 08:57:13 -06:00
|
|
|
if (offset + size > relsec->sh_size)
|
|
|
|
|
{
|
|
|
|
|
size = relsec->sh_size - offset;
|
|
|
|
|
}
|
2015-12-10 09:53:31 -06:00
|
|
|
|
|
|
|
|
/* And, finally, read the symbol table entry into memory */
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
return libelf_read(loadinfo, (FAR uint8_t *)rels, size,
|
2019-03-19 08:57:13 -06:00
|
|
|
relsec->sh_offset + offset);
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
|
|
|
|
|
2020-02-07 17:10:23 -06:00
|
|
|
/****************************************************************************
|
2025-04-10 09:51:25 +08:00
|
|
|
* Name: libelf_readrelas
|
2020-02-07 17:10:23 -06:00
|
|
|
*
|
|
|
|
|
* Description:
|
|
|
|
|
* Read the (ELF_Rela structure * buffer count) into memory.
|
|
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
static inline int libelf_readrelas(FAR struct mod_loadinfo_s *loadinfo,
|
2020-02-07 17:10:23 -06:00
|
|
|
FAR const Elf_Shdr *relsec,
|
|
|
|
|
int index, FAR Elf_Rela *relas,
|
|
|
|
|
int count)
|
|
|
|
|
{
|
|
|
|
|
off_t offset;
|
|
|
|
|
int size;
|
|
|
|
|
|
|
|
|
|
/* Verify that the symbol table index lies within symbol table */
|
|
|
|
|
|
|
|
|
|
if (index < 0 || index > (relsec->sh_size / sizeof(Elf_Rela)))
|
|
|
|
|
{
|
|
|
|
|
berr("ERROR: Bad relocation symbol index: %d\n", index);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the file offset to the symbol table entry */
|
|
|
|
|
|
|
|
|
|
offset = sizeof(Elf_Rela) * index;
|
|
|
|
|
size = sizeof(Elf_Rela) * count;
|
|
|
|
|
if (offset + size > relsec->sh_size)
|
|
|
|
|
{
|
|
|
|
|
size = relsec->sh_size - offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* And, finally, read the symbol table entry into memory */
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
return libelf_read(loadinfo, (FAR uint8_t *)relas, size,
|
2020-02-07 17:10:23 -06:00
|
|
|
relsec->sh_offset + offset);
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-10 09:53:31 -06:00
|
|
|
/****************************************************************************
|
2025-04-10 09:51:25 +08:00
|
|
|
* Name: libelf_relocate and libelf_relocateadd
|
2015-12-10 09:53:31 -06:00
|
|
|
*
|
|
|
|
|
* Description:
|
|
|
|
|
* Perform all relocations associated with a section.
|
|
|
|
|
*
|
|
|
|
|
* Returned Value:
|
|
|
|
|
* 0 (OK) is returned on success and a negated errno is returned on
|
|
|
|
|
* failure.
|
|
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
static int libelf_relocate(FAR struct module_s *modp,
|
2024-07-09 23:27:59 +08:00
|
|
|
FAR struct mod_loadinfo_s *loadinfo, int relidx,
|
|
|
|
|
FAR const struct symtab_s *exports, int nexports)
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
2023-09-15 21:59:06 +03:00
|
|
|
FAR Elf_Shdr *relsec = &loadinfo->shdr[relidx];
|
|
|
|
|
FAR Elf_Shdr *dstsec = &loadinfo->shdr[relsec->sh_info];
|
|
|
|
|
FAR Elf_Rel *rels;
|
|
|
|
|
FAR Elf_Rel *rel;
|
2020-02-07 17:10:23 -06:00
|
|
|
FAR Elf_SymCache *cache;
|
2023-09-15 21:59:06 +03:00
|
|
|
FAR Elf_Sym *sym;
|
|
|
|
|
FAR dq_entry_t *e;
|
|
|
|
|
dq_queue_t q;
|
|
|
|
|
uintptr_t addr;
|
|
|
|
|
int symidx;
|
|
|
|
|
int ret = OK;
|
|
|
|
|
int i;
|
|
|
|
|
int j;
|
2015-12-10 09:53:31 -06:00
|
|
|
|
riscv/arch_elf.c: Handle PCREL_HI20/LO12_I/S relocations correctly
There is a problem with the current elf loader for risc-v: when a pair of
PCREL_HI20 / LO12 relocations are encountered, it is assumed that these
will follow each other immediately, as follows:
label:
auipc a0, %pcrel_hi(symbol) // R_RISCV_PCREL_HI20
load/store a0, %pcrel_lo(label)(a0) // R_RISCV_PCREL_LO12_I/S
With this assumption, the hi/lo relocations are both done when a hi20
relocation entry is encountered, first to the current instruction (addr)
and to the next instruction (addr + 4).
However, this assumption is wrong. There is nothing in the elf relocation
specification[1] that mandates this. Thus, the hi/lo relocation always
needs to first fixup the hi-part, and when the lo-part is encountered, it
needs to find the corresponding hi relocation entry, via the given "label".
This necessitates (re-)visiting the relocation entries for the current
section as well as looking for "label" in the symbol table.
The NuttX elf loader does not allow such operations to be done in the
machine specific part, so this patch fixes the relocation issue by
introducing an architecture specific cache for the hi20 relocation and
symbol table entries. When a lo12 relocation is encountered, the cache
can be consulted to find the hi20 part.
[1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
2023-12-05 12:30:46 +02:00
|
|
|
/* Define potential architecture specific elf data container */
|
|
|
|
|
|
|
|
|
|
ARCH_ELFDATA_DEF;
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
rels = lib_malloc(CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT *
|
|
|
|
|
sizeof(Elf_Rel));
|
2019-03-19 08:57:13 -06:00
|
|
|
if (!rels)
|
|
|
|
|
{
|
|
|
|
|
berr("Failed to allocate memory for elf relocation rels\n");
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-19 09:13:50 -06:00
|
|
|
dq_init(&q);
|
|
|
|
|
|
2015-12-10 09:53:31 -06:00
|
|
|
/* Examine each relocation in the section. 'relsec' is the section
|
|
|
|
|
* containing the relations. 'dstsec' is the section containing the data
|
|
|
|
|
* to be relocated.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-02-07 17:10:23 -06:00
|
|
|
for (i = j = 0; i < relsec->sh_size / sizeof(Elf_Rel); i++)
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
|
|
|
|
/* Read the relocation entry into memory */
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
rel = &rels[i % CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT];
|
2019-03-19 08:57:13 -06:00
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
if (!(i % CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT))
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_readrels(loadinfo, relsec, i, rels,
|
|
|
|
|
CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT);
|
2019-03-19 08:57:13 -06:00
|
|
|
if (ret < 0)
|
2020-03-10 14:52:58 +09:00
|
|
|
{
|
|
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Failed to read relocation entry: %d\n",
|
2019-03-19 08:57:13 -06:00
|
|
|
relidx, i, ret);
|
|
|
|
|
break;
|
2020-03-10 14:52:58 +09:00
|
|
|
}
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the symbol table index for the relocation. This is contained
|
|
|
|
|
* in a bit-field within the r_info element.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-02-07 17:10:23 -06:00
|
|
|
symidx = ELF_R_SYM(rel->r_info);
|
2015-12-10 09:53:31 -06:00
|
|
|
|
2019-03-19 09:13:50 -06:00
|
|
|
/* First try the cache */
|
2015-12-10 09:53:31 -06:00
|
|
|
|
2019-03-19 09:13:50 -06:00
|
|
|
sym = NULL;
|
|
|
|
|
for (e = dq_peek(&q); e; e = dq_next(e))
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
2020-02-07 17:10:23 -06:00
|
|
|
cache = (FAR Elf_SymCache *)e;
|
2019-03-19 09:13:50 -06:00
|
|
|
if (cache->idx == symidx)
|
|
|
|
|
{
|
|
|
|
|
dq_rem(&cache->entry, &q);
|
|
|
|
|
dq_addfirst(&cache->entry, &q);
|
|
|
|
|
sym = &cache->sym;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
|
|
|
|
|
2019-03-19 09:13:50 -06:00
|
|
|
/* If the symbol was not found in the cache, we will need to read the
|
|
|
|
|
* symbol from the file.
|
|
|
|
|
*/
|
2015-12-10 09:53:31 -06:00
|
|
|
|
2019-03-19 09:13:50 -06:00
|
|
|
if (sym == NULL)
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
2025-04-10 09:51:25 +08:00
|
|
|
if (j < CONFIG_LIBC_ELF_SYMBOL_CACHECOUNT)
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
2020-02-07 17:10:23 -06:00
|
|
|
cache = lib_malloc(sizeof(Elf_SymCache));
|
2019-03-19 09:13:50 -06:00
|
|
|
if (!cache)
|
|
|
|
|
{
|
|
|
|
|
berr("Failed to allocate memory for elf symbols\n");
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-03-10 14:52:58 +09:00
|
|
|
|
2019-03-19 09:13:50 -06:00
|
|
|
j++;
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-02-07 17:10:23 -06:00
|
|
|
cache = (FAR Elf_SymCache *)dq_remlast(&q);
|
2019-03-19 09:13:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sym = &cache->sym;
|
|
|
|
|
|
|
|
|
|
/* Read the symbol table entry into memory */
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_readsym(loadinfo, symidx, sym,
|
2023-07-23 19:45:16 -03:00
|
|
|
&loadinfo->shdr[loadinfo->symtabidx]);
|
2019-03-19 09:13:50 -06:00
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2020-03-10 14:52:58 +09:00
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Failed to read symbol[%d]: %d\n",
|
2019-03-19 09:13:50 -06:00
|
|
|
relidx, i, symidx, ret);
|
|
|
|
|
lib_free(cache);
|
2019-03-19 08:57:13 -06:00
|
|
|
break;
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
2019-03-19 09:13:50 -06:00
|
|
|
|
|
|
|
|
/* Get the value of the symbol (in sym.st_value) */
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_symvalue(modp, loadinfo, sym,
|
2024-07-03 15:33:48 +08:00
|
|
|
loadinfo->shdr[loadinfo->strtabidx].sh_offset,
|
|
|
|
|
exports, nexports);
|
2019-03-19 09:13:50 -06:00
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2020-03-10 14:52:58 +09:00
|
|
|
/* The special error -ESRCH is returned only in one condition:
|
|
|
|
|
* The symbol has no name.
|
2019-03-19 09:13:50 -06:00
|
|
|
*
|
|
|
|
|
* There are a few relocations for a few architectures that do
|
|
|
|
|
* no depend upon a named symbol. We don't know if that is the
|
|
|
|
|
* case here, but we will use a NULL symbol pointer to indicate
|
|
|
|
|
* that case to up_relocate(). That function can then do what
|
|
|
|
|
* is best.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (ret == -ESRCH)
|
|
|
|
|
{
|
2020-03-10 14:52:58 +09:00
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Undefined symbol[%d] has no name: %d\n",
|
|
|
|
|
relidx, i, symidx, ret);
|
2019-03-19 09:13:50 -06:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-10 14:52:58 +09:00
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Failed to get value of symbol[%d]: %d\n",
|
|
|
|
|
relidx, i, symidx, ret);
|
2019-03-19 09:13:50 -06:00
|
|
|
lib_free(cache);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cache->idx = symidx;
|
|
|
|
|
dq_addfirst(&cache->entry, &q);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sym->st_shndx == SHN_UNDEF && sym->st_name == 0)
|
|
|
|
|
{
|
|
|
|
|
sym = NULL;
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Calculate the relocation address. */
|
|
|
|
|
|
libs/libc/elf, binfmt: Describe the GOT by base and size, not by index.
gotindex named the .got section header, and every user then reached through
shdr[] for what it actually wanted. Only one of the five wanted the index.
gotbase and gotsize say it directly. gotsize is the extent of .got and is
also what says the object has one, and gotbase is where the GOT ended up:
the placed address of .got for an ordinary object, or DT_PLTGOT for an FDPIC
one, which libelf_bind() already reads. Both are set in libelf_loadfile(),
after the sections are placed, so gotbase is the address the object will be
read at rather than the one it was linked for.
The GOT walk in libelf_loadfile() now runs only when there is a base, which
also keeps it off an FDPIC object. An FDPIC object's sections are never
placed, so .got carried a link time sh_addr there, and the walk read and
wrote through it. Its GOT is relocated through its own relocations.
The check that gates libelf_xipacquire() runs before the load, when neither
field is set, so it looks the section up by name. It hands the index it
found to libelf_loadfile(), which is the only reason that function takes
one: the object is searched once, not twice.
One behaviour changes: a .got that exists but is empty now reads as no GOT.
There is nothing for any of the five users to do with an empty one.
Built for pimoroni-pico-2-plus with CONFIG_PIC, CONFIG_ELF and
CONFIG_LIBC_ELF, and for mps3-an547:bl, which is the board that read the
index. Run on QEMU with mps3-an547:picostest, which loads PIC ELF modules
from a romfs: hello prints, and ostest reaches the timed mutex test, the
same as before the change.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-09-03 01:00:13 +02:00
|
|
|
if (loadinfo->gotsize != 0)
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
2024-07-03 15:33:48 +08:00
|
|
|
if (sym->st_shndx == SHN_UNDEF)
|
|
|
|
|
{
|
|
|
|
|
/* Symbol type is undefined, we need to set the address
|
|
|
|
|
* to the value of the symbol.
|
|
|
|
|
*/
|
|
|
|
|
|
libs/libc/elf, binfmt: Describe the GOT by base and size, not by index.
gotindex named the .got section header, and every user then reached through
shdr[] for what it actually wanted. Only one of the five wanted the index.
gotbase and gotsize say it directly. gotsize is the extent of .got and is
also what says the object has one, and gotbase is where the GOT ended up:
the placed address of .got for an ordinary object, or DT_PLTGOT for an FDPIC
one, which libelf_bind() already reads. Both are set in libelf_loadfile(),
after the sections are placed, so gotbase is the address the object will be
read at rather than the one it was linked for.
The GOT walk in libelf_loadfile() now runs only when there is a base, which
also keeps it off an FDPIC object. An FDPIC object's sections are never
placed, so .got carried a link time sh_addr there, and the walk read and
wrote through it. Its GOT is relocated through its own relocations.
The check that gates libelf_xipacquire() runs before the load, when neither
field is set, so it looks the section up by name. It hands the index it
found to libelf_loadfile(), which is the only reason that function takes
one: the object is searched once, not twice.
One behaviour changes: a .got that exists but is empty now reads as no GOT.
There is nothing for any of the five users to do with an empty one.
Built for pimoroni-pico-2-plus with CONFIG_PIC, CONFIG_ELF and
CONFIG_LIBC_ELF, and for mps3-an547:bl, which is the board that read the
index. Run on QEMU with mps3-an547:picostest, which loads PIC ELF modules
from a romfs: hello prints, and ostest reaches the timed mutex test, the
same as before the change.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-09-03 01:00:13 +02:00
|
|
|
FAR uintptr_t *gotaddr = (FAR uintptr_t *)(loadinfo->gotbase +
|
2024-07-03 15:33:48 +08:00
|
|
|
*((FAR uintptr_t *)(dstsec->sh_addr + rel->r_offset)));
|
|
|
|
|
|
|
|
|
|
*gotaddr = sym->st_value;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((dstsec->sh_flags & SHF_WRITE) == 0)
|
|
|
|
|
{
|
|
|
|
|
/* Skip relocations for read-only sections */
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Use the GOT to store the address */
|
|
|
|
|
|
|
|
|
|
if (rel->r_offset - dstsec->sh_offset >
|
|
|
|
|
dstsec->sh_size)
|
|
|
|
|
{
|
|
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Relocation address out of range, "
|
|
|
|
|
"offset %" PRIuPTR " size %ju\n",
|
|
|
|
|
relidx, i, (uintptr_t)rel->r_offset,
|
|
|
|
|
(uintmax_t)dstsec->sh_size);
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addr = dstsec->sh_addr + rel->r_offset - dstsec->sh_offset;
|
|
|
|
|
if (ELF_ST_TYPE(sym->st_info) == STT_SECTION)
|
|
|
|
|
{
|
|
|
|
|
/* Symbol type is section, we need clear the address
|
|
|
|
|
* and keep the original value.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
*(FAR uintptr_t *)addr -=
|
|
|
|
|
loadinfo->shdr[sym->st_shndx].sh_offset;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Normal symbol, just keep it zero */
|
|
|
|
|
|
|
|
|
|
*(FAR uintptr_t *)addr = 0;
|
|
|
|
|
}
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
2024-07-03 15:33:48 +08:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (rel->r_offset > dstsec->sh_size)
|
|
|
|
|
{
|
|
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Relocation address out of range, "
|
|
|
|
|
"offset %" PRIuPTR " size %ju\n",
|
|
|
|
|
relidx, i, (uintptr_t)rel->r_offset,
|
|
|
|
|
(uintmax_t)dstsec->sh_size);
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-12-10 09:53:31 -06:00
|
|
|
|
2024-07-03 15:33:48 +08:00
|
|
|
addr = dstsec->sh_addr + rel->r_offset;
|
|
|
|
|
}
|
2015-12-10 09:53:31 -06:00
|
|
|
|
|
|
|
|
/* Now perform the architecture-specific relocation */
|
|
|
|
|
|
riscv/arch_elf.c: Handle PCREL_HI20/LO12_I/S relocations correctly
There is a problem with the current elf loader for risc-v: when a pair of
PCREL_HI20 / LO12 relocations are encountered, it is assumed that these
will follow each other immediately, as follows:
label:
auipc a0, %pcrel_hi(symbol) // R_RISCV_PCREL_HI20
load/store a0, %pcrel_lo(label)(a0) // R_RISCV_PCREL_LO12_I/S
With this assumption, the hi/lo relocations are both done when a hi20
relocation entry is encountered, first to the current instruction (addr)
and to the next instruction (addr + 4).
However, this assumption is wrong. There is nothing in the elf relocation
specification[1] that mandates this. Thus, the hi/lo relocation always
needs to first fixup the hi-part, and when the lo-part is encountered, it
needs to find the corresponding hi relocation entry, via the given "label".
This necessitates (re-)visiting the relocation entries for the current
section as well as looking for "label" in the symbol table.
The NuttX elf loader does not allow such operations to be done in the
machine specific part, so this patch fixes the relocation issue by
introducing an architecture specific cache for the hi20 relocation and
symbol table entries. When a lo12 relocation is encountered, the cache
can be consulted to find the hi20 part.
[1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
2023-12-05 12:30:46 +02:00
|
|
|
ret = up_relocate(rel, sym, addr, ARCH_ELFDATA_PARM);
|
2015-12-10 09:53:31 -06:00
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2020-03-10 14:52:58 +09:00
|
|
|
berr("ERROR: Section %d reloc %d: Relocation failed: %d\n",
|
|
|
|
|
relidx, i, ret);
|
2019-03-19 08:57:13 -06:00
|
|
|
break;
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-19 08:57:13 -06:00
|
|
|
lib_free(rels);
|
2023-09-15 21:59:06 +03:00
|
|
|
while ((e = dq_peek(&q)) != NULL)
|
2019-03-19 09:13:50 -06:00
|
|
|
{
|
|
|
|
|
dq_rem(e, &q);
|
|
|
|
|
lib_free(e);
|
|
|
|
|
}
|
2019-03-19 08:57:13 -06:00
|
|
|
|
|
|
|
|
return ret;
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
static int libelf_relocateadd(FAR struct module_s *modp,
|
2023-03-26 12:55:00 +03:00
|
|
|
FAR struct mod_loadinfo_s *loadinfo,
|
2024-07-09 23:27:59 +08:00
|
|
|
int relidx,
|
|
|
|
|
FAR const struct symtab_s *exports,
|
|
|
|
|
int nexports)
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
2023-09-15 21:59:06 +03:00
|
|
|
FAR Elf_Shdr *relsec = &loadinfo->shdr[relidx];
|
|
|
|
|
FAR Elf_Shdr *dstsec = &loadinfo->shdr[relsec->sh_info];
|
|
|
|
|
FAR Elf_Rela *relas;
|
|
|
|
|
FAR Elf_Rela *rela;
|
2020-02-07 17:10:23 -06:00
|
|
|
FAR Elf_SymCache *cache;
|
2023-09-15 21:59:06 +03:00
|
|
|
FAR Elf_Sym *sym;
|
|
|
|
|
FAR dq_entry_t *e;
|
|
|
|
|
dq_queue_t q;
|
|
|
|
|
uintptr_t addr;
|
|
|
|
|
int symidx;
|
|
|
|
|
int ret = OK;
|
|
|
|
|
int i;
|
|
|
|
|
int j;
|
2020-02-07 17:10:23 -06:00
|
|
|
|
riscv/arch_elf.c: Handle PCREL_HI20/LO12_I/S relocations correctly
There is a problem with the current elf loader for risc-v: when a pair of
PCREL_HI20 / LO12 relocations are encountered, it is assumed that these
will follow each other immediately, as follows:
label:
auipc a0, %pcrel_hi(symbol) // R_RISCV_PCREL_HI20
load/store a0, %pcrel_lo(label)(a0) // R_RISCV_PCREL_LO12_I/S
With this assumption, the hi/lo relocations are both done when a hi20
relocation entry is encountered, first to the current instruction (addr)
and to the next instruction (addr + 4).
However, this assumption is wrong. There is nothing in the elf relocation
specification[1] that mandates this. Thus, the hi/lo relocation always
needs to first fixup the hi-part, and when the lo-part is encountered, it
needs to find the corresponding hi relocation entry, via the given "label".
This necessitates (re-)visiting the relocation entries for the current
section as well as looking for "label" in the symbol table.
The NuttX elf loader does not allow such operations to be done in the
machine specific part, so this patch fixes the relocation issue by
introducing an architecture specific cache for the hi20 relocation and
symbol table entries. When a lo12 relocation is encountered, the cache
can be consulted to find the hi20 part.
[1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
2023-12-05 12:30:46 +02:00
|
|
|
/* Define potential architecture specific elf data container */
|
|
|
|
|
|
|
|
|
|
ARCH_ELFDATA_DEF;
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
relas = lib_malloc(CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT *
|
2020-03-10 14:52:58 +09:00
|
|
|
sizeof(Elf_Rela));
|
2020-02-07 17:10:23 -06:00
|
|
|
if (!relas)
|
|
|
|
|
{
|
|
|
|
|
berr("Failed to allocate memory for elf relocation relas\n");
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dq_init(&q);
|
|
|
|
|
|
|
|
|
|
/* Examine each relocation in the section. 'relsec' is the section
|
|
|
|
|
* containing the relations. 'dstsec' is the section containing the data
|
|
|
|
|
* to be relocated.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
for (i = j = 0; i < relsec->sh_size / sizeof(Elf_Rela); i++)
|
|
|
|
|
{
|
|
|
|
|
/* Read the relocation entry into memory */
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
rela = &relas[i % CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT];
|
2020-02-07 17:10:23 -06:00
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
if (!(i % CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT))
|
2020-02-07 17:10:23 -06:00
|
|
|
{
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_readrelas(loadinfo, relsec, i, relas,
|
|
|
|
|
CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT);
|
2020-02-07 17:10:23 -06:00
|
|
|
if (ret < 0)
|
2020-03-10 14:52:58 +09:00
|
|
|
{
|
|
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Failed to read relocation entry: %d\n",
|
2020-02-07 17:10:23 -06:00
|
|
|
relidx, i, ret);
|
|
|
|
|
break;
|
2020-03-10 14:52:58 +09:00
|
|
|
}
|
2020-02-07 17:10:23 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the symbol table index for the relocation. This is contained
|
|
|
|
|
* in a bit-field within the r_info element.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
symidx = ELF_R_SYM(rela->r_info);
|
|
|
|
|
|
|
|
|
|
/* First try the cache */
|
|
|
|
|
|
|
|
|
|
sym = NULL;
|
|
|
|
|
for (e = dq_peek(&q); e; e = dq_next(e))
|
|
|
|
|
{
|
|
|
|
|
cache = (FAR Elf_SymCache *)e;
|
|
|
|
|
if (cache->idx == symidx)
|
|
|
|
|
{
|
|
|
|
|
dq_rem(&cache->entry, &q);
|
|
|
|
|
dq_addfirst(&cache->entry, &q);
|
|
|
|
|
sym = &cache->sym;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the symbol was not found in the cache, we will need to read the
|
|
|
|
|
* symbol from the file.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (sym == NULL)
|
|
|
|
|
{
|
2025-04-10 09:51:25 +08:00
|
|
|
if (j < CONFIG_LIBC_ELF_SYMBOL_CACHECOUNT)
|
2020-02-07 17:10:23 -06:00
|
|
|
{
|
|
|
|
|
cache = lib_malloc(sizeof(Elf_SymCache));
|
|
|
|
|
if (!cache)
|
|
|
|
|
{
|
|
|
|
|
berr("Failed to allocate memory for elf symbols\n");
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-03-10 14:52:58 +09:00
|
|
|
|
2020-02-07 17:10:23 -06:00
|
|
|
j++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cache = (FAR Elf_SymCache *)dq_remlast(&q);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sym = &cache->sym;
|
|
|
|
|
|
|
|
|
|
/* Read the symbol table entry into memory */
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_readsym(loadinfo, symidx, sym,
|
2022-09-26 16:22:03 +10:00
|
|
|
&loadinfo->shdr[loadinfo->symtabidx]);
|
2020-02-07 17:10:23 -06:00
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2020-03-10 14:52:58 +09:00
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Failed to read symbol[%d]: %d\n",
|
2020-02-07 17:10:23 -06:00
|
|
|
relidx, i, symidx, ret);
|
|
|
|
|
lib_free(cache);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the value of the symbol (in sym.st_value) */
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_symvalue(modp, loadinfo, sym,
|
2024-07-09 23:27:59 +08:00
|
|
|
loadinfo->shdr[loadinfo->strtabidx].sh_offset,
|
|
|
|
|
exports, nexports);
|
2020-02-07 17:10:23 -06:00
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2020-03-10 14:52:58 +09:00
|
|
|
/* The special error -ESRCH is returned only in one condition:
|
|
|
|
|
* The symbol has no name.
|
2020-02-07 17:10:23 -06:00
|
|
|
*
|
|
|
|
|
* There are a few relocations for a few architectures that do
|
|
|
|
|
* no depend upon a named symbol. We don't know if that is the
|
|
|
|
|
* case here, but we will use a NULL symbol pointer to indicate
|
|
|
|
|
* that case to up_relocate(). That function can then do what
|
|
|
|
|
* is best.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (ret == -ESRCH)
|
|
|
|
|
{
|
2020-03-10 14:52:58 +09:00
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Undefined symbol[%d] has no name: %d\n",
|
|
|
|
|
relidx, i, symidx, ret);
|
2020-02-07 17:10:23 -06:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-10 14:52:58 +09:00
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Failed to get value of symbol[%d]: %d\n",
|
|
|
|
|
relidx, i, symidx, ret);
|
2020-02-07 17:10:23 -06:00
|
|
|
lib_free(cache);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cache->idx = symidx;
|
|
|
|
|
dq_addfirst(&cache->entry, &q);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sym->st_shndx == SHN_UNDEF && sym->st_name == 0)
|
|
|
|
|
{
|
|
|
|
|
sym = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Calculate the relocation address. */
|
|
|
|
|
|
2024-10-13 17:24:17 +08:00
|
|
|
if (rela->r_offset < 0 ||
|
|
|
|
|
rela->r_offset > dstsec->sh_size)
|
2020-02-07 17:10:23 -06:00
|
|
|
{
|
2020-03-10 14:52:58 +09:00
|
|
|
berr("ERROR: Section %d reloc %d: "
|
2020-11-22 10:05:59 +09:00
|
|
|
"Relocation address out of range, "
|
|
|
|
|
"offset %" PRIuPTR " size %ju\n",
|
|
|
|
|
relidx, i, (uintptr_t)rela->r_offset,
|
|
|
|
|
(uintmax_t)dstsec->sh_size);
|
2020-02-07 17:10:23 -06:00
|
|
|
ret = -EINVAL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addr = dstsec->sh_addr + rela->r_offset;
|
|
|
|
|
|
|
|
|
|
/* Now perform the architecture-specific relocation */
|
|
|
|
|
|
riscv/arch_elf.c: Handle PCREL_HI20/LO12_I/S relocations correctly
There is a problem with the current elf loader for risc-v: when a pair of
PCREL_HI20 / LO12 relocations are encountered, it is assumed that these
will follow each other immediately, as follows:
label:
auipc a0, %pcrel_hi(symbol) // R_RISCV_PCREL_HI20
load/store a0, %pcrel_lo(label)(a0) // R_RISCV_PCREL_LO12_I/S
With this assumption, the hi/lo relocations are both done when a hi20
relocation entry is encountered, first to the current instruction (addr)
and to the next instruction (addr + 4).
However, this assumption is wrong. There is nothing in the elf relocation
specification[1] that mandates this. Thus, the hi/lo relocation always
needs to first fixup the hi-part, and when the lo-part is encountered, it
needs to find the corresponding hi relocation entry, via the given "label".
This necessitates (re-)visiting the relocation entries for the current
section as well as looking for "label" in the symbol table.
The NuttX elf loader does not allow such operations to be done in the
machine specific part, so this patch fixes the relocation issue by
introducing an architecture specific cache for the hi20 relocation and
symbol table entries. When a lo12 relocation is encountered, the cache
can be consulted to find the hi20 part.
[1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
2023-12-05 12:30:46 +02:00
|
|
|
ret = up_relocateadd(rela, sym, addr, ARCH_ELFDATA_PARM);
|
2020-02-07 17:10:23 -06:00
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2020-03-10 14:52:58 +09:00
|
|
|
berr("ERROR: Section %d reloc %d: Relocation failed: %d\n",
|
|
|
|
|
relidx, i, ret);
|
2020-02-07 17:10:23 -06:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lib_free(relas);
|
2023-09-15 21:59:06 +03:00
|
|
|
while ((e = dq_peek(&q)) != NULL)
|
2020-02-07 17:10:23 -06:00
|
|
|
{
|
|
|
|
|
dq_rem(e, &q);
|
|
|
|
|
lib_free(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
|
|
|
|
|
2022-09-26 16:22:03 +10:00
|
|
|
/****************************************************************************
|
2025-04-10 09:51:25 +08:00
|
|
|
* Name: libelf_relocatedyn
|
2022-09-26 16:22:03 +10:00
|
|
|
*
|
|
|
|
|
* Description:
|
|
|
|
|
* Perform all relocations associated with a dynamic section.
|
|
|
|
|
*
|
|
|
|
|
* Returned Value:
|
|
|
|
|
* 0 (OK) is returned on success and a negated errno is returned on
|
|
|
|
|
* failure.
|
|
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
static int libelf_relocatedyn(FAR struct module_s *modp,
|
2022-09-26 16:22:03 +10:00
|
|
|
FAR struct mod_loadinfo_s *loadinfo,
|
libs/libc/elf: Fix two ways an FDPIC module failed to relocate.
Running one for the first time turned up two holes in the ET_DYN path.
Neither shows up in a build.
An undefined symbol is resolved with libelf_findglobal(), which searches
only the table of globally registered symbols. The export table that
exec() hands its caller went no further than the ET_REL path, so an
ET_DYN module could not import anything the caller supplied. Invisible
while such modules resolved everything internally; an FDPIC module
imports its libc, and every import failed with "Unable to resolve addr of
ext ref printf" although the caller had passed a table containing printf.
The export table is now threaded into libelf_relocatedyn() and consulted
when the global table has no answer, leaving the existing lookup order
intact.
A relocation naming a symbol defined inside the object was dropped
silently. The code handles a relocation with no symbol, and one against
an undefined symbol, but a defined symbol fell through both. That was
harmless while every dynamic relocation arriving here had symbol index
zero, which is the case for R_ARM_RELATIVE. FDPIC brings the first ones
that do not: a pointer to a static function is emitted against the
*section* symbol, so the value is the section base and the offset within
it -- including the Thumb bit -- is carried as the addend. Deriving a
value from the word being patched, as the no-symbol case does, would
translate that addend as though it were an address. Confirmed against a
real module: .text at 0x23c plus an addend of 0x95 gives 0x2d1, which is
the function with its Thumb bit.
Also stop libelf_symname() reporting a nameless symbol as an error. A
section symbol has no name, and libelf_findsymbol() walks the whole table
looking for optional entries such as nx_stacksize, so it meets these
routinely and checks for -ESRCH itself. At error level it printed ten or
more lines per module load and buried the diagnostics that matter.
Built and run on lm3s6965-ek with the examples/elf ROMFS. The ET_REL
test modules load as before, and an FDPIC module now loads, relocates,
resolves printf and puts from the table exec() supplied, and calls
through a function descriptor of its own.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 11:28:49 +02:00
|
|
|
int relidx,
|
|
|
|
|
FAR const struct symtab_s *exports,
|
|
|
|
|
int nexports)
|
2022-09-26 16:22:03 +10:00
|
|
|
{
|
|
|
|
|
FAR Elf_Shdr *shdr = &loadinfo->shdr[relidx];
|
|
|
|
|
FAR Elf_Shdr *symhdr;
|
|
|
|
|
FAR Elf_Dyn *dyn = NULL;
|
|
|
|
|
FAR Elf_Rel *rels = NULL;
|
|
|
|
|
FAR Elf_Rel *rel;
|
2023-09-04 10:37:48 +10:00
|
|
|
FAR Elf_Rela *relas = NULL;
|
|
|
|
|
FAR Elf_Rela *rela;
|
2022-09-26 16:22:03 +10:00
|
|
|
FAR Elf_Sym *sym = NULL;
|
2023-09-15 21:59:06 +03:00
|
|
|
uintptr_t addr;
|
|
|
|
|
int ret;
|
|
|
|
|
int i;
|
|
|
|
|
int idx_rel;
|
|
|
|
|
int idx_sym;
|
2022-09-26 16:22:03 +10:00
|
|
|
|
riscv/arch_elf.c: Handle PCREL_HI20/LO12_I/S relocations correctly
There is a problem with the current elf loader for risc-v: when a pair of
PCREL_HI20 / LO12 relocations are encountered, it is assumed that these
will follow each other immediately, as follows:
label:
auipc a0, %pcrel_hi(symbol) // R_RISCV_PCREL_HI20
load/store a0, %pcrel_lo(label)(a0) // R_RISCV_PCREL_LO12_I/S
With this assumption, the hi/lo relocations are both done when a hi20
relocation entry is encountered, first to the current instruction (addr)
and to the next instruction (addr + 4).
However, this assumption is wrong. There is nothing in the elf relocation
specification[1] that mandates this. Thus, the hi/lo relocation always
needs to first fixup the hi-part, and when the lo-part is encountered, it
needs to find the corresponding hi relocation entry, via the given "label".
This necessitates (re-)visiting the relocation entries for the current
section as well as looking for "label" in the symbol table.
The NuttX elf loader does not allow such operations to be done in the
machine specific part, so this patch fixes the relocation issue by
introducing an architecture specific cache for the hi20 relocation and
symbol table entries. When a lo12 relocation is encountered, the cache
can be consulted to find the hi20 part.
[1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
2023-12-05 12:30:46 +02:00
|
|
|
/* Define potential architecture specific elf data container */
|
|
|
|
|
|
|
|
|
|
ARCH_ELFDATA_DEF;
|
|
|
|
|
|
2022-09-26 16:22:03 +10:00
|
|
|
dyn = lib_malloc(shdr->sh_size);
|
2024-09-11 15:19:30 +08:00
|
|
|
if (dyn == NULL)
|
|
|
|
|
{
|
|
|
|
|
berr("Failed to allocate memory for elf dynamic section\n");
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_read(loadinfo, (FAR uint8_t *)dyn, shdr->sh_size,
|
2022-09-26 16:22:03 +10:00
|
|
|
shdr->sh_offset);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
berr("Failed to read dynamic section header");
|
2024-09-11 15:19:30 +08:00
|
|
|
lib_free(dyn);
|
2022-09-26 16:22:03 +10:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-04 10:37:48 +10:00
|
|
|
/* Assume DT_RELA to get maximum size required */
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
rels = lib_zalloc(CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT *
|
|
|
|
|
sizeof(Elf_Rela));
|
2022-09-26 16:22:03 +10:00
|
|
|
if (!rels)
|
|
|
|
|
{
|
|
|
|
|
berr("Failed to allocate memory for elf relocation rels\n");
|
|
|
|
|
lib_free(dyn);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-24 19:21:12 -04:00
|
|
|
memset((FAR void *)&reldata, 0, sizeof(reldata));
|
2023-09-04 10:37:48 +10:00
|
|
|
relas = (FAR Elf_Rela *)rels;
|
2022-09-26 16:22:03 +10:00
|
|
|
|
|
|
|
|
for (i = 0; dyn[i].d_tag != DT_NULL; i++)
|
|
|
|
|
{
|
|
|
|
|
switch (dyn[i].d_tag)
|
2023-07-12 14:37:56 -03:00
|
|
|
{
|
2023-09-15 21:59:06 +03:00
|
|
|
case DT_REL:
|
2023-07-03 00:11:02 +08:00
|
|
|
reldata.reloff[I_REL] = dyn[i].d_un.d_val;
|
|
|
|
|
break;
|
2023-09-15 21:59:06 +03:00
|
|
|
case DT_RELSZ:
|
2023-07-03 00:11:02 +08:00
|
|
|
reldata.relsz[I_REL] = dyn[i].d_un.d_val;
|
|
|
|
|
break;
|
2023-09-15 21:59:06 +03:00
|
|
|
case DT_RELENT:
|
2023-09-04 10:37:48 +10:00
|
|
|
reldata.relentsz[I_REL] = dyn[i].d_un.d_val;
|
2023-07-03 00:11:02 +08:00
|
|
|
break;
|
2023-09-15 21:59:06 +03:00
|
|
|
case DT_SYMTAB:
|
2023-07-03 00:11:02 +08:00
|
|
|
reldata.symoff = dyn[i].d_un.d_val;
|
|
|
|
|
break;
|
2023-09-15 21:59:06 +03:00
|
|
|
case DT_STRTAB:
|
2023-07-03 00:11:02 +08:00
|
|
|
reldata.stroff = dyn[i].d_un.d_val;
|
|
|
|
|
break;
|
2023-09-15 21:59:06 +03:00
|
|
|
case DT_JMPREL:
|
2023-07-03 00:11:02 +08:00
|
|
|
reldata.reloff[I_PLT] = dyn[i].d_un.d_val;
|
|
|
|
|
break;
|
2023-09-15 21:59:06 +03:00
|
|
|
case DT_PLTRELSZ:
|
2023-07-03 00:11:02 +08:00
|
|
|
reldata.relsz[I_PLT] = dyn[i].d_un.d_val;
|
|
|
|
|
break;
|
libs/libc/elf: Read the dynamic tags an FDPIC object needs.
libelf_relocatedyn() reads the handful of DT_* tags it needs to walk the
relocation tables and ignores the rest. Three more matter now.
DT_PLTGOT is where the object's data base lives. An FDPIC module runs
with that in the PIC base register, and every function descriptor built
for it names the same base as the one its callee should run with, so
without it there is nothing to put in a descriptor's second word.
The DT_*_ARRAY tags are the constructor and destructor tables. These are
already found through the section headers a few lines further down, and
that path is kept, but the dynamic tags are the authoritative copy and an
object is not obliged to carry section headers at all. Both paths now
translate through libelf_addr(), so they agree on the answer rather than
depending on which ran last. The tag values themselves were missing from
include/elf.h and are added.
Sizing the descriptor pool has to happen here rather than later.
R_ARM_FUNCDESC asks the loader to manufacture a descriptor and hand back
its address, which means the space must exist by the time the relocation
is applied, and by then the segment has been placed. So libelf_elfsize()
reserves it behind the writable data, bounded by the relocation count --
one relocation cannot ask for more than one descriptor. That bound has
slack in it, but a descriptor is two words and modules are small, which
is cheaper than walking every relocation twice to get an exact count.
Nothing here runs for a non-FDPIC object. Built and booted
mps3-an547:picostest with no change in behaviour.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 08:49:25 +02:00
|
|
|
case DT_PLTGOT:
|
|
|
|
|
|
|
|
|
|
/* The object's data base. Every function descriptor built
|
|
|
|
|
* for it names this base.
|
|
|
|
|
*/
|
|
|
|
|
|
libs/libc/machine/arm: Relocate FDPIC function descriptors.
A function pointer under FDPIC is not a code address. Because each
PT_LOAD segment is placed independently, a pointer has to carry the data
base its callee will need, so it is a two-word descriptor: the entry
point, and the base to install in the PIC register before branching.
R_ARM_FUNCDESC_VALUE says "the thing you are patching is such a
descriptor", and R_ARM_FUNCDESC says "manufacture one and give me its
address".
Both need state a relocation cannot carry. A descriptor's second word is
the *object's* data base, from DT_PLTGOT, and R_ARM_FUNCDESC carves
descriptors from a pool whose cursor has to survive from one relocation
to the next. up_relocate() is handed only a relocation, a resolved
symbol and an address to patch.
arch_data is the existing channel for exactly this -- RISC-V already uses
it to remember a HI20 relocation while its LO12 partner is processed --
but nothing has ever put loader state into it: it is declared zeroed and
written only by up_relocate() itself. So ARCH_ELFDATA_INIT and
ARCH_ELFDATA_FINI are added, seeding the block from the loadinfo before
the relocation loop and reading the cursor back after. Both default to
nothing, so an architecture that does not define them is unaffected, and
RISC-V's use of arch_data is untouched. libelf_relocatedyn() walks both
dynamic tables under one arch_data, so the cursor spans the whole object.
The addend handling is the part that is easy to get wrong. REL format
keeps the addend in place, in the word about to become the entry point,
and a pointer to a static function is referenced through its *section*
symbol -- the value is the section base and the offset, including the
Thumb bit, is entirely in the addend. Dropping it yields an even address
and the core faults trying to execute it as ARM code.
The GOT written into a descriptor is the loading object's own, even for
an imported function, which is what makes a callback work: when the base
firmware's qsort() calls back into a module's comparison function, the
module needs its own data base in the PIC register.
libelf_relocatedyn()'s imported-symbol path needed a change to suit. It
stores the resolved address directly and never calls up_relocate(), which
cannot produce a two-word descriptor, so under FDPIC the resolved value
now goes through up_relocate() and the relocation type decides what to
write.
Implemented for armv7-m and armv8-m, the profiles FDPIC targets; the
other ARM variants gain the arch_data block but no new relocations.
Built and booted mps3-an547:picostest and lm3s6965-ek:qemu-nxflat, the
ELF PIC and NXFLAT users of this code, both unchanged.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 08:58:41 +02:00
|
|
|
loadinfo->gotbase = libelf_addr(loadinfo,
|
|
|
|
|
dyn[i].d_un.d_ptr);
|
libs/libc/elf: Read the dynamic tags an FDPIC object needs.
libelf_relocatedyn() reads the handful of DT_* tags it needs to walk the
relocation tables and ignores the rest. Three more matter now.
DT_PLTGOT is where the object's data base lives. An FDPIC module runs
with that in the PIC base register, and every function descriptor built
for it names the same base as the one its callee should run with, so
without it there is nothing to put in a descriptor's second word.
The DT_*_ARRAY tags are the constructor and destructor tables. These are
already found through the section headers a few lines further down, and
that path is kept, but the dynamic tags are the authoritative copy and an
object is not obliged to carry section headers at all. Both paths now
translate through libelf_addr(), so they agree on the answer rather than
depending on which ran last. The tag values themselves were missing from
include/elf.h and are added.
Sizing the descriptor pool has to happen here rather than later.
R_ARM_FUNCDESC asks the loader to manufacture a descriptor and hand back
its address, which means the space must exist by the time the relocation
is applied, and by then the segment has been placed. So libelf_elfsize()
reserves it behind the writable data, bounded by the relocation count --
one relocation cannot ask for more than one descriptor. That bound has
slack in it, but a descriptor is two words and modules are small, which
is cheaper than walking every relocation twice to get an exact count.
Nothing here runs for a non-FDPIC object. Built and booted
mps3-an547:picostest with no change in behaviour.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 08:49:25 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* The constructor and destructor tables. Section headers are
|
|
|
|
|
* optional, so the dynamic tags are the authoritative copy.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
case DT_INIT_ARRAY:
|
|
|
|
|
loadinfo->initarr = libelf_addr(loadinfo, dyn[i].d_un.d_ptr);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_INIT_ARRAYSZ:
|
|
|
|
|
loadinfo->ninit = dyn[i].d_un.d_val / sizeof(uintptr_t);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_FINI_ARRAY:
|
|
|
|
|
loadinfo->finiarr = libelf_addr(loadinfo, dyn[i].d_un.d_ptr);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_FINI_ARRAYSZ:
|
|
|
|
|
loadinfo->nfini = dyn[i].d_un.d_val / sizeof(uintptr_t);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_PREINIT_ARRAY:
|
|
|
|
|
loadinfo->preiarr = libelf_addr(loadinfo, dyn[i].d_un.d_ptr);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_PREINIT_ARRAYSZ:
|
|
|
|
|
loadinfo->nprei = dyn[i].d_un.d_val / sizeof(uintptr_t);
|
|
|
|
|
break;
|
|
|
|
|
|
2023-09-04 10:37:48 +10:00
|
|
|
case DT_PLTREL:
|
|
|
|
|
if (dyn[i].d_un.d_val == DT_REL)
|
|
|
|
|
{
|
|
|
|
|
reldata.relentsz[I_PLT] = sizeof(Elf_Rel);
|
|
|
|
|
reldata.relrela[I_PLT] = 0;
|
|
|
|
|
}
|
libs/libc/elf: Read the dynamic tags an FDPIC object needs.
libelf_relocatedyn() reads the handful of DT_* tags it needs to walk the
relocation tables and ignores the rest. Three more matter now.
DT_PLTGOT is where the object's data base lives. An FDPIC module runs
with that in the PIC base register, and every function descriptor built
for it names the same base as the one its callee should run with, so
without it there is nothing to put in a descriptor's second word.
The DT_*_ARRAY tags are the constructor and destructor tables. These are
already found through the section headers a few lines further down, and
that path is kept, but the dynamic tags are the authoritative copy and an
object is not obliged to carry section headers at all. Both paths now
translate through libelf_addr(), so they agree on the answer rather than
depending on which ran last. The tag values themselves were missing from
include/elf.h and are added.
Sizing the descriptor pool has to happen here rather than later.
R_ARM_FUNCDESC asks the loader to manufacture a descriptor and hand back
its address, which means the space must exist by the time the relocation
is applied, and by then the segment has been placed. So libelf_elfsize()
reserves it behind the writable data, bounded by the relocation count --
one relocation cannot ask for more than one descriptor. That bound has
slack in it, but a descriptor is two words and modules are small, which
is cheaper than walking every relocation twice to get an exact count.
Nothing here runs for a non-FDPIC object. Built and booted
mps3-an547:picostest with no change in behaviour.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 08:49:25 +02:00
|
|
|
else if (loadinfo->fdpic)
|
|
|
|
|
{
|
|
|
|
|
/* The ARM FDPIC ABI is REL throughout. RELA entries are
|
|
|
|
|
* longer, so walking them as REL reads the wrong place.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
berr("ERROR: FDPIC object claims RELA PLT relocations\n");
|
|
|
|
|
lib_free(sym);
|
|
|
|
|
lib_free(rels);
|
|
|
|
|
lib_free(dyn);
|
|
|
|
|
return -ENOEXEC;
|
|
|
|
|
}
|
2023-09-04 10:37:48 +10:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reldata.relentsz[I_PLT] = sizeof(Elf_Rela);
|
|
|
|
|
reldata.relrela[I_PLT] = 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2022-09-26 16:22:03 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
libs/libc/machine/arm: Relocate FDPIC function descriptors.
A function pointer under FDPIC is not a code address. Because each
PT_LOAD segment is placed independently, a pointer has to carry the data
base its callee will need, so it is a two-word descriptor: the entry
point, and the base to install in the PIC register before branching.
R_ARM_FUNCDESC_VALUE says "the thing you are patching is such a
descriptor", and R_ARM_FUNCDESC says "manufacture one and give me its
address".
Both need state a relocation cannot carry. A descriptor's second word is
the *object's* data base, from DT_PLTGOT, and R_ARM_FUNCDESC carves
descriptors from a pool whose cursor has to survive from one relocation
to the next. up_relocate() is handed only a relocation, a resolved
symbol and an address to patch.
arch_data is the existing channel for exactly this -- RISC-V already uses
it to remember a HI20 relocation while its LO12 partner is processed --
but nothing has ever put loader state into it: it is declared zeroed and
written only by up_relocate() itself. So ARCH_ELFDATA_INIT and
ARCH_ELFDATA_FINI are added, seeding the block from the loadinfo before
the relocation loop and reading the cursor back after. Both default to
nothing, so an architecture that does not define them is unaffected, and
RISC-V's use of arch_data is untouched. libelf_relocatedyn() walks both
dynamic tables under one arch_data, so the cursor spans the whole object.
The addend handling is the part that is easy to get wrong. REL format
keeps the addend in place, in the word about to become the entry point,
and a pointer to a static function is referenced through its *section*
symbol -- the value is the section base and the offset, including the
Thumb bit, is entirely in the addend. Dropping it yields an even address
and the core faults trying to execute it as ARM code.
The GOT written into a descriptor is the loading object's own, even for
an imported function, which is what makes a callback work: when the base
firmware's qsort() calls back into a module's comparison function, the
module needs its own data base in the PIC register.
libelf_relocatedyn()'s imported-symbol path needed a change to suit. It
stores the resolved address directly and never calls up_relocate(), which
cannot produce a two-word descriptor, so under FDPIC the resolved value
now goes through up_relocate() and the relocation type decides what to
write.
Implemented for armv7-m and armv8-m, the profiles FDPIC targets; the
other ARM variants gain the arch_data block but no new relocations.
Built and booted mps3-an547:picostest and lm3s6965-ek:qemu-nxflat, the
ELF PIC and NXFLAT users of this code, both unchanged.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 08:58:41 +02:00
|
|
|
/* After the loop, because DT_PLTGOT is read there. Both relocation
|
|
|
|
|
* tables are walked under this one arch_data, so the pool cursor
|
|
|
|
|
* survives from one to the next.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
ARCH_ELFDATA_SETUP(loadinfo);
|
|
|
|
|
|
2022-09-26 16:22:03 +10:00
|
|
|
symhdr = &loadinfo->shdr[loadinfo->dsymtabidx];
|
|
|
|
|
sym = lib_malloc(symhdr->sh_size);
|
|
|
|
|
if (!sym)
|
|
|
|
|
{
|
|
|
|
|
berr("Error obtaining storage for dynamic symbol table");
|
|
|
|
|
lib_free(rels);
|
|
|
|
|
lib_free(dyn);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_read(loadinfo, (FAR uint8_t *)sym, symhdr->sh_size,
|
2022-09-26 16:22:03 +10:00
|
|
|
symhdr->sh_offset);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
berr("Error reading dynamic symbol table - %d", ret);
|
|
|
|
|
lib_free(sym);
|
|
|
|
|
lib_free(rels);
|
|
|
|
|
lib_free(dyn);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-24 21:06:59 -03:00
|
|
|
reldata.lsymtab = reldata.stroff - reldata.symoff;
|
2022-09-26 16:22:03 +10:00
|
|
|
|
|
|
|
|
for (idx_rel = 0; idx_rel < N_RELS; idx_rel++)
|
|
|
|
|
{
|
2023-09-04 10:37:48 +10:00
|
|
|
int lrelent;
|
|
|
|
|
|
|
|
|
|
if ((reldata.relsz[idx_rel] == 0) || (reldata.reloff[idx_rel] == 0))
|
2022-09-26 16:22:03 +10:00
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Examine each relocation in the .rel.* section. */
|
|
|
|
|
|
|
|
|
|
ret = OK;
|
2023-09-04 10:37:48 +10:00
|
|
|
lrelent = reldata.relsz[idx_rel] / reldata.relentsz[idx_rel];
|
2022-09-26 16:22:03 +10:00
|
|
|
|
libs/libc/machine/arm: Relocate FDPIC function descriptors.
A function pointer under FDPIC is not a code address. Because each
PT_LOAD segment is placed independently, a pointer has to carry the data
base its callee will need, so it is a two-word descriptor: the entry
point, and the base to install in the PIC register before branching.
R_ARM_FUNCDESC_VALUE says "the thing you are patching is such a
descriptor", and R_ARM_FUNCDESC says "manufacture one and give me its
address".
Both need state a relocation cannot carry. A descriptor's second word is
the *object's* data base, from DT_PLTGOT, and R_ARM_FUNCDESC carves
descriptors from a pool whose cursor has to survive from one relocation
to the next. up_relocate() is handed only a relocation, a resolved
symbol and an address to patch.
arch_data is the existing channel for exactly this -- RISC-V already uses
it to remember a HI20 relocation while its LO12 partner is processed --
but nothing has ever put loader state into it: it is declared zeroed and
written only by up_relocate() itself. So ARCH_ELFDATA_INIT and
ARCH_ELFDATA_FINI are added, seeding the block from the loadinfo before
the relocation loop and reading the cursor back after. Both default to
nothing, so an architecture that does not define them is unaffected, and
RISC-V's use of arch_data is untouched. libelf_relocatedyn() walks both
dynamic tables under one arch_data, so the cursor spans the whole object.
The addend handling is the part that is easy to get wrong. REL format
keeps the addend in place, in the word about to become the entry point,
and a pointer to a static function is referenced through its *section*
symbol -- the value is the section base and the offset, including the
Thumb bit, is entirely in the addend. Dropping it yields an even address
and the core faults trying to execute it as ARM code.
The GOT written into a descriptor is the loading object's own, even for
an imported function, which is what makes a callback work: when the base
firmware's qsort() calls back into a module's comparison function, the
module needs its own data base in the PIC register.
libelf_relocatedyn()'s imported-symbol path needed a change to suit. It
stores the resolved address directly and never calls up_relocate(), which
cannot produce a two-word descriptor, so under FDPIC the resolved value
now goes through up_relocate() and the relocation type decides what to
write.
Implemented for armv7-m and armv8-m, the profiles FDPIC targets; the
other ARM variants gain the arch_data block but no new relocations.
Built and booted mps3-an547:picostest and lm3s6965-ek:qemu-nxflat, the
ELF PIC and NXFLAT users of this code, both unchanged.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 08:58:41 +02:00
|
|
|
/* Say which table this is, for an architecture that cares. */
|
|
|
|
|
|
|
|
|
|
ARCH_ELFDATA_PLTREL(idx_rel == I_PLT);
|
|
|
|
|
|
2023-09-04 10:37:48 +10:00
|
|
|
for (i = 0; i < lrelent; i++)
|
2022-09-26 16:22:03 +10:00
|
|
|
{
|
2023-09-04 10:37:48 +10:00
|
|
|
/* Process each relocation entry
|
|
|
|
|
* - we cheat by using the fact the 1st two fields of Elf_Rel
|
|
|
|
|
* and Elf_Rela are identical so can do things based on the
|
|
|
|
|
* former until it's important
|
|
|
|
|
*/
|
2022-09-26 16:22:03 +10:00
|
|
|
|
2023-09-04 10:37:48 +10:00
|
|
|
if (reldata.relrela[idx_rel] == 0)
|
|
|
|
|
{
|
2025-04-10 09:51:25 +08:00
|
|
|
rel = &rels[i % CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT];
|
2023-09-04 10:37:48 +10:00
|
|
|
rela = (Elf_Rela *)rel; /* Just to keep the compiler happy */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-04-10 09:51:25 +08:00
|
|
|
rela = &relas[i % CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT];
|
2023-09-04 10:37:48 +10:00
|
|
|
rel = (Elf_Rel *)rela;
|
|
|
|
|
}
|
2022-09-26 16:22:03 +10:00
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
if (!(i % CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT))
|
2022-09-26 16:22:03 +10:00
|
|
|
{
|
2023-09-04 10:37:48 +10:00
|
|
|
size_t relsize = (sizeof(Elf_Rela) *
|
2025-04-10 09:51:25 +08:00
|
|
|
CONFIG_LIBC_ELF_RELOCATION_BUFFERCOUNT);
|
2023-07-12 14:37:56 -03:00
|
|
|
|
2023-07-24 21:06:59 -03:00
|
|
|
if (reldata.relsz[idx_rel] < relsize)
|
2023-07-12 14:37:56 -03:00
|
|
|
{
|
2023-07-24 21:06:59 -03:00
|
|
|
relsize = reldata.relsz[idx_rel];
|
2023-07-12 14:37:56 -03:00
|
|
|
}
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_read(loadinfo, (FAR uint8_t *)rels,
|
2023-07-24 21:06:59 -03:00
|
|
|
relsize,
|
|
|
|
|
reldata.reloff[idx_rel] +
|
2022-09-26 16:22:03 +10:00
|
|
|
i * sizeof(Elf_Rel));
|
2023-07-12 14:37:56 -03:00
|
|
|
|
2022-09-26 16:22:03 +10:00
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
berr("ERROR: Section %d reloc %d:"
|
|
|
|
|
"Failed to read relocation entry: %d\n",
|
|
|
|
|
relidx, i, ret);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now perform the architecture-specific relocation */
|
|
|
|
|
|
|
|
|
|
if ((idx_sym = ELF_R_SYM(rel->r_info)) != 0)
|
|
|
|
|
{
|
2024-08-03 11:23:18 +08:00
|
|
|
/* We have an external reference */
|
|
|
|
|
|
|
|
|
|
if (sym[idx_sym].st_shndx == SHN_UNDEF)
|
2022-09-26 16:22:03 +10:00
|
|
|
{
|
2026-08-26 18:17:28 +02:00
|
|
|
FAR void *ep;
|
|
|
|
|
|
|
|
|
|
ep = libelf_findglobal(modp, loadinfo, symhdr,
|
|
|
|
|
&sym[idx_sym]);
|
libs/libc/elf: Fix two ways an FDPIC module failed to relocate.
Running one for the first time turned up two holes in the ET_DYN path.
Neither shows up in a build.
An undefined symbol is resolved with libelf_findglobal(), which searches
only the table of globally registered symbols. The export table that
exec() hands its caller went no further than the ET_REL path, so an
ET_DYN module could not import anything the caller supplied. Invisible
while such modules resolved everything internally; an FDPIC module
imports its libc, and every import failed with "Unable to resolve addr of
ext ref printf" although the caller had passed a table containing printf.
The export table is now threaded into libelf_relocatedyn() and consulted
when the global table has no answer, leaving the existing lookup order
intact.
A relocation naming a symbol defined inside the object was dropped
silently. The code handles a relocation with no symbol, and one against
an undefined symbol, but a defined symbol fell through both. That was
harmless while every dynamic relocation arriving here had symbol index
zero, which is the case for R_ARM_RELATIVE. FDPIC brings the first ones
that do not: a pointer to a static function is emitted against the
*section* symbol, so the value is the section base and the offset within
it -- including the Thumb bit -- is carried as the addend. Deriving a
value from the word being patched, as the no-symbol case does, would
translate that addend as though it were an address. Confirmed against a
real module: .text at 0x23c plus an addend of 0x95 gives 0x2d1, which is
the function with its Thumb bit.
Also stop libelf_symname() reporting a nameless symbol as an error. A
section symbol has no name, and libelf_findsymbol() walks the whole table
looking for optional entries such as nx_stacksize, so it meets these
routinely and checks for -ESRCH itself. At error level it printed ten or
more lines per module load and buried the diagnostics that matter.
Built and run on lm3s6965-ek with the examples/elf ROMFS. The ET_REL
test modules load as before, and an FDPIC module now loads, relocates,
resolves printf and puts from the table exec() supplied, and calls
through a function descriptor of its own.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 11:28:49 +02:00
|
|
|
|
|
|
|
|
/* libelf_findglobal() searches only the registered
|
|
|
|
|
* symbols. A module from exec() has its own export
|
|
|
|
|
* table, and an FDPIC module imports its libc there.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (ep == NULL && exports != NULL)
|
|
|
|
|
{
|
|
|
|
|
FAR const struct symtab_s *sm;
|
|
|
|
|
|
|
|
|
|
sm = symtab_findbyname(exports,
|
|
|
|
|
(FAR char *)
|
|
|
|
|
loadinfo->iobuffer,
|
|
|
|
|
nexports);
|
|
|
|
|
if (sm != NULL)
|
|
|
|
|
{
|
|
|
|
|
ep = (FAR void *)sm->sym_value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-26 18:17:28 +02:00
|
|
|
if ((ep == NULL) && (ELF_ST_BIND(sym[idx_sym].st_info)
|
|
|
|
|
!= STB_WEAK))
|
|
|
|
|
{
|
|
|
|
|
berr("ERROR: Unable to resolve addr of ext ref %s\n",
|
|
|
|
|
loadinfo->iobuffer);
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
lib_free(sym);
|
|
|
|
|
lib_free(rels);
|
|
|
|
|
lib_free(dyn);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addr = libelf_addr(loadinfo, rel->r_offset);
|
|
|
|
|
|
|
|
|
|
if (reldata.relrela[idx_rel] == 1)
|
|
|
|
|
{
|
|
|
|
|
addr += rela->r_addend;
|
|
|
|
|
}
|
|
|
|
|
|
libs/libc/machine/arm: Relocate FDPIC function descriptors.
A function pointer under FDPIC is not a code address. Because each
PT_LOAD segment is placed independently, a pointer has to carry the data
base its callee will need, so it is a two-word descriptor: the entry
point, and the base to install in the PIC register before branching.
R_ARM_FUNCDESC_VALUE says "the thing you are patching is such a
descriptor", and R_ARM_FUNCDESC says "manufacture one and give me its
address".
Both need state a relocation cannot carry. A descriptor's second word is
the *object's* data base, from DT_PLTGOT, and R_ARM_FUNCDESC carves
descriptors from a pool whose cursor has to survive from one relocation
to the next. up_relocate() is handed only a relocation, a resolved
symbol and an address to patch.
arch_data is the existing channel for exactly this -- RISC-V already uses
it to remember a HI20 relocation while its LO12 partner is processed --
but nothing has ever put loader state into it: it is declared zeroed and
written only by up_relocate() itself. So ARCH_ELFDATA_INIT and
ARCH_ELFDATA_FINI are added, seeding the block from the loadinfo before
the relocation loop and reading the cursor back after. Both default to
nothing, so an architecture that does not define them is unaffected, and
RISC-V's use of arch_data is untouched. libelf_relocatedyn() walks both
dynamic tables under one arch_data, so the cursor spans the whole object.
The addend handling is the part that is easy to get wrong. REL format
keeps the addend in place, in the word about to become the entry point,
and a pointer to a static function is referenced through its *section*
symbol -- the value is the section base and the offset, including the
Thumb bit, is entirely in the addend. Dropping it yields an even address
and the core faults trying to execute it as ARM code.
The GOT written into a descriptor is the loading object's own, even for
an imported function, which is what makes a callback work: when the base
firmware's qsort() calls back into a module's comparison function, the
module needs its own data base in the PIC register.
libelf_relocatedyn()'s imported-symbol path needed a change to suit. It
stores the resolved address directly and never calls up_relocate(), which
cannot produce a two-word descriptor, so under FDPIC the resolved value
now goes through up_relocate() and the relocation type decides what to
write.
Implemented for armv7-m and armv8-m, the profiles FDPIC targets; the
other ARM variants gain the arch_data block but no new relocations.
Built and booted mps3-an547:picostest and lm3s6965-ek:qemu-nxflat, the
ELF PIC and NXFLAT users of this code, both unchanged.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 08:58:41 +02:00
|
|
|
/* An import may be a descriptor under FDPIC, which is
|
|
|
|
|
* built rather than assigned, so the relocation type
|
|
|
|
|
* decides what to write. Everything else stores the
|
|
|
|
|
* resolved address, which R_ARM_JUMP_SLOT and
|
|
|
|
|
* R_ARM_GLOB_DAT do, so one path serves both.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Elf_Sym extsym =
|
|
|
|
|
{
|
|
|
|
|
0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
extsym.st_value = (uintptr_t)ep;
|
|
|
|
|
|
|
|
|
|
ret = up_relocate(rel, &extsym, addr, ARCH_ELFDATA_PARM);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Relocation failed: %d\n", relidx, i, ret);
|
|
|
|
|
lib_free(sym);
|
|
|
|
|
lib_free(rels);
|
|
|
|
|
lib_free(dyn);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2022-09-26 16:22:03 +10:00
|
|
|
}
|
libs/libc/elf: Fix two ways an FDPIC module failed to relocate.
Running one for the first time turned up two holes in the ET_DYN path.
Neither shows up in a build.
An undefined symbol is resolved with libelf_findglobal(), which searches
only the table of globally registered symbols. The export table that
exec() hands its caller went no further than the ET_REL path, so an
ET_DYN module could not import anything the caller supplied. Invisible
while such modules resolved everything internally; an FDPIC module
imports its libc, and every import failed with "Unable to resolve addr of
ext ref printf" although the caller had passed a table containing printf.
The export table is now threaded into libelf_relocatedyn() and consulted
when the global table has no answer, leaving the existing lookup order
intact.
A relocation naming a symbol defined inside the object was dropped
silently. The code handles a relocation with no symbol, and one against
an undefined symbol, but a defined symbol fell through both. That was
harmless while every dynamic relocation arriving here had symbol index
zero, which is the case for R_ARM_RELATIVE. FDPIC brings the first ones
that do not: a pointer to a static function is emitted against the
*section* symbol, so the value is the section base and the offset within
it -- including the Thumb bit -- is carried as the addend. Deriving a
value from the word being patched, as the no-symbol case does, would
translate that addend as though it were an address. Confirmed against a
real module: .text at 0x23c plus an addend of 0x95 gives 0x2d1, which is
the function with its Thumb bit.
Also stop libelf_symname() reporting a nameless symbol as an error. A
section symbol has no name, and libelf_findsymbol() walks the whole table
looking for optional entries such as nx_stacksize, so it meets these
routinely and checks for -ESRCH itself. At error level it printed ten or
more lines per module load and buried the diagnostics that matter.
Built and run on lm3s6965-ek with the examples/elf ROMFS. The ET_REL
test modules load as before, and an FDPIC module now loads, relocates,
resolves printf and puts from the table exec() supplied, and calls
through a function descriptor of its own.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 11:28:49 +02:00
|
|
|
else if (loadinfo->fdpic)
|
|
|
|
|
{
|
|
|
|
|
/* A relocation naming a symbol inside this object. A
|
|
|
|
|
* pointer to a static function is emitted against the
|
|
|
|
|
* section symbol, so the offset, Thumb bit included, is
|
|
|
|
|
* the addend and must not come from the patched word.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Elf_Sym defsym = sym[idx_sym];
|
|
|
|
|
|
|
|
|
|
defsym.st_value = libelf_addr(loadinfo,
|
|
|
|
|
sym[idx_sym].st_value);
|
|
|
|
|
|
|
|
|
|
addr = libelf_addr(loadinfo, rel->r_offset);
|
|
|
|
|
|
|
|
|
|
if (reldata.relrela[idx_rel] == 1)
|
|
|
|
|
{
|
|
|
|
|
addr += rela->r_addend;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = up_relocate(rel, &defsym, addr, ARCH_ELFDATA_PARM);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
berr("ERROR: Section %d reloc %d: "
|
|
|
|
|
"Relocation failed: %d\n", relidx, i, ret);
|
|
|
|
|
lib_free(sym);
|
|
|
|
|
lib_free(rels);
|
|
|
|
|
lib_free(dyn);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-26 16:22:03 +10:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-09-11 16:38:12 +08:00
|
|
|
Elf_Sym dynsym =
|
2026-08-26 18:17:28 +02:00
|
|
|
{
|
|
|
|
|
0
|
|
|
|
|
};
|
2022-09-26 16:22:03 +10:00
|
|
|
|
libs/libc/elf: Translate link-time addresses through one place.
The ET_DYN path computes run-time addresses from link-time ones in five
places, each open-coding the arithmetic, and two of them disagree about
how: libelf_relocatedyn() adds textalloc to a relocation's r_offset in
one branch and subtracts datasec before adding datastart in the next,
while the value translation a few lines further down picks between those
two forms with an explicit test on datasec.
Collect that into libelf_addr(), which makes the test once: an address
below the data segment's link-time base belongs to text, anything at or
above it to data.
This changes nothing today. libelf_elfsize() sets
segpad = datasec - (text_vaddr + textsize)
and libelf_load() then places
datastart = textalloc + textsize + segpad
so datastart - datasec is textalloc, and the data branch reduces to
textalloc + vaddr -- exactly what the text branch returns, and exactly
what adding a single load bias did before. The two forms are the same
arithmetic written twice.
They stop being the same once text and data are placed independently,
which is what an FDPIC object requires: its two PT_LOAD segments are
relocated separately so that the read-only one can be mapped in place on
the media while only the writable one is copied. Having the translation
in one function is what makes that possible without auditing every
open-coded expression again.
Built for mps3-an547:picostest, which is CONFIG_ELF with CONFIG_PIC, and
boots identically to the same configuration without this change.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 00:49:15 +02:00
|
|
|
addr = libelf_addr(loadinfo, rel->r_offset);
|
2022-09-26 16:22:03 +10:00
|
|
|
|
2023-09-04 10:37:48 +10:00
|
|
|
if (reldata.relrela[idx_rel] == 1)
|
|
|
|
|
{
|
|
|
|
|
addr += rela->r_addend;
|
|
|
|
|
}
|
|
|
|
|
|
libs/libc/elf: Translate link-time addresses through one place.
The ET_DYN path computes run-time addresses from link-time ones in five
places, each open-coding the arithmetic, and two of them disagree about
how: libelf_relocatedyn() adds textalloc to a relocation's r_offset in
one branch and subtracts datasec before adding datastart in the next,
while the value translation a few lines further down picks between those
two forms with an explicit test on datasec.
Collect that into libelf_addr(), which makes the test once: an address
below the data segment's link-time base belongs to text, anything at or
above it to data.
This changes nothing today. libelf_elfsize() sets
segpad = datasec - (text_vaddr + textsize)
and libelf_load() then places
datastart = textalloc + textsize + segpad
so datastart - datasec is textalloc, and the data branch reduces to
textalloc + vaddr -- exactly what the text branch returns, and exactly
what adding a single load bias did before. The two forms are the same
arithmetic written twice.
They stop being the same once text and data are placed independently,
which is what an FDPIC object requires: its two PT_LOAD segments are
relocated separately so that the read-only one can be mapped in place on
the media while only the writable one is copied. Having the translation
in one function is what makes that possible without auditing every
open-coded expression again.
Built for mps3-an547:picostest, which is CONFIG_ELF with CONFIG_PIC, and
boots identically to the same configuration without this change.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 00:49:15 +02:00
|
|
|
dynsym.st_value = libelf_addr(loadinfo,
|
|
|
|
|
*(FAR uint32_t *)addr);
|
2023-09-15 21:59:06 +03:00
|
|
|
|
riscv/arch_elf.c: Handle PCREL_HI20/LO12_I/S relocations correctly
There is a problem with the current elf loader for risc-v: when a pair of
PCREL_HI20 / LO12 relocations are encountered, it is assumed that these
will follow each other immediately, as follows:
label:
auipc a0, %pcrel_hi(symbol) // R_RISCV_PCREL_HI20
load/store a0, %pcrel_lo(label)(a0) // R_RISCV_PCREL_LO12_I/S
With this assumption, the hi/lo relocations are both done when a hi20
relocation entry is encountered, first to the current instruction (addr)
and to the next instruction (addr + 4).
However, this assumption is wrong. There is nothing in the elf relocation
specification[1] that mandates this. Thus, the hi/lo relocation always
needs to first fixup the hi-part, and when the lo-part is encountered, it
needs to find the corresponding hi relocation entry, via the given "label".
This necessitates (re-)visiting the relocation entries for the current
section as well as looking for "label" in the symbol table.
The NuttX elf loader does not allow such operations to be done in the
machine specific part, so this patch fixes the relocation issue by
introducing an architecture specific cache for the hi20 relocation and
symbol table entries. When a lo12 relocation is encountered, the cache
can be consulted to find the hi20 part.
[1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
2023-12-05 12:30:46 +02:00
|
|
|
ret = up_relocate(rel, &dynsym, addr, ARCH_ELFDATA_PARM);
|
2022-09-26 16:22:03 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
berr("ERROR: Section %d reloc %d: Relocation failed: %d\n",
|
|
|
|
|
relidx, i, ret);
|
|
|
|
|
lib_free(sym);
|
|
|
|
|
lib_free(rels);
|
|
|
|
|
lib_free(dyn);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
libs/libc/machine/arm: Relocate FDPIC function descriptors.
A function pointer under FDPIC is not a code address. Because each
PT_LOAD segment is placed independently, a pointer has to carry the data
base its callee will need, so it is a two-word descriptor: the entry
point, and the base to install in the PIC register before branching.
R_ARM_FUNCDESC_VALUE says "the thing you are patching is such a
descriptor", and R_ARM_FUNCDESC says "manufacture one and give me its
address".
Both need state a relocation cannot carry. A descriptor's second word is
the *object's* data base, from DT_PLTGOT, and R_ARM_FUNCDESC carves
descriptors from a pool whose cursor has to survive from one relocation
to the next. up_relocate() is handed only a relocation, a resolved
symbol and an address to patch.
arch_data is the existing channel for exactly this -- RISC-V already uses
it to remember a HI20 relocation while its LO12 partner is processed --
but nothing has ever put loader state into it: it is declared zeroed and
written only by up_relocate() itself. So ARCH_ELFDATA_INIT and
ARCH_ELFDATA_FINI are added, seeding the block from the loadinfo before
the relocation loop and reading the cursor back after. Both default to
nothing, so an architecture that does not define them is unaffected, and
RISC-V's use of arch_data is untouched. libelf_relocatedyn() walks both
dynamic tables under one arch_data, so the cursor spans the whole object.
The addend handling is the part that is easy to get wrong. REL format
keeps the addend in place, in the word about to become the entry point,
and a pointer to a static function is referenced through its *section*
symbol -- the value is the section base and the offset, including the
Thumb bit, is entirely in the addend. Dropping it yields an even address
and the core faults trying to execute it as ARM code.
The GOT written into a descriptor is the loading object's own, even for
an imported function, which is what makes a callback work: when the base
firmware's qsort() calls back into a module's comparison function, the
module needs its own data base in the PIC register.
libelf_relocatedyn()'s imported-symbol path needed a change to suit. It
stores the resolved address directly and never calls up_relocate(), which
cannot produce a two-word descriptor, so under FDPIC the resolved value
now goes through up_relocate() and the relocation type decides what to
write.
Implemented for armv7-m and armv8-m, the profiles FDPIC targets; the
other ARM variants gain the arch_data block but no new relocations.
Built and booted mps3-an547:picostest and lm3s6965-ek:qemu-nxflat, the
ELF PIC and NXFLAT users of this code, both unchanged.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 08:58:41 +02:00
|
|
|
/* Hand back what the relocations consumed. The error paths above do
|
|
|
|
|
* not bother: the load is being abandoned, so the cursor has no reader.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
ARCH_ELFDATA_TEARDOWN(loadinfo);
|
|
|
|
|
|
2022-09-26 16:22:03 +10:00
|
|
|
lib_free(sym);
|
|
|
|
|
lib_free(rels);
|
|
|
|
|
lib_free(dyn);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-10 09:53:31 -06:00
|
|
|
/****************************************************************************
|
|
|
|
|
* Public Functions
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
2025-04-10 09:51:25 +08:00
|
|
|
* Name: libelf_bind
|
2015-12-10 09:53:31 -06:00
|
|
|
*
|
|
|
|
|
* Description:
|
|
|
|
|
* Bind the imported symbol names in the loaded module described by
|
2020-03-10 14:52:58 +09:00
|
|
|
* 'loadinfo' using the exported symbol values provided by
|
2025-04-10 09:51:25 +08:00
|
|
|
* libelf_setsymtab().
|
2015-12-10 09:53:31 -06:00
|
|
|
*
|
2017-01-27 11:43:27 -06:00
|
|
|
* Input Parameters:
|
|
|
|
|
* modp - Module state information
|
|
|
|
|
* loadinfo - Load state information
|
2024-07-09 23:27:59 +08:00
|
|
|
* exports - The table of exported symbols
|
|
|
|
|
* nexports - The number of symbols in the exports table
|
2017-01-27 11:43:27 -06:00
|
|
|
*
|
2015-12-10 09:53:31 -06:00
|
|
|
* Returned Value:
|
|
|
|
|
* 0 (OK) is returned on success and a negated errno is returned on
|
|
|
|
|
* failure.
|
|
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
int libelf_bind(FAR struct module_s *modp,
|
2024-07-09 23:27:59 +08:00
|
|
|
FAR struct mod_loadinfo_s *loadinfo,
|
|
|
|
|
FAR const struct symtab_s *exports, int nexports)
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
int i;
|
|
|
|
|
|
2024-06-30 16:49:13 +08:00
|
|
|
#ifdef CONFIG_ARCH_ADDRENV
|
|
|
|
|
/* If CONFIG_ARCH_ADDRENV=y, then the loaded ELF lies in a virtual address
|
2025-04-10 09:51:25 +08:00
|
|
|
* space that may not be in place now. libelf_addrenv_select() will
|
2024-06-30 16:49:13 +08:00
|
|
|
* temporarily instantiate that address space.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (loadinfo->addrenv != NULL)
|
|
|
|
|
{
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_addrenv_select(loadinfo);
|
2024-06-30 16:49:13 +08:00
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2025-04-10 09:51:25 +08:00
|
|
|
berr("ERROR: libelf_addrenv_select() failed: %d\n", ret);
|
2024-06-30 16:49:13 +08:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-12-10 09:53:31 -06:00
|
|
|
/* Find the symbol and string tables */
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_findsymtab(loadinfo);
|
2015-12-10 09:53:31 -06:00
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2025-02-13 09:28:08 +02:00
|
|
|
goto errout_with_addrenv;
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Process relocations in every allocated section */
|
|
|
|
|
|
|
|
|
|
for (i = 1; i < loadinfo->ehdr.e_shnum; i++)
|
|
|
|
|
{
|
|
|
|
|
/* Get the index to the relocation section */
|
|
|
|
|
|
|
|
|
|
int infosec = loadinfo->shdr[i].sh_info;
|
2026-08-26 18:17:28 +02:00
|
|
|
|
2015-12-10 09:53:31 -06:00
|
|
|
if (infosec >= loadinfo->ehdr.e_shnum)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 16:22:03 +10:00
|
|
|
if (loadinfo->ehdr.e_type == ET_DYN)
|
2015-12-10 09:53:31 -06:00
|
|
|
{
|
2023-09-04 10:37:48 +10:00
|
|
|
modp->dynamic = 1;
|
2022-09-26 16:22:03 +10:00
|
|
|
switch (loadinfo->shdr[i].sh_type)
|
|
|
|
|
{
|
2023-09-15 21:59:06 +03:00
|
|
|
case SHT_DYNAMIC:
|
libs/libc/elf: Fix two ways an FDPIC module failed to relocate.
Running one for the first time turned up two holes in the ET_DYN path.
Neither shows up in a build.
An undefined symbol is resolved with libelf_findglobal(), which searches
only the table of globally registered symbols. The export table that
exec() hands its caller went no further than the ET_REL path, so an
ET_DYN module could not import anything the caller supplied. Invisible
while such modules resolved everything internally; an FDPIC module
imports its libc, and every import failed with "Unable to resolve addr of
ext ref printf" although the caller had passed a table containing printf.
The export table is now threaded into libelf_relocatedyn() and consulted
when the global table has no answer, leaving the existing lookup order
intact.
A relocation naming a symbol defined inside the object was dropped
silently. The code handles a relocation with no symbol, and one against
an undefined symbol, but a defined symbol fell through both. That was
harmless while every dynamic relocation arriving here had symbol index
zero, which is the case for R_ARM_RELATIVE. FDPIC brings the first ones
that do not: a pointer to a static function is emitted against the
*section* symbol, so the value is the section base and the offset within
it -- including the Thumb bit -- is carried as the addend. Deriving a
value from the word being patched, as the no-symbol case does, would
translate that addend as though it were an address. Confirmed against a
real module: .text at 0x23c plus an addend of 0x95 gives 0x2d1, which is
the function with its Thumb bit.
Also stop libelf_symname() reporting a nameless symbol as an error. A
section symbol has no name, and libelf_findsymbol() walks the whole table
looking for optional entries such as nx_stacksize, so it meets these
routinely and checks for -ESRCH itself. At error level it printed ten or
more lines per module load and buried the diagnostics that matter.
Built and run on lm3s6965-ek with the examples/elf ROMFS. The ET_REL
test modules load as before, and an FDPIC module now loads, relocates,
resolves printf and puts from the table exec() supplied, and calls
through a function descriptor of its own.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 11:28:49 +02:00
|
|
|
ret = libelf_relocatedyn(modp, loadinfo, i,
|
|
|
|
|
exports, nexports);
|
2023-09-15 21:59:06 +03:00
|
|
|
break;
|
|
|
|
|
case SHT_DYNSYM:
|
|
|
|
|
loadinfo->dsymtabidx = i;
|
|
|
|
|
break;
|
|
|
|
|
case SHT_INIT_ARRAY:
|
libs/libc/elf: Translate link-time addresses through one place.
The ET_DYN path computes run-time addresses from link-time ones in five
places, each open-coding the arithmetic, and two of them disagree about
how: libelf_relocatedyn() adds textalloc to a relocation's r_offset in
one branch and subtracts datasec before adding datastart in the next,
while the value translation a few lines further down picks between those
two forms with an explicit test on datasec.
Collect that into libelf_addr(), which makes the test once: an address
below the data segment's link-time base belongs to text, anything at or
above it to data.
This changes nothing today. libelf_elfsize() sets
segpad = datasec - (text_vaddr + textsize)
and libelf_load() then places
datastart = textalloc + textsize + segpad
so datastart - datasec is textalloc, and the data branch reduces to
textalloc + vaddr -- exactly what the text branch returns, and exactly
what adding a single load bias did before. The two forms are the same
arithmetic written twice.
They stop being the same once text and data are placed independently,
which is what an FDPIC object requires: its two PT_LOAD segments are
relocated separately so that the read-only one can be mapped in place on
the media while only the writable one is copied. Having the translation
in one function is what makes that possible without auditing every
open-coded expression again.
Built for mps3-an547:picostest, which is CONFIG_ELF with CONFIG_PIC, and
boots identically to the same configuration without this change.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 00:49:15 +02:00
|
|
|
loadinfo->initarr = libelf_addr(loadinfo,
|
|
|
|
|
loadinfo->shdr[i].sh_addr);
|
2023-09-15 21:59:06 +03:00
|
|
|
loadinfo->ninit = loadinfo->shdr[i].sh_size /
|
|
|
|
|
sizeof(uintptr_t);
|
|
|
|
|
break;
|
|
|
|
|
case SHT_FINI_ARRAY:
|
libs/libc/elf: Translate link-time addresses through one place.
The ET_DYN path computes run-time addresses from link-time ones in five
places, each open-coding the arithmetic, and two of them disagree about
how: libelf_relocatedyn() adds textalloc to a relocation's r_offset in
one branch and subtracts datasec before adding datastart in the next,
while the value translation a few lines further down picks between those
two forms with an explicit test on datasec.
Collect that into libelf_addr(), which makes the test once: an address
below the data segment's link-time base belongs to text, anything at or
above it to data.
This changes nothing today. libelf_elfsize() sets
segpad = datasec - (text_vaddr + textsize)
and libelf_load() then places
datastart = textalloc + textsize + segpad
so datastart - datasec is textalloc, and the data branch reduces to
textalloc + vaddr -- exactly what the text branch returns, and exactly
what adding a single load bias did before. The two forms are the same
arithmetic written twice.
They stop being the same once text and data are placed independently,
which is what an FDPIC object requires: its two PT_LOAD segments are
relocated separately so that the read-only one can be mapped in place on
the media while only the writable one is copied. Having the translation
in one function is what makes that possible without auditing every
open-coded expression again.
Built for mps3-an547:picostest, which is CONFIG_ELF with CONFIG_PIC, and
boots identically to the same configuration without this change.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 00:49:15 +02:00
|
|
|
loadinfo->finiarr = libelf_addr(loadinfo,
|
|
|
|
|
loadinfo->shdr[i].sh_addr);
|
2023-09-15 21:59:06 +03:00
|
|
|
loadinfo->nfini = loadinfo->shdr[i].sh_size /
|
|
|
|
|
sizeof(uintptr_t);
|
|
|
|
|
break;
|
|
|
|
|
case SHT_PREINIT_ARRAY:
|
libs/libc/elf: Translate link-time addresses through one place.
The ET_DYN path computes run-time addresses from link-time ones in five
places, each open-coding the arithmetic, and two of them disagree about
how: libelf_relocatedyn() adds textalloc to a relocation's r_offset in
one branch and subtracts datasec before adding datastart in the next,
while the value translation a few lines further down picks between those
two forms with an explicit test on datasec.
Collect that into libelf_addr(), which makes the test once: an address
below the data segment's link-time base belongs to text, anything at or
above it to data.
This changes nothing today. libelf_elfsize() sets
segpad = datasec - (text_vaddr + textsize)
and libelf_load() then places
datastart = textalloc + textsize + segpad
so datastart - datasec is textalloc, and the data branch reduces to
textalloc + vaddr -- exactly what the text branch returns, and exactly
what adding a single load bias did before. The two forms are the same
arithmetic written twice.
They stop being the same once text and data are placed independently,
which is what an FDPIC object requires: its two PT_LOAD segments are
relocated separately so that the read-only one can be mapped in place on
the media while only the writable one is copied. Having the translation
in one function is what makes that possible without auditing every
open-coded expression again.
Built for mps3-an547:picostest, which is CONFIG_ELF with CONFIG_PIC, and
boots identically to the same configuration without this change.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-03 00:49:15 +02:00
|
|
|
loadinfo->preiarr = libelf_addr(loadinfo,
|
|
|
|
|
loadinfo->shdr[i].sh_addr);
|
2023-09-15 21:59:06 +03:00
|
|
|
loadinfo->nprei = loadinfo->shdr[i].sh_size /
|
|
|
|
|
sizeof(uintptr_t);
|
|
|
|
|
break;
|
2022-09-26 16:22:03 +10:00
|
|
|
}
|
2024-07-09 23:27:59 +08:00
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2025-02-13 09:28:08 +02:00
|
|
|
goto errout_with_addrenv;
|
2024-07-09 23:27:59 +08:00
|
|
|
}
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
2022-09-26 16:22:03 +10:00
|
|
|
else
|
|
|
|
|
{
|
2023-09-04 10:37:48 +10:00
|
|
|
modp->dynamic = 0;
|
|
|
|
|
|
|
|
|
|
/* Make sure that the section is allocated. We can't
|
|
|
|
|
* relocate sections that were not loaded into memory.
|
2022-09-26 16:22:03 +10:00
|
|
|
*/
|
2015-12-10 09:53:31 -06:00
|
|
|
|
2024-04-28 18:16:07 +08:00
|
|
|
if ((loadinfo->shdr[i].sh_flags & SHF_ALLOC) == 0 &&
|
|
|
|
|
(loadinfo->shdr[i].sh_flags & SHF_INFO_LINK) == 0)
|
2022-09-26 16:22:03 +10:00
|
|
|
{
|
2023-09-15 21:59:06 +03:00
|
|
|
continue;
|
2022-09-26 16:22:03 +10:00
|
|
|
}
|
2015-12-10 09:53:31 -06:00
|
|
|
|
2022-09-26 16:22:03 +10:00
|
|
|
/* Process the relocations by type */
|
|
|
|
|
|
|
|
|
|
switch (loadinfo->shdr[i].sh_type)
|
|
|
|
|
{
|
2023-09-15 21:59:06 +03:00
|
|
|
case SHT_REL:
|
2024-07-09 23:27:59 +08:00
|
|
|
if ((loadinfo->shdr[infosec].sh_flags & SHF_ALLOC) == 0)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_relocate(modp, loadinfo, i, exports, nexports);
|
2023-07-03 00:11:02 +08:00
|
|
|
break;
|
2023-09-15 21:59:06 +03:00
|
|
|
case SHT_RELA:
|
2024-07-09 23:27:59 +08:00
|
|
|
if ((loadinfo->shdr[infosec].sh_flags & SHF_ALLOC) == 0)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 09:51:25 +08:00
|
|
|
ret = libelf_relocateadd(modp, loadinfo, i, exports,
|
2024-07-09 23:27:59 +08:00
|
|
|
nexports);
|
2023-07-03 00:11:02 +08:00
|
|
|
break;
|
2024-04-28 18:16:07 +08:00
|
|
|
case SHT_INIT_ARRAY:
|
|
|
|
|
loadinfo->initarr = loadinfo->shdr[i].sh_addr;
|
|
|
|
|
loadinfo->ninit = loadinfo->shdr[i].sh_size /
|
|
|
|
|
sizeof(uintptr_t);
|
|
|
|
|
break;
|
|
|
|
|
case SHT_FINI_ARRAY:
|
|
|
|
|
loadinfo->finiarr = loadinfo->shdr[i].sh_addr;
|
|
|
|
|
loadinfo->nfini = loadinfo->shdr[i].sh_size /
|
|
|
|
|
sizeof(uintptr_t);
|
|
|
|
|
break;
|
2022-09-26 16:22:03 +10:00
|
|
|
}
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2025-02-13 09:28:08 +02:00
|
|
|
goto errout_with_addrenv;
|
2015-12-10 09:53:31 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-03 19:45:27 +08:00
|
|
|
modp->xipbase = loadinfo->xipbase;
|
|
|
|
|
|
2015-12-10 09:53:31 -06:00
|
|
|
/* Ensure that the I and D caches are coherent before starting the newly
|
|
|
|
|
* loaded module by cleaning the D cache (i.e., flushing the D cache
|
|
|
|
|
* contents to memory and invalidating the I cache).
|
|
|
|
|
*/
|
|
|
|
|
|
2023-11-29 22:25:47 +08:00
|
|
|
if (loadinfo->textsize > 0)
|
|
|
|
|
{
|
|
|
|
|
up_coherent_dcache(loadinfo->textalloc, loadinfo->textsize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (loadinfo->datasize > 0)
|
|
|
|
|
{
|
|
|
|
|
up_coherent_dcache(loadinfo->datastart, loadinfo->datasize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_ARCH_USE_SEPARATED_SECTION
|
|
|
|
|
for (i = 0; loadinfo->ehdr.e_type == ET_REL && i < loadinfo->ehdr.e_shnum;
|
|
|
|
|
i++)
|
|
|
|
|
{
|
|
|
|
|
if (loadinfo->sectalloc[i] == 0)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
up_coherent_dcache(loadinfo->sectalloc[i], loadinfo->shdr[i].sh_size);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2015-12-10 09:53:31 -06:00
|
|
|
|
2025-02-13 09:28:08 +02:00
|
|
|
errout_with_addrenv:
|
|
|
|
|
|
2024-06-30 16:49:13 +08:00
|
|
|
#ifdef CONFIG_ARCH_ADDRENV
|
|
|
|
|
if (loadinfo->addrenv != NULL)
|
|
|
|
|
{
|
2025-04-10 09:51:25 +08:00
|
|
|
int status = libelf_addrenv_restore(loadinfo);
|
2026-08-26 18:17:28 +02:00
|
|
|
|
2024-06-30 16:49:13 +08:00
|
|
|
if (status < 0)
|
|
|
|
|
{
|
2025-04-10 09:51:25 +08:00
|
|
|
berr("ERROR: libelf_addrenv_restore() failed: %d\n", status);
|
2024-06-30 16:49:13 +08:00
|
|
|
if (ret == OK)
|
|
|
|
|
{
|
|
|
|
|
ret = status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-12-10 09:53:31 -06:00
|
|
|
return ret;
|
|
|
|
|
}
|