diff --git a/include/nuttx/lib/modlib.h b/include/nuttx/lib/modlib.h index 2a9c24b5b54..f55521e07fa 100644 --- a/include/nuttx/lib/modlib.h +++ b/include/nuttx/lib/modlib.h @@ -32,6 +32,8 @@ #include #include +#include + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -227,6 +229,17 @@ struct mod_loadinfo_s uint16_t buflen; /* size of iobuffer[] */ int filfd; /* Descriptor for the file being loaded */ int nexports; /* ET_DYN - Number of symbols exported */ + + /* Address environment. + * + * addrenv - This is the handle created by addrenv_allocate() that can be + * used to manage the tasks address space. + */ + +#ifdef CONFIG_ARCH_ADDRENV + FAR addrenv_t *addrenv; /* Address environment */ + FAR addrenv_t *oldenv; /* Saved address environment */ +#endif }; /**************************************************************************** @@ -318,6 +331,25 @@ void modlib_setsymtab(FAR const struct symtab_s *symtab, int nsymbols); int modlib_load(FAR struct mod_loadinfo_s *loadinfo); +/**************************************************************************** + * Name: modlib_load_with_addrenv + * + * Description: + * Loads the binary into memory, use the address environment to load the + * binary. + * + * Returned Value: + * 0 (OK) is returned on success and a negated errno is returned on + * failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ARCH_ADDRENV +int modlib_load_with_addrenv(FAR struct mod_loadinfo_s *loadinfo); +#else +# define modlib_load_with_addrenv(l) modlib_load(l) +#endif + /**************************************************************************** * Name: modlib_bind * diff --git a/libs/libc/modlib/CMakeLists.txt b/libs/libc/modlib/CMakeLists.txt index 7372cad4fea..fde06db98e3 100644 --- a/libs/libc/modlib/CMakeLists.txt +++ b/libs/libc/modlib/CMakeLists.txt @@ -25,6 +25,7 @@ if(CONFIG_LIBC_MODLIB) list( APPEND SRCS + modlib_addrenv.c modlib_bind.c modlib_depend.c modlib_init.c diff --git a/libs/libc/modlib/Make.defs b/libs/libc/modlib/Make.defs index c9f5c222cfc..4d727470d87 100644 --- a/libs/libc/modlib/Make.defs +++ b/libs/libc/modlib/Make.defs @@ -24,8 +24,8 @@ ifeq ($(CONFIG_LIBC_MODLIB),y) # Add the nuttx/lib/modlib.h files to the build -CSRCS += modlib_bind.c modlib_depend.c modlib_init.c modlib_iobuffer.c -CSRCS += modlib_load.c modlib_loadhdrs.c modlib_verify.c +CSRCS += modlib_addrenv.c modlib_bind.c modlib_depend.c modlib_init.c +CSRCS += modlib_iobuffer.c modlib_load.c modlib_loadhdrs.c modlib_verify.c CSRCS += modlib_read.c modlib_registry.c modlib_sections.c CSRCS += modlib_symbols.c modlib_symtab.c modlib_uninit.c modlib_unload.c CSRCS += modlib_gethandle.c modlib_getsymbol.c modlib_insert.c diff --git a/libs/libc/modlib/modlib.h b/libs/libc/modlib/modlib.h index 74043cc5a42..4e004521d37 100644 --- a/libs/libc/modlib/modlib.h +++ b/libs/libc/modlib/modlib.h @@ -27,6 +27,7 @@ * Included Files ****************************************************************************/ +#include #include /**************************************************************************** @@ -237,4 +238,87 @@ int modlib_reallocbuffer(FAR struct mod_loadinfo_s *loadinfo, int modlib_freebuffers(FAR struct mod_loadinfo_s *loadinfo); +#ifdef CONFIG_ARCH_ADDRENV + +/**************************************************************************** + * Name: modlib_addrenv_alloc + * + * Description: + * Allocate memory for the ELF image (textalloc and datastart). If + * CONFIG_ARCH_ADDRENV=n, textalloc will be allocated using kmm_zalloc() + * and datastart will be a offset from textalloc. If + * CONFIG_ARCH_ADDRENV=y, then textalloc and datastart will be allocated + * using up_addrenv_create(). In either case, there will be a unique + * instance of textalloc and datastart (and stack) for each instance of a + * process. + * + * Input Parameters: + * loadinfo - Load state information + * textsize - The size (in bytes) of the .text address environment needed + * for the ELF image (read/execute). + * datasize - The size (in bytes) of the .bss/.data address environment + * needed for the ELF image (read/write). + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int modlib_addrenv_alloc(FAR struct mod_loadinfo_s *loadinfo, + size_t textsize, size_t datasize); + +/**************************************************************************** + * Name: modlib_addrenv_select + * + * Description: + * Temporarily select the task's address environment. + * + * Input Parameters: + * loadinfo - Load state information + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int modlib_addrenv_select(FAR struct mod_loadinfo_s *loadinfo); + +/**************************************************************************** + * Name: modlib_addrenv_restore + * + * Description: + * Restore the address environment before modlib_addrenv_select() was + * called. + * + * Input Parameters: + * loadinfo - Load state information + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int modlib_addrenv_restore(FAR struct mod_loadinfo_s *loadinfo); + +/**************************************************************************** + * Name: modlib_addrenv_free + * + * Description: + * Release the address environment previously created by + * modlib_addrenv_alloc(). This function is called only under certain + * error conditions after the module has been loaded but not yet started. + * After the module has been started, the address environment will + * automatically be freed when the module exits. + * + * Input Parameters: + * loadinfo - Load state information + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void modlib_addrenv_free(FAR struct mod_loadinfo_s *loadinfo); + +#endif /* CONFIG_ARCH_ADDRENV */ #endif /* __LIBS_LIBC_MODLIB_MODLIB_H */ diff --git a/libs/libc/modlib/modlib_addrenv.c b/libs/libc/modlib/modlib_addrenv.c new file mode 100644 index 00000000000..6d140ff341f --- /dev/null +++ b/libs/libc/modlib/modlib_addrenv.c @@ -0,0 +1,251 @@ +/**************************************************************************** + * libs/libc/modlib/modlib_addrenv.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 + +#include +#include + +#include +#include + +#include +#include + +#include "modlib.h" + +#ifdef CONFIG_ARCH_ADDRENV + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ELF_TEXT_WRE (PROT_READ | PROT_WRITE | PROT_EXEC) +#define ELF_TEXT_RE (PROT_READ | PROT_EXEC) + +/**************************************************************************** + * Private Constant Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: modlib_addrenv_alloc + * + * Description: + * Allocate memory for the ELF image (textalloc and datastart). If + * CONFIG_ARCH_ADDRENV=n, textalloc will be allocated using kmm_zalloc() + * and datastart will be a offset from textalloc. If + * CONFIG_ARCH_ADDRENV=y, then textalloc and datastart will be allocated + * using up_addrenv_create(). In either case, there will be a unique + * instance of textalloc and datastart (and stack) for each instance of a + * process. + * + * Input Parameters: + * loadinfo - Load state information + * textsize - The size (in bytes) of the .text address environment needed + * for the ELF image (read/execute). + * datasize - The size (in bytes) of the .bss/.data address environment + * needed for the ELF image (read/write). + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int modlib_addrenv_alloc(FAR struct mod_loadinfo_s *loadinfo, + size_t textsize, size_t datasize) +{ +#if defined(CONFIG_ARCH_STACK_DYNAMIC) + size_t heapsize = ARCH_HEAP_SIZE; +#else + size_t heapsize = MAX(ARCH_HEAP_SIZE, CONFIG_ELF_STACKSIZE); +#endif + FAR struct arch_addrenv_s *addrenv; + FAR void *vtext; + FAR void *vdata; + int ret; + + /* Create an address environment for the new ELF task */ + + loadinfo->addrenv = addrenv_allocate(); + if (!loadinfo->addrenv) + { + return -ENOMEM; + } + + /* Start creating the address environment sections */ + + addrenv = &loadinfo->addrenv->addrenv; + + ret = up_addrenv_create(textsize, datasize, heapsize, addrenv); + if (ret < 0) + { + berr("ERROR: up_addrenv_create failed: %d\n", ret); + goto errout_with_addrenv; + } + + /* Get the virtual address associated with the start of the address + * environment. This is the base address that we will need to use to + * access the ELF image (but only if the address environment has been + * selected. + */ + + ret = up_addrenv_vtext(addrenv, &vtext); + if (ret < 0) + { + berr("ERROR: up_addrenv_vtext failed: %d\n", ret); + goto errout_with_addrenv; + } + + ret = up_addrenv_vdata(addrenv, textsize, &vdata); + if (ret < 0) + { + berr("ERROR: up_addrenv_vdata failed: %d\n", ret); + goto errout_with_addrenv; + } + + loadinfo->textalloc = (uintptr_t)vtext; + loadinfo->datastart = (uintptr_t)vdata; + + return OK; + +errout_with_addrenv: + addrenv_drop(loadinfo->addrenv, false); + return ret; +} + +/**************************************************************************** + * Name: modlib_addrenv_select + * + * Description: + * Temporarily select the task's address environment. + * + * Input Parameters: + * loadinfo - Load state information + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int modlib_addrenv_select(FAR struct mod_loadinfo_s *loadinfo) +{ + int ret; + + /* Instantiate the new address environment */ + + ret = addrenv_select(loadinfo->addrenv, &loadinfo->oldenv); + if (ret < 0) + { + berr("ERROR: addrenv_select failed: %d\n", ret); + return ret; + } + + /* Allow write access to .text */ + + ret = up_addrenv_mprot(&loadinfo->addrenv->addrenv, loadinfo->textalloc, + loadinfo->textsize, ELF_TEXT_WRE); + if (ret < 0) + { + berr("ERROR: up_addrenv_text_enable_write failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: modlib_addrenv_restore + * + * Description: + * Restore the address environment before modlib_addrenv_select() was + * called. + * + * Input Parameters: + * loadinfo - Load state information + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int modlib_addrenv_restore(FAR struct mod_loadinfo_s *loadinfo) +{ + int ret; + + /* Remove write access to .text */ + + ret = up_addrenv_mprot(&loadinfo->addrenv->addrenv, loadinfo->textalloc, + loadinfo->textsize, ELF_TEXT_RE); + if (ret < 0) + { + berr("ERROR: up_addrenv_text_disable_write failed: %d\n", ret); + return ret; + } + + /* Restore the old address environment */ + + ret = addrenv_restore(loadinfo->oldenv); + if (ret < 0) + { + berr("ERROR: addrenv_restore failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: modlib_addrenv_free + * + * Description: + * Release the address environment previously created by + * modlib_addrenv_alloc(). This function is called only under certain + * error conditions after the module has been loaded but not yet started. + * After the module has been started, the address environment will + * automatically be freed when the module exits. + * + * Input Parameters: + * loadinfo - Load state information + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void modlib_addrenv_free(FAR struct mod_loadinfo_s *loadinfo) +{ + /* Free the address environment */ + + addrenv_drop(loadinfo->addrenv, false); +} + +#endif diff --git a/libs/libc/modlib/modlib_bind.c b/libs/libc/modlib/modlib_bind.c index 2e9ef7ac5e2..d8679d28545 100644 --- a/libs/libc/modlib/modlib_bind.c +++ b/libs/libc/modlib/modlib_bind.c @@ -851,6 +851,23 @@ int modlib_bind(FAR struct module_s *modp, int ret; int i; +#ifdef CONFIG_ARCH_ADDRENV + /* If CONFIG_ARCH_ADDRENV=y, then the loaded ELF lies in a virtual address + * space that may not be in place now. modlib_addrenv_select() will + * temporarily instantiate that address space. + */ + + if (loadinfo->addrenv != NULL) + { + ret = modlib_addrenv_select(loadinfo); + if (ret < 0) + { + berr("ERROR: modlib_addrenv_select() failed: %d\n", ret); + return ret; + } + } +#endif + /* Find the symbol and string tables */ ret = modlib_findsymtab(loadinfo); @@ -1007,5 +1024,20 @@ int modlib_bind(FAR struct module_s *modp, } #endif +#ifdef CONFIG_ARCH_ADDRENV + if (loadinfo->addrenv != NULL) + { + int status = modlib_addrenv_restore(loadinfo); + if (status < 0) + { + berr("ERROR: modlib_addrenv_restore() failed: %d\n", status); + if (ret == OK) + { + ret = status; + } + } + } +#endif + return ret; } diff --git a/libs/libc/modlib/modlib_load.c b/libs/libc/modlib/modlib_load.c index fa58513c515..865df005a1f 100644 --- a/libs/libc/modlib/modlib_load.c +++ b/libs/libc/modlib/modlib_load.c @@ -129,7 +129,7 @@ static int modlib_section_alloc(FAR struct mod_loadinfo_s *loadinfo, * ****************************************************************************/ -static void modlib_elfsize(FAR struct mod_loadinfo_s *loadinfo) +static void modlib_elfsize(FAR struct mod_loadinfo_s *loadinfo, bool alloc) { size_t textsize = 0; size_t datasize = 0; @@ -187,7 +187,7 @@ static void modlib_elfsize(FAR struct mod_loadinfo_s *loadinfo) ) { #ifdef CONFIG_ARCH_USE_SEPARATED_SECTION - if (modlib_section_alloc(loadinfo, shdr, i) >= 0) + if (alloc && modlib_section_alloc(loadinfo, shdr, i) >= 0) { continue; } @@ -203,7 +203,7 @@ static void modlib_elfsize(FAR struct mod_loadinfo_s *loadinfo) else { #ifdef CONFIG_ARCH_USE_SEPARATED_SECTION - if (modlib_section_alloc(loadinfo, shdr, i) >= 0) + if (alloc && modlib_section_alloc(loadinfo, shdr, i) >= 0) { continue; } @@ -456,7 +456,7 @@ int modlib_load(FAR struct mod_loadinfo_s *loadinfo) /* Determine total size to allocate */ - modlib_elfsize(loadinfo); + modlib_elfsize(loadinfo, true); /* Allocate (and zero) memory for the ELF file. */ @@ -545,3 +545,84 @@ errout_with_buffers: modlib_unload(loadinfo); return ret; } + +/**************************************************************************** + * Name: modlib_load_with_addrenv + * + * Description: + * Loads the binary into memory, use the address environment to load the + * binary. + * + * Returned Value: + * 0 (OK) is returned on success and a negated errno is returned on + * failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ARCH_ADDRENV +int modlib_load_with_addrenv(FAR struct mod_loadinfo_s *loadinfo) +{ + int ret; + + binfo("loadinfo: %p\n", loadinfo); + DEBUGASSERT(loadinfo && loadinfo->filfd >= 0); + + /* Load section and program headers into memory */ + + ret = modlib_loadhdrs(loadinfo); + if (ret < 0) + { + berr("ERROR: modlib_loadhdrs failed: %d\n", ret); + goto errout_with_buffers; + } + + /* Determine total size to allocate */ + + modlib_elfsize(loadinfo, false); + + ret = modlib_addrenv_alloc(loadinfo, loadinfo->textsize, + loadinfo->datasize); + if (ret < 0) + { + berr("ERROR: Failed to create address environment: %d\n", ret); + goto errout_with_buffers; + } + + /* If CONFIG_ARCH_ADDRENV=y, then the loaded ELF lies in a virtual address + * space that may not be in place now. elf_addrenv_select() will + * temporarily instantiate that address space. + */ + + ret = modlib_addrenv_select(loadinfo); + if (ret < 0) + { + berr("ERROR: elf_addrenv_select() failed: %d\n", ret); + goto errout_with_buffers; + } + + ret = modlib_loadfile(loadinfo); + if (ret < 0) + { + berr("ERROR: modlib_loadfile failed: %d\n", ret); + goto errout_with_addrenv; + } + + /* Restore the original address environment */ + + ret = modlib_addrenv_restore(loadinfo); + if (ret < 0) + { + berr("ERROR: modlib_addrenv_restore() failed: %d\n", ret); + goto errout_with_buffers; + } + + return OK; + +errout_with_addrenv: + modlib_addrenv_restore(loadinfo); + +errout_with_buffers: + modlib_unload(loadinfo); + return ret; +} +#endif diff --git a/libs/libc/modlib/modlib_unload.c b/libs/libc/modlib/modlib_unload.c index 9d36cc79d09..5fde605d2b0 100644 --- a/libs/libc/modlib/modlib_unload.c +++ b/libs/libc/modlib/modlib_unload.c @@ -58,6 +58,13 @@ int modlib_unload(FAR struct mod_loadinfo_s *loadinfo) modlib_freebuffers(loadinfo); +#ifdef CONFIG_ARCH_ADDRENV + if (loadinfo->addrenv != NULL) + { + modlib_addrenv_free(loadinfo); + } + else +#endif /* Release memory holding the relocated ELF image */ /* ET_DYN has a single allocation so we only free textalloc */