* binfmt/binfmt_execmodule.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/stat.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdlib.h>
#include <sched.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/addrenv.h>
#include <nuttx/arch.h>
#include <nuttx/fs/fs.h>
#include <nuttx/kmalloc.h>
#include <nuttx/sched.h>
#include <sched/sched.h>
#include <task/spawn.h>
#include <nuttx/spawn.h>
#include <nuttx/binfmt/binfmt.h>
#include "binfmt.h"
#ifndef CONFIG_BINFMT_DISABLE
* Pre-processor Definitions
****************************************************************************/
* Private Functions
****************************************************************************/
* Name: exec_swap
*
* Description:
* swap the pid of tasks, and reverse parent-child relationship.
*
* Input Parameters:
* ptcb - parent task tcb.
* chtcb - child task tcb.
*
* Returned Value:
* none
*
****************************************************************************/
static void exec_swap(FAR struct tcb_s *ptcb, FAR struct tcb_s *chtcb)
{
int pndx;
int chndx;
pid_t pid;
irqstate_t flags;
#ifdef CONFIG_SCHED_HAVE_PARENT
# ifdef CONFIG_SCHED_CHILD_STATUS
FAR struct child_status_s *tg_children;
# else
uint32_t nchildren;
# endif
#endif
DEBUGASSERT(ptcb);
DEBUGASSERT(chtcb);
flags = spin_lock_irqsave(&g_pidhashlock);
pndx = PIDHASH(ptcb->pid);
chndx = PIDHASH(chtcb->pid);
DEBUGASSERT(g_pidhash[pndx]);
DEBUGASSERT(g_pidhash[chndx]);
g_pidhash[pndx] = chtcb;
g_pidhash[chndx] = ptcb;
pid = chtcb->pid;
chtcb->pid = ptcb->pid;
ptcb->pid = pid;
pid = chtcb->group->tg_pid;
chtcb->group->tg_pid = ptcb->group->tg_pid;
ptcb->group->tg_pid = pid;
pid = chtcb->group->tg_ppid;
chtcb->group->tg_ppid = ptcb->group->tg_ppid;
ptcb->group->tg_ppid = pid;
#ifdef CONFIG_SCHED_HAVE_PARENT
# ifdef CONFIG_SCHED_CHILD_STATUS
tg_children = chtcb->group->tg_children;
chtcb->group->tg_children = ptcb->group->tg_children;
ptcb->group->tg_children = tg_children;
# else
nchildren = atomic_read(&chtcb->group->tg_nchildren);
atomic_set(&chtcb->group->tg_nchildren,
atomic_read(&ptcb->group->tg_nchildren));
atomic_set(&ptcb->group->tg_nchildren, nchildren);
# endif
#endif
spin_unlock_irqrestore(&g_pidhashlock, flags);
}
* Public Functions
****************************************************************************/
* Name: exec_module
*
* Description:
* Execute a module that has been loaded into memory by load_module().
*
* 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 exec_module(FAR struct binary_s *binp,
FAR const char *filename, FAR char * const *argv,
FAR char * const *envp,
FAR const posix_spawn_file_actions_t *actions,
FAR const posix_spawnattr_t *attr,
bool spawn)
{
posix_spawnattr_t tmp;
FAR struct tcb_s *tcb;
#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_BUILD_KERNEL)
FAR struct arch_addrenv_s *addrenv = &binp->addrenv->addrenv;
FAR void *vheap;
char name[CONFIG_PATH_MAX];
#endif
#ifndef CONFIG_BUILD_KERNEL
FAR void *stackaddr = NULL;
#endif
pid_t pid;
int ret;
#ifdef CONFIG_DEBUG_FEATURES
if (!binp || !binp->entrypt || binp->stacksize <= 0)
{
return -EINVAL;
}
#endif
binfo("Executing %s\n", filename);
tcb = kmm_zalloc(sizeof(struct tcb_s) + sizeof(struct task_group_s));
if (!tcb)
{
return -ENOMEM;
}
ret = binfmt_copyargv(&argv, argv);
if (ret < 0)
{
goto errout_with_tcb;
}
if (envp == NULL)
{
envp = environ;
}
ret = binfmt_copyenv(&envp, envp);
if (ret < 0)
{
goto errout_with_args;
}
ret = binfmt_copyactions(&actions, actions);
if (ret < 0)
{
goto errout_with_envp;
}
ret = binfmt_copyattr(&attr, attr);
if (ret < 0)
{
goto errout_with_actions;
}
#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_BUILD_KERNEL)
if (argv == NULL)
{
strlcpy(name, filename, CONFIG_PATH_MAX);
filename = name;
}
ret = addrenv_select(binp->addrenv, &binp->oldenv);
if (ret < 0)
{
berr("ERROR: addrenv_select() failed: %d\n", ret);
goto errout_with_attr;
}
ret = up_addrenv_vheap(addrenv, &vheap);
if (ret < 0)
{
berr("ERROR: up_addrenv_vheap() failed: %d\n", ret);
goto errout_with_addrenv;
}
binfo("Initialize the user heap (heapsize=%zu)\n",
up_addrenv_heapsize(addrenv));
umm_initialize(vheap, up_addrenv_heapsize(addrenv));
#endif
atomic_or(&tcb->flags, TCB_FLAG_FREE_TCB);
#ifndef CONFIG_BUILD_KERNEL
stackaddr = binp->stackaddr;
#endif
#ifdef CONFIG_ARCH_ADDRENV
ret = addrenv_attach(tcb, binp->addrenv);
if (ret < 0)
{
berr("ERROR: addrenv_attach() failed: %d\n", ret);
goto errout_with_addrenv;
}
#endif
if (!attr)
{
posix_spawnattr_init(&tmp);
posix_spawnattr_setstackaddr(&tmp, stackaddr);
posix_spawnattr_setpriority(&tmp, binp->priority);
posix_spawnattr_setstacksize(&tmp, binp->stacksize);
posix_spawnattr_setheapsize(&tmp, binp->heapsize);
attr = &tmp;
}
if (argv && argv[0])
{
ret = nxtask_init(tcb, argv[0], binp->entrypt, actions, attr,
&argv[1], envp);
}
else
{
ret = nxtask_init(tcb, filename, binp->entrypt, actions, attr,
argv, envp);
}
if (attr == &tmp)
{
posix_spawnattr_destroy(&tmp);
}
if (ret < 0)
{
berr("nxtask_init() failed: %d\n", ret);
goto errout_with_addrenv;
}
if (attr != &tmp)
{
binfmt_freeattr(attr);
}
binfmt_freeactions(actions);
binfmt_freeargv(argv);
binfmt_freeenv(envp);
#ifdef CONFIG_PIC
* must be the first allocated address space.
*/
tcb->dspace = binp->picbase;
up_initial_state(tcb);
#endif
#ifdef CONFIG_SCHED_USER_IDENTITY
if (binp->mode & S_ISUID)
{
tcb->group->tg_euid = binp->uid;
}
if (binp->mode & S_ISGID)
{
tcb->group->tg_egid = binp->gid;
}
#endif
if (!spawn)
{
exec_swap(this_task(), tcb);
}
pid = tcb->pid;
#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_BUILD_KERNEL)
ret = addrenv_restore(binp->oldenv);
if (ret < 0)
{
berr("ERROR: addrenv_restore() failed: %d\n", ret);
goto errout_with_tcbinit;
}
#endif
if (attr != &tmp)
{
ret = spawn_execattrs(pid, attr);
if (ret < 0)
{
goto errout_with_tcbinit;
}
}
#ifdef CONFIG_BINFMT_LOADABLE
tcb->group->tg_bininfo = binp;
#endif
nxtask_activate(tcb);
return pid;
errout_with_tcbinit:
#ifndef CONFIG_BUILD_KERNEL
if (binp->stackaddr != NULL)
{
tcb->stack_alloc_ptr = NULL;
}
#endif
nxtask_uninit(tcb);
tcb = NULL;
errout_with_addrenv:
#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_BUILD_KERNEL)
addrenv_restore(binp->oldenv);
errout_with_attr:
if (attr != &tmp)
{
binfmt_freeattr(attr);
}
#endif
errout_with_actions:
binfmt_freeactions(actions);
errout_with_envp:
binfmt_freeenv(envp);
errout_with_args:
binfmt_freeargv(argv);
errout_with_tcb:
kmm_free(tcb);
return ret;
}
#endif