mirror of
https://github.com/apache/nuttx.git
synced 2026-10-03 04:07:55 +00:00
exec() of an FDPIC module now works. The loader already places such an object and binds it; what was missing is everything binfmt has to carry across from the load to the running task. The task needs the module's data base in its PIC base register. binfmt builds a D-Space for any object with a GOT, taking the base from the .got section address; an FDPIC object names it in DT_PLTGOT instead, which the loader has already translated, so the two are the same idea reached by different routes and both are what up_initial_state() installs. Constructors are not binfmt's business. A module carries its own crt0, which walks .init_array on the task that runs the module and then calls main, so they run in the module's own context and with its own data base. For a module that arrives through dlopen(), libelf_insert() walks the array instead, and it enters each entry through fdpic_invoke() because a descriptor resolved on the calling task carries the wrong base. The read-only segment of a module that executes in place is held by a filesystem pin. The load takes it, and the module owns it from the point where nothing can fail any more; it is given back when the task that runs the module exits. The pin is held through a reference to the file rather than a descriptor, because the descriptor belongs to the task that called the loader and the release happens on another one. libelf_remove() and libelf_uninit() give back what an FDPIC module holds: the pin, and the writable segment, while the read-only one is media rather than an allocation and must not be freed. Built for mps3-an547:picostest with CONFIG_FDPIC both ways. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
285 lines
7.5 KiB
C
285 lines
7.5 KiB
C
/****************************************************************************
|
|
* libs/libc/elf/elf_remove.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 <assert.h>
|
|
#include <nuttx/debug.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/fdpic.h>
|
|
#include <nuttx/lib/lib.h>
|
|
#include <nuttx/lib/elf.h>
|
|
|
|
#include "elf/elf.h"
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: libelf_uninit
|
|
*
|
|
* Description:
|
|
* Uninitialize module resources.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int libelf_uninit(FAR struct module_s *modp)
|
|
{
|
|
FAR void (**array)(void);
|
|
int ret = OK;
|
|
int i;
|
|
|
|
#if CONFIG_LIBC_ELF_MAXDEPEND > 0
|
|
/* Refuse to remove any module that other modules may depend upon. */
|
|
|
|
if (modp->dependents > 0)
|
|
{
|
|
berr("ERROR: Module has dependents: %d\n", modp->dependents);
|
|
return -EBUSY;
|
|
}
|
|
#endif
|
|
|
|
/* Is there an uninitializer? Like the constructors, an FDPIC object's
|
|
* destructors reach its globals through its own data base, which the
|
|
* unloading thread does not carry.
|
|
*/
|
|
|
|
array = (FAR void (**)(void))modp->finiarr;
|
|
for (i = 0; i < modp->nfini; i++)
|
|
{
|
|
fdpic_call(0, array[i], modp->gotbase);
|
|
}
|
|
|
|
if (modp->modinfo.uninitializer != NULL)
|
|
{
|
|
/* Try to uninitialize the module */
|
|
|
|
ret = modp->modinfo.uninitializer(modp->modinfo.arg);
|
|
|
|
/* Did the module successfully uninitialize? */
|
|
|
|
if (ret < 0)
|
|
{
|
|
berr("ERROR: Failed to uninitialize the module: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Nullify so that the uninitializer cannot be called again */
|
|
|
|
modp->modinfo.uninitializer = NULL;
|
|
modp->modinfo.arg = NULL;
|
|
}
|
|
|
|
/* Free the symbol table that the module exports. It is built for
|
|
* every loaded module, whether or not it has an uninitializer.
|
|
*/
|
|
|
|
libelf_freesymtab(modp);
|
|
modp->modinfo.exports = NULL;
|
|
modp->modinfo.nexports = 0;
|
|
|
|
#ifdef HAVE_LIBC_ELF_PIN
|
|
/* Give the pin back before the text goes out of use. This does nothing if
|
|
* the loader took no pin.
|
|
*/
|
|
|
|
libelf_pinrelease(&modp->pinfile);
|
|
#endif
|
|
|
|
/* Release resources held by the module */
|
|
|
|
if (modp->textalloc != NULL || modp->dataalloc != NULL)
|
|
{
|
|
/* Free the module memory and nullify so that the memory cannot
|
|
* be freed again
|
|
*
|
|
* NOTE: For dynamic shared objects there is only a single
|
|
* allocation: the text/data were allocated in one operation
|
|
*/
|
|
|
|
if (!modp->dynamic)
|
|
{
|
|
#ifdef CONFIG_ARCH_USE_SEPARATED_SECTION
|
|
for (i = 0; i < modp->nsect && modp->sectalloc[i] != NULL; i++)
|
|
{
|
|
# ifdef CONFIG_ARCH_USE_TEXT_HEAP
|
|
if (up_textheap_heapmember(modp->sectalloc[i]))
|
|
{
|
|
up_textheap_free(modp->sectalloc[i]);
|
|
continue;
|
|
}
|
|
# endif
|
|
|
|
# ifdef CONFIG_ARCH_USE_DATA_HEAP
|
|
if (up_dataheap_heapmember(modp->sectalloc[i]))
|
|
{
|
|
up_dataheap_free(modp->sectalloc[i]);
|
|
continue;
|
|
}
|
|
# endif
|
|
|
|
lib_free(modp->sectalloc[i]);
|
|
}
|
|
|
|
lib_free(modp->sectalloc);
|
|
modp->sectalloc = NULL;
|
|
modp->nsect = 0;
|
|
#else
|
|
if (modp->xipbase == 0)
|
|
{
|
|
# if defined(CONFIG_ARCH_USE_TEXT_HEAP)
|
|
up_textheap_free((FAR void *)modp->textalloc);
|
|
# else
|
|
lib_free((FAR void *)modp->textalloc);
|
|
# endif
|
|
}
|
|
|
|
# if defined(CONFIG_ARCH_USE_DATA_HEAP)
|
|
up_dataheap_free((FAR void *)modp->dataalloc);
|
|
# else
|
|
lib_free((FAR void *)modp->dataalloc);
|
|
# endif
|
|
#endif
|
|
}
|
|
else if (modp->gotbase != 0)
|
|
{
|
|
/* An FDPIC object, which placed its two segments separately. Free
|
|
* each one. If the text stayed on the media, it was never
|
|
* allocated, thus leave it.
|
|
*/
|
|
|
|
if (modp->xipbase == 0)
|
|
{
|
|
#ifdef CONFIG_ARCH_USE_TEXT_HEAP
|
|
up_textheap_free((FAR void *)modp->textalloc);
|
|
#else
|
|
lib_free((FAR void *)modp->textalloc);
|
|
#endif
|
|
}
|
|
|
|
lib_free((FAR void *)modp->dataalloc);
|
|
}
|
|
else
|
|
{
|
|
lib_free((FAR void *)modp->textalloc);
|
|
}
|
|
|
|
modp->textalloc = NULL;
|
|
modp->dataalloc = NULL;
|
|
#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE)
|
|
modp->textsize = 0;
|
|
modp->datasize = 0;
|
|
#endif
|
|
}
|
|
|
|
#if CONFIG_LIBC_ELF_MAXDEPEND > 0
|
|
/* Eliminate any dependencies that this module has on other modules */
|
|
|
|
libelf_undepend(modp);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: libelf_remove
|
|
*
|
|
* Description:
|
|
* Remove a previously installed module from memory.
|
|
*
|
|
* Input Parameters:
|
|
* handle - The module handler previously returned by libelf_insert().
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) on success. On any failure, -1 (ERROR) is returned the
|
|
* errno value is set appropriately.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int libelf_remove(FAR void *handle)
|
|
{
|
|
FAR struct module_s *modp = (FAR struct module_s *)handle;
|
|
int ret;
|
|
|
|
DEBUGASSERT(modp != NULL);
|
|
|
|
/* Get exclusive access to the module registry */
|
|
|
|
libelf_registry_lock();
|
|
|
|
/* Verify that the module is in the registry */
|
|
|
|
ret = libelf_registry_verify(modp);
|
|
if (ret < 0)
|
|
{
|
|
berr("ERROR: Failed to verify module: %d\n", ret);
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* Give back a reference. The module goes only when the last one does,
|
|
* so an rmmod() cannot pull a module out from under a dlopen() that is
|
|
* still holding it.
|
|
*/
|
|
|
|
if (modp->nopen > 1)
|
|
{
|
|
modp->nopen--;
|
|
libelf_registry_unlock();
|
|
return OK;
|
|
}
|
|
|
|
modp->nopen = 0;
|
|
|
|
ret = libelf_uninit(modp);
|
|
if (ret < 0)
|
|
{
|
|
berr("ERROR: Failed to uninitialize module %d\n", ret);
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* Remove the module from the registry */
|
|
|
|
ret = libelf_registry_del(modp);
|
|
if (ret < 0)
|
|
{
|
|
berr("ERROR: Failed to remove the module from the registry: %d\n",
|
|
ret);
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
libelf_registry_unlock();
|
|
|
|
/* And free the registry entry */
|
|
|
|
lib_free(modp);
|
|
|
|
return ret;
|
|
|
|
errout_with_lock:
|
|
libelf_registry_unlock();
|
|
set_errno(-ret);
|
|
return ERROR;
|
|
}
|