996625ec58
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
974 lines
29 KiB
C
974 lines
29 KiB
C
/****************************************************************************
|
|
* libs/libc/modlib/modlib_bind.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/elf.h>
|
|
#include <nuttx/lib/modlib.h>
|
|
|
|
#include "libc.h"
|
|
#include "modlib/modlib.h"
|
|
|
|
/****************************************************************************
|
|
* 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 */
|
|
|
|
#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
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* REVISIT: This naming breaks the NuttX coding standard, but is consistent
|
|
* with legacy naming of other ELF types.
|
|
*/
|
|
|
|
typedef struct
|
|
{
|
|
dq_entry_t entry;
|
|
Elf_Sym sym;
|
|
int idx;
|
|
} Elf_SymCache;
|
|
|
|
struct
|
|
{
|
|
int stroff; /* offset to string table */
|
|
int symoff; /* offset to symbol table */
|
|
int lsymtab; /* size of symbol table */
|
|
int relentsz[2]; /* size of relocation entry */
|
|
int reloff[2]; /* offset to the relocation section */
|
|
int relsz[2]; /* size of relocation table */
|
|
int relrela[2]; /* type of relocation type - 0: DT_REL / 1: DT_RELA */
|
|
} reldata;
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: modlib_readrels
|
|
*
|
|
* Description:
|
|
* Read the (ELF_Rel structure * buffer count) into memory.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline int modlib_readrels(FAR struct mod_loadinfo_s *loadinfo,
|
|
FAR const Elf_Shdr *relsec,
|
|
int index, FAR Elf_Rel *rels,
|
|
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_Rel)))
|
|
{
|
|
berr("ERROR: Bad relocation symbol index: %d\n", index);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Get the file offset to the symbol table entry */
|
|
|
|
offset = sizeof(Elf_Rel) * index;
|
|
size = sizeof(Elf_Rel) * count;
|
|
if (offset + size > relsec->sh_size)
|
|
{
|
|
size = relsec->sh_size - offset;
|
|
}
|
|
|
|
/* And, finally, read the symbol table entry into memory */
|
|
|
|
return modlib_read(loadinfo, (FAR uint8_t *)rels, size,
|
|
relsec->sh_offset + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: modlib_readrelas
|
|
*
|
|
* Description:
|
|
* Read the (ELF_Rela structure * buffer count) into memory.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline int modlib_readrelas(FAR struct mod_loadinfo_s *loadinfo,
|
|
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 */
|
|
|
|
return modlib_read(loadinfo, (FAR uint8_t *)relas, size,
|
|
relsec->sh_offset + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: modlib_relocate and modlib_relocateadd
|
|
*
|
|
* Description:
|
|
* Perform all relocations associated with a section.
|
|
*
|
|
* Returned Value:
|
|
* 0 (OK) is returned on success and a negated errno is returned on
|
|
* failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int modlib_relocate(FAR struct module_s *modp,
|
|
FAR struct mod_loadinfo_s *loadinfo, int relidx)
|
|
{
|
|
FAR Elf_Shdr *relsec = &loadinfo->shdr[relidx];
|
|
FAR Elf_Shdr *dstsec = &loadinfo->shdr[relsec->sh_info];
|
|
FAR Elf_Rel *rels;
|
|
FAR Elf_Rel *rel;
|
|
FAR Elf_SymCache *cache;
|
|
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;
|
|
|
|
/* Define potential architecture specific elf data container */
|
|
|
|
ARCH_ELFDATA_DEF;
|
|
|
|
rels = lib_malloc(CONFIG_MODLIB_RELOCATION_BUFFERCOUNT * sizeof(Elf_Rel));
|
|
if (!rels)
|
|
{
|
|
berr("Failed to allocate memory for elf relocation rels\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_Rel); i++)
|
|
{
|
|
/* Read the relocation entry into memory */
|
|
|
|
rel = &rels[i % CONFIG_MODLIB_RELOCATION_BUFFERCOUNT];
|
|
|
|
if (!(i % CONFIG_MODLIB_RELOCATION_BUFFERCOUNT))
|
|
{
|
|
ret = modlib_readrels(loadinfo, relsec, i, rels,
|
|
CONFIG_MODLIB_RELOCATION_BUFFERCOUNT);
|
|
if (ret < 0)
|
|
{
|
|
berr("ERROR: Section %d reloc %d: "
|
|
"Failed to read relocation entry: %d\n",
|
|
relidx, i, ret);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Get the symbol table index for the relocation. This is contained
|
|
* in a bit-field within the r_info element.
|
|
*/
|
|
|
|
symidx = ELF_R_SYM(rel->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)
|
|
{
|
|
if (j < CONFIG_MODLIB_SYMBOL_CACHECOUNT)
|
|
{
|
|
cache = lib_malloc(sizeof(Elf_SymCache));
|
|
if (!cache)
|
|
{
|
|
berr("Failed to allocate memory for elf symbols\n");
|
|
ret = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
cache = (FAR Elf_SymCache *)dq_remlast(&q);
|
|
}
|
|
|
|
sym = &cache->sym;
|
|
|
|
/* Read the symbol table entry into memory */
|
|
|
|
ret = modlib_readsym(loadinfo, symidx, sym,
|
|
&loadinfo->shdr[loadinfo->symtabidx]);
|
|
if (ret < 0)
|
|
{
|
|
berr("ERROR: Section %d reloc %d: "
|
|
"Failed to read symbol[%d]: %d\n",
|
|
relidx, i, symidx, ret);
|
|
lib_free(cache);
|
|
break;
|
|
}
|
|
|
|
/* Get the value of the symbol (in sym.st_value) */
|
|
|
|
ret = modlib_symvalue(modp, loadinfo, sym,
|
|
loadinfo->shdr[loadinfo->strtabidx].sh_offset);
|
|
if (ret < 0)
|
|
{
|
|
/* The special error -ESRCH is returned only in one condition:
|
|
* The symbol has no name.
|
|
*
|
|
* 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)
|
|
{
|
|
berr("ERROR: Section %d reloc %d: "
|
|
"Undefined symbol[%d] has no name: %d\n",
|
|
relidx, i, symidx, ret);
|
|
}
|
|
else
|
|
{
|
|
berr("ERROR: Section %d reloc %d: "
|
|
"Failed to get value of symbol[%d]: %d\n",
|
|
relidx, i, symidx, ret);
|
|
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. */
|
|
|
|
if (rel->r_offset + sizeof(uint32_t) > 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;
|
|
|
|
/* Now perform the architecture-specific relocation */
|
|
|
|
ret = up_relocate(rel, sym, addr, ARCH_ELFDATA_PARM);
|
|
if (ret < 0)
|
|
{
|
|
berr("ERROR: Section %d reloc %d: Relocation failed: %d\n",
|
|
relidx, i, ret);
|
|
break;
|
|
}
|
|
}
|
|
|
|
lib_free(rels);
|
|
while ((e = dq_peek(&q)) != NULL)
|
|
{
|
|
dq_rem(e, &q);
|
|
lib_free(e);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int modlib_relocateadd(FAR struct module_s *modp,
|
|
FAR struct mod_loadinfo_s *loadinfo,
|
|
int relidx)
|
|
{
|
|
FAR Elf_Shdr *relsec = &loadinfo->shdr[relidx];
|
|
FAR Elf_Shdr *dstsec = &loadinfo->shdr[relsec->sh_info];
|
|
FAR Elf_Rela *relas;
|
|
FAR Elf_Rela *rela;
|
|
FAR Elf_SymCache *cache;
|
|
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;
|
|
|
|
/* Define potential architecture specific elf data container */
|
|
|
|
ARCH_ELFDATA_DEF;
|
|
|
|
relas = lib_malloc(CONFIG_MODLIB_RELOCATION_BUFFERCOUNT *
|
|
sizeof(Elf_Rela));
|
|
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 */
|
|
|
|
rela = &relas[i % CONFIG_MODLIB_RELOCATION_BUFFERCOUNT];
|
|
|
|
if (!(i % CONFIG_MODLIB_RELOCATION_BUFFERCOUNT))
|
|
{
|
|
ret = modlib_readrelas(loadinfo, relsec, i, relas,
|
|
CONFIG_MODLIB_RELOCATION_BUFFERCOUNT);
|
|
if (ret < 0)
|
|
{
|
|
berr("ERROR: Section %d reloc %d: "
|
|
"Failed to read relocation entry: %d\n",
|
|
relidx, i, ret);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
if (j < CONFIG_MODLIB_SYMBOL_CACHECOUNT)
|
|
{
|
|
cache = lib_malloc(sizeof(Elf_SymCache));
|
|
if (!cache)
|
|
{
|
|
berr("Failed to allocate memory for elf symbols\n");
|
|
ret = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
cache = (FAR Elf_SymCache *)dq_remlast(&q);
|
|
}
|
|
|
|
sym = &cache->sym;
|
|
|
|
/* Read the symbol table entry into memory */
|
|
|
|
ret = modlib_readsym(loadinfo, symidx, sym,
|
|
&loadinfo->shdr[loadinfo->symtabidx]);
|
|
if (ret < 0)
|
|
{
|
|
berr("ERROR: Section %d reloc %d: "
|
|
"Failed to read symbol[%d]: %d\n",
|
|
relidx, i, symidx, ret);
|
|
lib_free(cache);
|
|
break;
|
|
}
|
|
|
|
/* Get the value of the symbol (in sym.st_value) */
|
|
|
|
ret = modlib_symvalue(modp, loadinfo, sym,
|
|
loadinfo->shdr[loadinfo->strtabidx].sh_offset);
|
|
if (ret < 0)
|
|
{
|
|
/* The special error -ESRCH is returned only in one condition:
|
|
* The symbol has no name.
|
|
*
|
|
* 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)
|
|
{
|
|
berr("ERROR: Section %d reloc %d: "
|
|
"Undefined symbol[%d] has no name: %d\n",
|
|
relidx, i, symidx, ret);
|
|
}
|
|
else
|
|
{
|
|
berr("ERROR: Section %d reloc %d: "
|
|
"Failed to get value of symbol[%d]: %d\n",
|
|
relidx, i, symidx, ret);
|
|
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. */
|
|
|
|
if (rela->r_offset + sizeof(uint32_t) > dstsec->sh_size)
|
|
{
|
|
berr("ERROR: Section %d reloc %d: "
|
|
"Relocation address out of range, "
|
|
"offset %" PRIuPTR " size %ju\n",
|
|
relidx, i, (uintptr_t)rela->r_offset,
|
|
(uintmax_t)dstsec->sh_size);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
addr = dstsec->sh_addr + rela->r_offset;
|
|
|
|
/* Now perform the architecture-specific relocation */
|
|
|
|
ret = up_relocateadd(rela, sym, addr, ARCH_ELFDATA_PARM);
|
|
if (ret < 0)
|
|
{
|
|
berr("ERROR: Section %d reloc %d: Relocation failed: %d\n",
|
|
relidx, i, ret);
|
|
break;
|
|
}
|
|
}
|
|
|
|
lib_free(relas);
|
|
while ((e = dq_peek(&q)) != NULL)
|
|
{
|
|
dq_rem(e, &q);
|
|
lib_free(e);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: modlib_relocatedyn
|
|
*
|
|
* 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int modlib_relocatedyn(FAR struct module_s *modp,
|
|
FAR struct mod_loadinfo_s *loadinfo,
|
|
int relidx)
|
|
{
|
|
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;
|
|
FAR Elf_Rela *relas = NULL;
|
|
FAR Elf_Rela *rela;
|
|
FAR Elf_Sym *sym = NULL;
|
|
uintptr_t addr;
|
|
int ret;
|
|
int i;
|
|
int idx_rel;
|
|
int idx_sym;
|
|
|
|
/* Define potential architecture specific elf data container */
|
|
|
|
ARCH_ELFDATA_DEF;
|
|
|
|
dyn = lib_malloc(shdr->sh_size);
|
|
ret = modlib_read(loadinfo, (FAR uint8_t *)dyn, shdr->sh_size,
|
|
shdr->sh_offset);
|
|
if (ret < 0)
|
|
{
|
|
berr("Failed to read dynamic section header");
|
|
return ret;
|
|
}
|
|
|
|
/* Assume DT_RELA to get maximum size required */
|
|
|
|
rels = lib_malloc(CONFIG_MODLIB_RELOCATION_BUFFERCOUNT * sizeof(Elf_Rela));
|
|
if (!rels)
|
|
{
|
|
berr("Failed to allocate memory for elf relocation rels\n");
|
|
lib_free(dyn);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memset((void *)&reldata, 0, sizeof(reldata));
|
|
relas = (FAR Elf_Rela *)rels;
|
|
|
|
for (i = 0; dyn[i].d_tag != DT_NULL; i++)
|
|
{
|
|
switch (dyn[i].d_tag)
|
|
{
|
|
case DT_REL:
|
|
reldata.reloff[I_REL] = dyn[i].d_un.d_val;
|
|
break;
|
|
case DT_RELSZ:
|
|
reldata.relsz[I_REL] = dyn[i].d_un.d_val;
|
|
break;
|
|
case DT_RELENT:
|
|
reldata.relentsz[I_REL] = dyn[i].d_un.d_val;
|
|
break;
|
|
case DT_SYMTAB:
|
|
reldata.symoff = dyn[i].d_un.d_val;
|
|
break;
|
|
case DT_STRTAB:
|
|
reldata.stroff = dyn[i].d_un.d_val;
|
|
break;
|
|
case DT_JMPREL:
|
|
reldata.reloff[I_PLT] = dyn[i].d_un.d_val;
|
|
break;
|
|
case DT_PLTRELSZ:
|
|
reldata.relsz[I_PLT] = dyn[i].d_un.d_val;
|
|
break;
|
|
case DT_PLTREL:
|
|
if (dyn[i].d_un.d_val == DT_REL)
|
|
{
|
|
reldata.relentsz[I_PLT] = sizeof(Elf_Rel);
|
|
reldata.relrela[I_PLT] = 0;
|
|
}
|
|
else
|
|
{
|
|
reldata.relentsz[I_PLT] = sizeof(Elf_Rela);
|
|
reldata.relrela[I_PLT] = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
ret = modlib_read(loadinfo, (FAR uint8_t *)sym, symhdr->sh_size,
|
|
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;
|
|
}
|
|
|
|
reldata.lsymtab = reldata.stroff - reldata.symoff;
|
|
|
|
for (idx_rel = 0; idx_rel < N_RELS; idx_rel++)
|
|
{
|
|
int lrelent;
|
|
|
|
if ((reldata.relsz[idx_rel] == 0) || (reldata.reloff[idx_rel] == 0))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Examine each relocation in the .rel.* section. */
|
|
|
|
ret = OK;
|
|
lrelent = reldata.relsz[idx_rel] / reldata.relentsz[idx_rel];
|
|
|
|
for (i = 0; i < lrelent; i++)
|
|
{
|
|
/* 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
|
|
*/
|
|
|
|
if (reldata.relrela[idx_rel] == 0)
|
|
{
|
|
rel = &rels[i % CONFIG_MODLIB_RELOCATION_BUFFERCOUNT];
|
|
rela = (Elf_Rela *)rel; /* Just to keep the compiler happy */
|
|
}
|
|
else
|
|
{
|
|
rela = &relas[i % CONFIG_MODLIB_RELOCATION_BUFFERCOUNT];
|
|
rel = (Elf_Rel *)rela;
|
|
}
|
|
|
|
if (!(i % CONFIG_MODLIB_RELOCATION_BUFFERCOUNT))
|
|
{
|
|
size_t relsize = (sizeof(Elf_Rela) *
|
|
CONFIG_MODLIB_RELOCATION_BUFFERCOUNT);
|
|
|
|
if (reldata.relsz[idx_rel] < relsize)
|
|
{
|
|
relsize = reldata.relsz[idx_rel];
|
|
}
|
|
|
|
ret = modlib_read(loadinfo, (FAR uint8_t *)rels,
|
|
relsize,
|
|
reldata.reloff[idx_rel] +
|
|
i * sizeof(Elf_Rel));
|
|
|
|
if (ret < 0)
|
|
{
|
|
berr("ERROR: Section %d reloc %d:"
|
|
"Failed to read relocation entry: %d\n",
|
|
relidx, i, ret);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Calculate the relocation address. */
|
|
|
|
if (rel->r_offset < 0)
|
|
{
|
|
berr("ERROR: Section %d reloc %d:"
|
|
"Relocation address out of range, offset %u\n",
|
|
relidx, i, (int)rel->r_offset);
|
|
ret = -EINVAL;
|
|
lib_free(sym);
|
|
lib_free(rels);
|
|
lib_free(dyn);
|
|
return ret;
|
|
}
|
|
|
|
/* Now perform the architecture-specific relocation */
|
|
|
|
if ((idx_sym = ELF_R_SYM(rel->r_info)) != 0)
|
|
{
|
|
if (sym[idx_sym].st_shndx == SHN_UNDEF) /* We have an external reference */
|
|
{
|
|
FAR void *ep;
|
|
|
|
ep = modlib_findglobal(modp, loadinfo, symhdr,
|
|
&sym[idx_sym]);
|
|
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 = rel->r_offset + loadinfo->textalloc;
|
|
|
|
if (reldata.relrela[idx_rel] == 1)
|
|
{
|
|
addr += rela->r_addend;
|
|
}
|
|
|
|
*(FAR uintptr_t *)addr = (uintptr_t)ep;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Elf_Sym dynsym;
|
|
|
|
addr = rel->r_offset - loadinfo->datasec + loadinfo->datastart;
|
|
|
|
if (reldata.relrela[idx_rel] == 1)
|
|
{
|
|
addr += rela->r_addend;
|
|
}
|
|
|
|
if ((*(FAR uint32_t *)addr) < loadinfo->datasec)
|
|
{
|
|
dynsym.st_value = *(FAR uint32_t *)addr +
|
|
loadinfo->textalloc;
|
|
}
|
|
else
|
|
{
|
|
dynsym.st_value = *(FAR uint32_t *)addr -
|
|
loadinfo->datasec + loadinfo->datastart;
|
|
}
|
|
|
|
ret = up_relocate(rel, &dynsym, 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Iterate through the dynamic symbol table looking for global symbols
|
|
* to put in our own symbol table for use with dlgetsym()
|
|
*/
|
|
|
|
/* Relocate the entries in the table */
|
|
|
|
for (i = 0; i < symhdr->sh_size / sizeof(Elf_Sym); i++)
|
|
{
|
|
FAR Elf_Shdr *s = &loadinfo->shdr[sym[i].st_shndx];
|
|
|
|
if (sym[i].st_shndx != SHN_UNDEF)
|
|
{
|
|
if (s->sh_addr < loadinfo->datasec)
|
|
{
|
|
sym[i].st_value = sym[i].st_value + loadinfo->textalloc;
|
|
}
|
|
else
|
|
{
|
|
sym[i].st_value = sym[i].st_value -
|
|
loadinfo->datasec + loadinfo->datastart;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = modlib_insertsymtab(modp, loadinfo, symhdr, sym);
|
|
|
|
lib_free(sym);
|
|
lib_free(rels);
|
|
lib_free(dyn);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: modlib_bind
|
|
*
|
|
* Description:
|
|
* Bind the imported symbol names in the loaded module described by
|
|
* 'loadinfo' using the exported symbol values provided by
|
|
* modlib_setsymtab().
|
|
*
|
|
* Input Parameters:
|
|
* modp - Module state information
|
|
* loadinfo - Load state information
|
|
*
|
|
* Returned Value:
|
|
* 0 (OK) is returned on success and a negated errno is returned on
|
|
* failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int modlib_bind(FAR struct module_s *modp,
|
|
FAR struct mod_loadinfo_s *loadinfo)
|
|
{
|
|
int ret;
|
|
int i;
|
|
|
|
/* Find the symbol and string tables */
|
|
|
|
ret = modlib_findsymtab(loadinfo);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* 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;
|
|
if (infosec >= loadinfo->ehdr.e_shnum)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (loadinfo->ehdr.e_type == ET_DYN)
|
|
{
|
|
modp->dynamic = 1;
|
|
switch (loadinfo->shdr[i].sh_type)
|
|
{
|
|
case SHT_DYNAMIC:
|
|
ret = modlib_relocatedyn(modp, loadinfo, i);
|
|
break;
|
|
case SHT_DYNSYM:
|
|
loadinfo->dsymtabidx = i;
|
|
break;
|
|
case SHT_INIT_ARRAY:
|
|
loadinfo->initarr = loadinfo->shdr[i].sh_addr -
|
|
loadinfo->datasec +
|
|
loadinfo->datastart;
|
|
loadinfo->ninit = loadinfo->shdr[i].sh_size /
|
|
sizeof(uintptr_t);
|
|
break;
|
|
case SHT_FINI_ARRAY:
|
|
loadinfo->finiarr = loadinfo->shdr[i].sh_addr -
|
|
loadinfo->datasec +
|
|
loadinfo->datastart;
|
|
loadinfo->nfini = loadinfo->shdr[i].sh_size /
|
|
sizeof(uintptr_t);
|
|
break;
|
|
case SHT_PREINIT_ARRAY:
|
|
loadinfo->preiarr = loadinfo->shdr[i].sh_addr -
|
|
loadinfo->datasec +
|
|
loadinfo->datastart;
|
|
loadinfo->nprei = loadinfo->shdr[i].sh_size /
|
|
sizeof(uintptr_t);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
modp->dynamic = 0;
|
|
|
|
/* Make sure that the section is allocated. We can't
|
|
* relocate sections that were not loaded into memory.
|
|
*/
|
|
|
|
if ((loadinfo->shdr[infosec].sh_flags & SHF_ALLOC) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Process the relocations by type */
|
|
|
|
switch (loadinfo->shdr[i].sh_type)
|
|
{
|
|
case SHT_REL:
|
|
ret = modlib_relocate(modp, loadinfo, i);
|
|
break;
|
|
case SHT_RELA:
|
|
ret = modlib_relocateadd(modp, loadinfo, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* 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).
|
|
*/
|
|
|
|
up_coherent_dcache(loadinfo->textalloc, loadinfo->textsize);
|
|
up_coherent_dcache(loadinfo->datastart, loadinfo->datasize);
|
|
|
|
return ret;
|
|
}
|