* binfmt/elf.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/param.h>
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/arch.h>
#include <nuttx/binfmt/binfmt.h>
#include <nuttx/kmalloc.h>
#ifdef CONFIG_ELF
* Pre-processor Definitions
****************************************************************************/
* have to be defined or CONFIG_LIBC_ELF_DUMPBUFFER does nothing.
*/
#if !defined(CONFIG_DEBUG_INFO) || !defined(CONFIG_DEBUG_BINFMT)
# undef CONFIG_LIBC_ELF_DUMPBUFFER
#endif
#ifndef CONFIG_ELF_STACKSIZE
# define CONFIG_ELF_STACKSIZE 2048
#endif
* Private Function Prototypes
****************************************************************************/
static int elf_loadbinary(FAR struct binary_s *binp,
FAR const char *filename,
FAR const struct symtab_s *exports,
int nexports);
static int elf_unloadbinary(FAR struct binary_s *binp);
* Private Data
****************************************************************************/
static DEFINE_PER_CPU_BMP(struct binfmt_s, g_elfbinfmt) =
{
NULL,
elf_loadbinary,
elf_unloadbinary,
};
#define g_elfbinfmt this_cpu_var_bmp(g_elfbinfmt)
* Private Functions
****************************************************************************/
* Name: elf_loadbinary
*
* Description:
* Verify that the file is an ELF binary and, if so, load the ELF
* binary into memory
*
****************************************************************************/
static int elf_loadbinary(FAR struct binary_s *binp,
FAR const char *filename,
FAR const struct symtab_s *exports,
int nexports)
{
struct mod_loadinfo_s loadinfo;
Elf_Sym sym;
int ret;
binfo("Loading file: %s\n", filename);
ret = libelf_initialize(filename, &loadinfo);
libelf_dumploadinfo(&loadinfo);
if (ret != 0)
{
goto errout_with_init;
}
ret = libelf_load_with_addrenv(&loadinfo, false);
libelf_dumploadinfo(&loadinfo);
if (ret != 0)
{
berr("Failed to load ELF program binary: %d\n", ret);
goto errout_with_init;
}
if (loadinfo.ehdr.e_type == ET_REL
#ifdef CONFIG_LIBC_ELF_GOT
|| loadinfo.gotindex >= 0
#endif
)
{
ret = libelf_bind(&binp->mod, &loadinfo, exports, nexports);
if (ret != 0)
{
berr("Failed to bind symbols program binary: %d\n", ret);
goto errout_with_load;
}
binp->entrypt = (main_t)(loadinfo.textalloc + loadinfo.ehdr.e_entry);
}
else if (loadinfo.ehdr.e_type == ET_EXEC)
{
if (nexports > 0)
{
berr("Cannot bind exported symbols to a "
"fully linked executable\n");
ret = -ENOEXEC;
goto errout_with_load;
}
binp->entrypt = (main_t)(loadinfo.ehdr.e_entry);
}
else
{
berr("Unexpected elf type %d\n", loadinfo.ehdr.e_type);
ret = -ENOEXEC;
goto errout_with_load;
}
ret = libelf_findsymbol(&loadinfo, "nx_stacksize", &sym);
if (ret == 0)
{
binp->stacksize = sym.st_value;
}
else
{
binp->stacksize = CONFIG_ELF_STACKSIZE;
}
ret = libelf_findsymbol(&loadinfo, "nx_priority", &sym);
if (ret == 0)
{
binp->priority = sym.st_value;
}
else
{
binp->priority = SCHED_PRIORITY_DEFAULT;
}
#ifdef CONFIG_SCHED_USER_IDENTITY
ret = libelf_findsymbol(&loadinfo, "nx_uid", &sym);
if (ret == 0)
{
binp->uid = sym.st_value;
}
else
{
binp->uid = 0;
}
ret = libelf_findsymbol(&loadinfo, "nx_gid", &sym);
if (ret == 0)
{
binp->gid = sym.st_value;
}
else
{
binp->gid = 0;
}
ret = libelf_findsymbol(&loadinfo, "nx_mode", &sym);
if (ret == 0)
{
binp->mode = sym.st_value;
}
else
{
binp->mode = 0;
}
#endif
#ifdef CONFIG_MM_TASK_HEAP
ret = libelf_findsymbol(&loadinfo, "nx_heapsize", &sym);
if (ret == 0)
{
binp->heapsize = sym.st_value;
}
else
{
binp->priority = CONFIG_MM_TASK_HEAP_DEFAULT_SIZE;
}
#endif
* environment. If there is an address environment, it will automatically
* be freed when the function exits
*
* REVISIT: If the module is loaded then unloaded, wouldn't this cause
* a memory leak?
*/
#ifdef CONFIG_ARCH_ADDRENV
* needed when the module is executed.
*/
binp->addrenv = loadinfo.addrenv;
#else
# ifdef CONFIG_ARCH_USE_SEPARATED_SECTION
if (loadinfo.ehdr.e_type == ET_REL)
{
binp->mod.sectalloc = (FAR void *)loadinfo.sectalloc;
binp->mod.nsect = loadinfo.ehdr.e_shnum;
}
# endif
binp->mod.textalloc = (FAR void *)loadinfo.textalloc;
binp->mod.dataalloc = (FAR void *)loadinfo.datastart;
# ifdef CONFIG_BINFMT_CONSTRUCTORS
binp->mod.initarr = loadinfo.initarr;
binp->mod.finiarr = loadinfo.finiarr;
# endif
#endif
#ifdef CONFIG_BINFMT_CONSTRUCTORS
binp->mod.initarr = loadinfo.initarr;
binp->mod.ninit = loadinfo.ninit;
binp->mod.finiarr = loadinfo.finiarr;
binp->mod.nfini = loadinfo.nfini;
#endif
#ifdef CONFIG_SCHED_USER_IDENTITY
binp->uid = loadinfo.fileuid;
binp->gid = loadinfo.filegid;
binp->mode = loadinfo.filemode;
#endif
libelf_dumpentrypt(&loadinfo);
#if defined(CONFIG_PIC) && defined(CONFIG_LIBC_ELF_GOT)
if (loadinfo.gotindex >= 0)
{
FAR struct dspace_s *dspaces = kmm_zalloc(sizeof(struct dspace_s));
if (dspaces == NULL)
{
ret = -ENOMEM;
goto errout_with_load;
}
dspaces->region = (FAR void *)loadinfo.shdr[loadinfo.gotindex].sh_addr;
dspaces->crefs = 1;
binp->picbase = (FAR void *)dspaces;
}
#endif
#ifdef HAVE_LIBC_ELF_NAMES
strlcpy(binp->mod.modname, strrchr(filename, '/') == NULL ?
filename: strrchr(filename, '/') + 1,
sizeof(binp->mod.modname));
#endif
libelf_uninitialize(&loadinfo);
return OK;
errout_with_load:
libelf_unload(&loadinfo);
errout_with_init:
libelf_uninitialize(&loadinfo);
return ret;
}
* Name: elf_unloadbinary
*
* Description:
* Unload the ELF binary that was loaded into memory by elf_loadbinary.
*
****************************************************************************/
static int elf_unloadbinary(FAR struct binary_s *binp)
{
binfo("Unloading %p\n", binp);
libelf_uninit(&binp->mod);
return OK;
}
* Public Functions
****************************************************************************/
* Name: elf_initialize
*
* Description:
* In order to use the ELF binary format, this function must be called
* during system initialization to register the ELF binary format.
*
* Returned Value:
* This is a NuttX internal function so it follows the convention that
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
****************************************************************************/
int elf_initialize(void)
{
int ret;
binfo("Registering ELF\n");
ret = register_binfmt(&g_elfbinfmt);
if (ret != 0)
{
berr("Failed to register binfmt: %d\n", ret);
}
return ret;
}
* Name: elf_uninitialize
*
* Description:
* Unregister the ELF binary loader
*
* Returned Value:
* None
*
****************************************************************************/
void elf_uninitialize(void)
{
unregister_binfmt(&g_elfbinfmt);
}
#endif