walnux/libs/libc/elf/elf_symbols.c
chao an 52482219c8 libc/elf: rename modlib to libelf
Renaming "modlib" to "libelf" is more in line with the implementation content,
which makes it easier for individual developers to understand the capabilities of this module.

CONFIG_LIBC_MODLIB -> CONFIG_LIBC_ELF

Signed-off-by: chao an <anchao.archer@bytedance.com>
2025-04-11 09:43:22 +08:00

642 lines
18 KiB
C

/****************************************************************************
* libs/libc/elf/elf_symbols.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/symtab.h>
#include <nuttx/lib/elf.h>
#include "libc.h"
#include "elf/elf.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Return values search for exported modules */
#define SYM_NOT_FOUND 0
#define SYM_FOUND 1
/****************************************************************************
* Private Types
****************************************************************************/
struct mod_exportinfo_s
{
FAR const char *name; /* Symbol name to find */
FAR struct module_s *modp; /* The module that needs the symbol */
FAR const struct symtab_s *symbol; /* Symbol info returned (if found) */
};
struct eptable_s
{
FAR uint8_t *epname; /* Name of global symbol */
FAR void *epaddr; /* Address of global symbol */
};
/****************************************************************************
* Public Data
****************************************************************************/
extern struct eptable_s global_table[];
extern int nglobals;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: libelf_symname
*
* Description:
* Get the symbol name in loadinfo->iobuffer[].
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
* EINVAL - There is something inconsistent in the symbol table (should
* only happen if the file is corrupted).
* ESRCH - Symbol has no name
*
****************************************************************************/
static int libelf_symname(FAR struct mod_loadinfo_s *loadinfo,
FAR const Elf_Sym *sym, Elf_Off sh_offset)
{
FAR uint8_t *buffer;
off_t offset;
size_t readlen;
size_t bytesread;
int ret;
/* Get the file offset to the string that is the name of the symbol. The
* st_name member holds an offset into the file's symbol string table.
*/
if (sym->st_name == 0)
{
berr("ERROR: Symbol has no name\n");
return -ESRCH;
}
/* Allocate an I/O buffer. This buffer is used by mod_symname() to
* accumulate the variable length symbol name.
*/
ret = libelf_allocbuffer(loadinfo);
if (ret < 0)
{
berr("ERROR: libelf_allocbuffer failed: %d\n", ret);
return -ENOMEM;
}
offset = sh_offset + sym->st_name;
/* Loop until we get the entire symbol name into memory */
bytesread = 0;
for (; ; )
{
/* Get the number of bytes to read */
readlen = loadinfo->buflen - bytesread;
if (offset + readlen > loadinfo->filelen)
{
if (loadinfo->filelen <= offset)
{
berr("ERROR: At end of file\n");
return -EINVAL;
}
readlen = loadinfo->filelen - offset;
}
/* Read that number of bytes into the array */
buffer = &loadinfo->iobuffer[bytesread];
ret = libelf_read(loadinfo, buffer, readlen, offset);
if (ret < 0)
{
berr("ERROR: libelf_read failed: %d\n", ret);
return ret;
}
bytesread += readlen;
/* Did we read the NUL terminator? */
if (memchr(buffer, '\0', readlen) != NULL)
{
/* Yes, the buffer contains a NUL terminator. */
return OK;
}
/* No.. then we have to read more */
ret = libelf_reallocbuffer(loadinfo, CONFIG_LIBC_ELF_BUFFERINCR);
if (ret < 0)
{
berr("ERROR: mod_reallocbuffer failed: %d\n", ret);
return ret;
}
offset += CONFIG_LIBC_ELF_BUFFERINCR;
}
/* We will not get here */
return OK;
}
/****************************************************************************
* Name: libelf_symcallback
*
* Description:
* libelf_registry_foreach() callback function. Test if the provided
* module, modp, exports the symbol of interest. If so, return that symbol
* value and setup the module dependency relationship.
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
****************************************************************************/
static int libelf_symcallback(FAR struct module_s *modp, FAR void *arg)
{
FAR struct mod_exportinfo_s *exportinfo = (FAR struct mod_exportinfo_s *)
arg;
/* Check if this module exports a symbol of that name */
exportinfo->symbol = symtab_findbyname(modp->modinfo.exports,
exportinfo->name,
modp->modinfo.nexports);
if (exportinfo->symbol != NULL)
{
/* Yes.. save the dependency relationship and return SYM_FOUND to
* stop the traversal.
*/
#if CONFIG_LIBC_ELF_MAXDEPEND > 0
int ret = libelf_depend(exportinfo->modp, modp);
if (ret < 0)
{
berr("ERROR: libelf_depend failed: %d\n", ret);
return ret;
}
#endif
return SYM_FOUND;
}
return SYM_NOT_FOUND;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: libelf_findsymtab
*
* Description:
* Find the symbol table section.
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
****************************************************************************/
int libelf_findsymtab(FAR struct mod_loadinfo_s *loadinfo)
{
int i;
/* Find the symbol table section header and its associated string table */
for (i = 1; i < loadinfo->ehdr.e_shnum; i++)
{
if (loadinfo->shdr[i].sh_type == SHT_SYMTAB)
{
loadinfo->symtabidx = i;
loadinfo->strtabidx = loadinfo->shdr[i].sh_link;
break;
}
}
/* Verify that there is a symbol and string table */
if (loadinfo->symtabidx == 0)
{
berr("ERROR: No symbols in ELF file\n");
return -EINVAL;
}
return OK;
}
/****************************************************************************
* Name: libelf_readsym
*
* Description:
* Read the ELF symbol structure at the specified index into memory.
*
* Input Parameters:
* loadinfo - Load state information
* index - Symbol table index
* sym - Location to return the table entry
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
****************************************************************************/
int libelf_readsym(FAR struct mod_loadinfo_s *loadinfo, int index,
FAR Elf_Sym *sym, FAR Elf_Shdr *symtab)
{
off_t offset;
/* Verify that the symbol table index lies within symbol table */
if (index < 0 || index > (symtab->sh_size / sizeof(Elf_Sym)))
{
berr("ERROR: Bad relocation symbol index: %d\n", index);
return -EINVAL;
}
/* Get the file offset to the symbol table entry */
offset = symtab->sh_offset + sizeof(Elf_Sym) * index;
/* And, finally, read the symbol table entry into memory */
return libelf_read(loadinfo, (FAR uint8_t *)sym, sizeof(Elf_Sym), offset);
}
/****************************************************************************
* Name: libelf_symvalue
*
* Description:
* Get the value of a symbol. The updated value of the symbol is returned
* in the st_value field of the symbol table entry.
*
* Input Parameters:
* modp - Module state information
* loadinfo - Load state information
* sym - Symbol table entry (value might be undefined)
* sh_offset - Offset of strtab
* exports - Pointer to the symbol table
* nexports - Number of symbols in the symbol table*
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
* EINVAL - There is something inconsistent in the symbol table (should
* only happen if the file is corrupted).
* ENOSYS - Symbol lies in common
* ESRCH - Symbol has no name
* ENOENT - Symbol undefined and not provided via a symbol table
*
****************************************************************************/
int libelf_symvalue(FAR struct module_s *modp,
FAR struct mod_loadinfo_s *loadinfo, FAR Elf_Sym *sym,
Elf_Off sh_offset,
FAR const struct symtab_s *exports, int nexports)
{
FAR const struct symtab_s *symbol;
struct mod_exportinfo_s exportinfo;
uintptr_t secbase;
int ret;
switch (sym->st_shndx)
{
case SHN_COMMON:
{
/* NuttX ELF modules should be compiled with -fno-common. */
berr("ERROR: SHN_COMMON: Re-compile with -fno-common\n");
return -ENOSYS;
}
case SHN_ABS:
{
/* st_value already holds the correct value */
binfo("SHN_ABS: st_value=%08lx\n", (long)sym->st_value);
return OK;
}
case SHN_UNDEF:
{
/* Get the name of the undefined symbol */
ret = libelf_symname(loadinfo, sym, sh_offset);
if (ret < 0)
{
/* 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 return and special error to the caller to
* indicate the nameless symbol.
*/
berr("ERROR: SHN_UNDEF: Failed to get symbol name: %d\n", ret);
return ret;
}
/* First check if the symbol is exported by an installed module.
* Newest modules are installed at the head of the list. Therefore,
* if the symbol is exported by numerous modules, then the most
* recently installed will take precedence.
*/
exportinfo.name = (FAR const char *)loadinfo->iobuffer;
exportinfo.modp = modp;
exportinfo.symbol = NULL;
ret = libelf_registry_foreach(libelf_symcallback,
(FAR void *)&exportinfo);
if (ret < 0)
{
berr("ERROR: libelf_symcallback failed: %d\n", ret);
return ret;
}
symbol = exportinfo.symbol;
/* If the symbol is not exported by any module, then check if the
* base code exports a symbol of this name.
*/
if (symbol == NULL)
{
symbol = symtab_findbyname(exports, exportinfo.name,
nexports);
}
/* Was the symbol found from any exporter? */
if (symbol == NULL)
{
berr("ERROR: SHN_UNDEF: Exported symbol \"%s\" not found\n",
loadinfo->iobuffer);
return -ENOENT;
}
/* Yes... add the exported symbol value to the ELF symbol tablei
* entry
*/
binfo("SHN_UNDEF: name=%s "
"%08" PRIxPTR "+%08" PRIxPTR "=%08" PRIxPTR "\n",
loadinfo->iobuffer,
(uintptr_t)sym->st_value, (uintptr_t)symbol->sym_value,
(uintptr_t)(sym->st_value + (uintptr_t)symbol->sym_value));
sym->st_value += ((uintptr_t)symbol->sym_value);
}
break;
default:
{
secbase = loadinfo->shdr[sym->st_shndx].sh_addr;
binfo("Other[%d]: %08" PRIxPTR "+%08" PRIxPTR "=%08" PRIxPTR "\n",
sym->st_shndx,
(uintptr_t)sym->st_value, secbase,
(uintptr_t)(sym->st_value + secbase));
sym->st_value += secbase;
if (loadinfo->gotindex >= 0)
{
sym->st_value -= loadinfo->shdr[sym->st_shndx].sh_offset;
}
}
break;
}
return OK;
}
/****************************************************************************
* Name: libelf_insertsymtab
*
* Description:
* Insert a symbol into the modules exportinfo array.
*
* Input Parameters:
* modp - Module state information
* loadinfo - Load state information
* shdr - Symbol table section header
* sym - Symbol table entry
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
* EINVAL - There is something inconsistent in the symbol table
* (should only happen if the file is corrupted).
*
****************************************************************************/
int libelf_insertsymtab(FAR struct module_s *modp,
struct mod_loadinfo_s *loadinfo,
FAR Elf_Shdr *shdr, FAR Elf_Sym *sym)
{
FAR struct symtab_s *symbol;
FAR Elf_Shdr *strtab = &loadinfo->shdr[shdr->sh_link];
int ret = 0;
int i;
int j;
int nsym;
int symcount;
if (modp->modinfo.exports != NULL)
{
bwarn("Module export information already present - replacing");
libelf_freesymtab((FAR void *)modp);
}
/* Count the "live" symbols */
nsym = shdr->sh_size / sizeof(Elf_Sym);
for (i = 0, symcount = 0; i < nsym; i++)
{
if (sym[i].st_name != 0 &&
ELF_ST_BIND(sym[i].st_info) == STB_GLOBAL &&
ELF_ST_TYPE(sym[i].st_info) != STT_NOTYPE &&
ELF_ST_VISIBILITY(sym[i].st_other) == STV_DEFAULT)
{
symcount++;
}
}
if (symcount > 0)
{
modp->modinfo.exports = symbol =
loadinfo->exported =
lib_malloc(sizeof(*symbol) * symcount);
if (modp->modinfo.exports)
{
/* Build out module's symbol table */
modp->modinfo.nexports = symcount;
for (i = 0, j = 0; i < nsym; i++)
{
if (sym[i].st_name != 0 &&
ELF_ST_BIND(sym[i].st_info) == STB_GLOBAL &&
ELF_ST_TYPE(sym[i].st_info) != STT_NOTYPE &&
ELF_ST_VISIBILITY(sym[i].st_other) == STV_DEFAULT)
{
ret = libelf_symname(loadinfo, &sym[i], strtab->sh_offset);
if (ret < 0)
{
lib_free((FAR void *)modp->modinfo.exports);
modp->modinfo.exports = NULL;
return ret;
}
symbol[j].sym_name =
strdup((FAR char *)loadinfo->iobuffer);
symbol[j].sym_value =
(FAR const void *)(uintptr_t)sym[i].st_value;
j++;
}
}
#ifdef CONFIG_SYMTAB_ORDEREDBYNAME
symtab_sortbyname(symbol, symcount);
#endif
}
else
{
berr("Unable to get memory for exported symbols table");
ret = -ENOMEM;
}
}
return ret;
}
/****************************************************************************
* Name: findep
*
* Description:
* Binary search comparison function
*
* Input Parameters:
* c1 - Comparand 1
* c2 - Comparand 2
*
****************************************************************************/
static int findep(FAR const void *c1, FAR const void *c2)
{
FAR const struct eptable_s *m1 = (FAR const struct eptable_s *)c1;
FAR const struct eptable_s *m2 = (FAR const struct eptable_s *)c2;
return strcmp((FAR const char *)m1->epname, (FAR const char *)m2->epname);
}
/****************************************************************************
* Name: libelf_findglobal
*
* Description:
* Find a symbol in our library entry point table
*
* Input Parameters:
* modp - Module state information
*
****************************************************************************/
void *libelf_findglobal(FAR struct module_s *modp,
FAR struct mod_loadinfo_s *loadinfo,
FAR Elf_Shdr *shdr, FAR Elf_Sym *sym)
{
FAR Elf_Shdr *strtab = &loadinfo->shdr[shdr->sh_link];
int ret;
struct eptable_s key;
FAR struct eptable_s *res;
ret = libelf_symname(loadinfo, sym, strtab->sh_offset);
if (ret < 0)
{
return NULL;
}
key.epname = loadinfo->iobuffer;
res = bsearch(&key, global_table, nglobals,
sizeof(struct eptable_s), findep);
if (res != NULL)
{
return res->epaddr;
}
else
{
return NULL;
}
}
/****************************************************************************
* Name: libelf_freesymtab
*
* Description:
* Free a symbol table
*
* Input Parameters:
* modp - Module state information
*
****************************************************************************/
void libelf_freesymtab(FAR struct module_s *modp)
{
FAR const struct symtab_s *symbol;
int i;
if ((symbol = modp->modinfo.exports) != NULL)
{
for (i = 0; i < modp->modinfo.nexports; i++)
{
lib_free((FAR void *)symbol[i].sym_name);
}
lib_free((FAR void *)symbol);
}
}