700 lines
18 KiB
C
700 lines
18 KiB
C
/*
|
|
* Copyright (c) 2006-2018, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2018/08/29 Bernard first version
|
|
*/
|
|
|
|
#include <rthw.h>
|
|
|
|
#include "dlfcn.h"
|
|
#include "dlmodule.h"
|
|
#include "dlelf.h"
|
|
|
|
#include <dfs_posix.h>
|
|
|
|
#define DBG_SECTION_NAME "DLMD"
|
|
#define DBG_ENABLE // enable debug macro
|
|
#define DBG_LEVEL DBG_INFO
|
|
#define DBG_COLOR
|
|
#include <rtdbg.h> // must after of DEBUG_ENABLE or some other options
|
|
|
|
static struct rt_module_symtab *_rt_module_symtab_begin = RT_NULL;
|
|
static struct rt_module_symtab *_rt_module_symtab_end = RT_NULL;
|
|
|
|
#if defined(__IAR_SYSTEMS_ICC__) /* for IAR compiler */
|
|
#pragma section="RTMSymTab"
|
|
#endif
|
|
|
|
/* set the name of module */
|
|
static void _dlmodule_set_name(struct rt_dlmodule *module, const char *path)
|
|
{
|
|
int size;
|
|
struct rt_object *object;
|
|
const char *first, *end, *ptr;
|
|
|
|
object = &(module->parent);
|
|
ptr = first = (char *)path;
|
|
end = path + rt_strlen(path);
|
|
|
|
while (*ptr != '\0')
|
|
{
|
|
if (*ptr == '/')
|
|
first = ptr + 1;
|
|
if (*ptr == '.')
|
|
end = ptr - 1;
|
|
|
|
ptr ++;
|
|
}
|
|
|
|
size = end - first + 1;
|
|
if (size > RT_NAME_MAX) size = RT_NAME_MAX;
|
|
|
|
rt_strncpy(object->name, first, size);
|
|
object->name[size] = '\0';
|
|
}
|
|
|
|
#define RT_MODULE_ARG_MAX 8
|
|
static int _rt_module_split_arg(char *cmd, rt_size_t length, char *argv[])
|
|
{
|
|
int argc = 0;
|
|
char *ptr = cmd;
|
|
|
|
while ((ptr - cmd) < length)
|
|
{
|
|
/* strip bank and tab */
|
|
while ((*ptr == ' ' || *ptr == '\t') && (ptr - cmd) < length)
|
|
*ptr++ = '\0';
|
|
/* check whether it's the end of line */
|
|
if ((ptr - cmd) >= length) break;
|
|
|
|
/* handle string with quote */
|
|
if (*ptr == '"')
|
|
{
|
|
argv[argc++] = ++ptr;
|
|
|
|
/* skip this string */
|
|
while (*ptr != '"' && (ptr - cmd) < length)
|
|
if (*ptr ++ == '\\') ptr ++;
|
|
if ((ptr - cmd) >= length) break;
|
|
|
|
/* skip '"' */
|
|
*ptr ++ = '\0';
|
|
}
|
|
else
|
|
{
|
|
argv[argc++] = ptr;
|
|
while ((*ptr != ' ' && *ptr != '\t') && (ptr - cmd) < length)
|
|
ptr ++;
|
|
}
|
|
|
|
if (argc >= RT_MODULE_ARG_MAX) break;
|
|
}
|
|
|
|
return argc;
|
|
}
|
|
|
|
/* invoked by main thread for exit */
|
|
static void _dlmodule_exit(void)
|
|
{
|
|
struct rt_dlmodule *module;
|
|
|
|
module = dlmodule_self();
|
|
if (!module) return; /* not a module thread */
|
|
|
|
rt_enter_critical();
|
|
if (module->stat == RT_DLMODULE_STAT_RUNNING)
|
|
{
|
|
struct rt_object *object = RT_NULL;
|
|
struct rt_list_node *node = RT_NULL;
|
|
|
|
/* set stat to closing */
|
|
module->stat = RT_DLMODULE_STAT_CLOSING;
|
|
|
|
/* suspend all threads in this module */
|
|
for (node = module->object_list.next; node != &(module->object_list); node = node->next)
|
|
{
|
|
object = rt_list_entry(node, struct rt_object, list);
|
|
|
|
if ((object->type & ~RT_Object_Class_Static) == RT_Object_Class_Thread)
|
|
{
|
|
rt_thread_t thread = (rt_thread_t)object;
|
|
|
|
/* stop timer and suspend thread*/
|
|
if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE ||
|
|
(thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT)
|
|
{
|
|
rt_timer_stop(&(thread->thread_timer));
|
|
rt_thread_suspend(thread);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
rt_exit_critical();
|
|
|
|
return;
|
|
}
|
|
|
|
static void _dlmodule_thread_entry(void* parameter)
|
|
{
|
|
int argc = 0;
|
|
char *argv[RT_MODULE_ARG_MAX];
|
|
|
|
struct rt_dlmodule *module = (struct rt_dlmodule*)parameter;
|
|
|
|
if (module == RT_NULL || module->cmd_line == RT_NULL)
|
|
/* malloc for module_cmd_line failed. */
|
|
return;
|
|
|
|
if (module->cmd_line)
|
|
{
|
|
rt_memset(argv, 0x00, sizeof(argv));
|
|
argc = _rt_module_split_arg((char *)module->cmd_line, rt_strlen(module->cmd_line), argv);
|
|
if (argc == 0) goto __exit;
|
|
}
|
|
|
|
/* set status of module */
|
|
module->stat = RT_DLMODULE_STAT_RUNNING;
|
|
|
|
LOG_D("run main entry: 0x%p with %s",
|
|
module->entry_addr,
|
|
module->cmd_line);
|
|
|
|
if (module->entry_addr)
|
|
module->entry_addr(argc, argv);
|
|
|
|
__exit:
|
|
_dlmodule_exit();
|
|
|
|
return ;
|
|
}
|
|
|
|
struct rt_dlmodule *dlmodule_create(void)
|
|
{
|
|
struct rt_dlmodule *module = RT_NULL;
|
|
|
|
module = (struct rt_dlmodule*) rt_object_allocate(RT_Object_Class_Module, "module");
|
|
if (module)
|
|
{
|
|
module->stat = RT_DLMODULE_STAT_INIT;
|
|
|
|
/* set initial priority and stack size */
|
|
module->priority = RT_THREAD_PRIORITY_MAX - 1;
|
|
module->stack_size = 2048;
|
|
|
|
rt_list_init(&(module->object_list));
|
|
}
|
|
|
|
return module;
|
|
}
|
|
|
|
void dlmodule_destroy_subthread(struct rt_dlmodule *module, rt_thread_t thread)
|
|
{
|
|
RT_ASSERT(thread->module_id == module);
|
|
|
|
/* lock scheduler to prevent scheduling in cleanup function. */
|
|
rt_enter_critical();
|
|
|
|
/* remove thread from thread_list (ready or defunct thread list) */
|
|
rt_list_remove(&(thread->tlist));
|
|
|
|
if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE &&
|
|
(thread->thread_timer.parent.type == (RT_Object_Class_Static | RT_Object_Class_Timer)))
|
|
{
|
|
/* release thread timer */
|
|
rt_timer_detach(&(thread->thread_timer));
|
|
}
|
|
|
|
/* change stat */
|
|
thread->stat = RT_THREAD_CLOSE;
|
|
|
|
/* invoke thread cleanup */
|
|
if (thread->cleanup != RT_NULL)
|
|
thread->cleanup(thread);
|
|
|
|
rt_exit_critical();
|
|
|
|
#ifdef RT_USING_SIGNALS
|
|
rt_thread_free_sig(thread);
|
|
#endif
|
|
|
|
if (thread->type & RT_Object_Class_Static)
|
|
{
|
|
/* detach object */
|
|
rt_object_detach((rt_object_t)thread);
|
|
}
|
|
#ifdef RT_USING_HEAP
|
|
else
|
|
{
|
|
/* release thread's stack */
|
|
RT_KERNEL_FREE(thread->stack_addr);
|
|
/* delete thread object */
|
|
rt_object_delete((rt_object_t)thread);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
rt_err_t dlmodule_destroy(struct rt_dlmodule* module)
|
|
{
|
|
int i;
|
|
|
|
RT_DEBUG_NOT_IN_INTERRUPT;
|
|
|
|
/* check parameter */
|
|
if (module == RT_NULL)
|
|
return -RT_ERROR;
|
|
|
|
/* can not destroy a running module */
|
|
if (module->stat == RT_DLMODULE_STAT_RUNNING)
|
|
return -RT_EBUSY;
|
|
|
|
/* do module cleanup */
|
|
if (module->cleanup_func)
|
|
{
|
|
rt_enter_critical();
|
|
module->cleanup_func(module);
|
|
rt_exit_critical();
|
|
}
|
|
|
|
// list_object(&(module->object_list));
|
|
|
|
/* cleanup for all kernel objects inside module*/
|
|
{
|
|
struct rt_object *object = RT_NULL;
|
|
struct rt_list_node *node = RT_NULL;
|
|
|
|
/* detach/delete all threads in this module */
|
|
for (node = module->object_list.next; node != &(module->object_list); )
|
|
{
|
|
int object_type;
|
|
|
|
object = rt_list_entry(node, struct rt_object, list);
|
|
object_type = object->type & ~RT_Object_Class_Static;
|
|
|
|
/* to next node */
|
|
node = node->next;
|
|
|
|
if (object->type & RT_Object_Class_Static)
|
|
{
|
|
switch (object_type)
|
|
{
|
|
case RT_Object_Class_Thread:
|
|
dlmodule_destroy_subthread(module, (rt_thread_t)object);
|
|
break;
|
|
#ifdef RT_USING_SEMAPHORE
|
|
case RT_Object_Class_Semaphore:
|
|
rt_sem_detach((rt_sem_t)object);
|
|
break;
|
|
#endif
|
|
#ifdef RT_USING_MUTEX
|
|
case RT_Object_Class_Mutex:
|
|
rt_mutex_detach((rt_mutex_t)object);
|
|
break;
|
|
#endif
|
|
#ifdef RT_USING_EVENT
|
|
case RT_Object_Class_Event:
|
|
rt_event_detach((rt_event_t)object);
|
|
break;
|
|
#endif
|
|
#ifdef RT_USING_MAILBOX
|
|
case RT_Object_Class_MailBox:
|
|
rt_mb_detach((rt_mailbox_t)object);
|
|
break;
|
|
#endif
|
|
#ifdef RT_USING_MESSAGEQUEUE
|
|
case RT_Object_Class_MessageQueue:
|
|
rt_mq_detach((rt_mq_t)object);
|
|
break;
|
|
#endif
|
|
#ifdef RT_USING_MEMHEAP
|
|
case RT_Object_Class_MemHeap:
|
|
rt_memheap_detach((struct rt_memheap*)object);
|
|
break;
|
|
#endif
|
|
#ifdef RT_USING_MEMPOOL
|
|
case RT_Object_Class_MemPool:
|
|
rt_mp_detach((struct rt_mempool*)object);
|
|
break;
|
|
#endif
|
|
case RT_Object_Class_Timer:
|
|
rt_timer_detach((rt_timer_t)object);
|
|
break;
|
|
default:
|
|
LOG_E("Unsupported oject type in module.");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (object_type)
|
|
{
|
|
case RT_Object_Class_Thread:
|
|
dlmodule_destroy_subthread(module, (rt_thread_t)object);
|
|
break;
|
|
#ifdef RT_USING_SEMAPHORE
|
|
case RT_Object_Class_Semaphore:
|
|
rt_sem_delete((rt_sem_t)object);
|
|
break;
|
|
#endif
|
|
#ifdef RT_USING_MUTEX
|
|
case RT_Object_Class_Mutex:
|
|
rt_mutex_delete((rt_mutex_t)object);
|
|
break;
|
|
#endif
|
|
#ifdef RT_USING_EVENT
|
|
case RT_Object_Class_Event:
|
|
rt_event_delete((rt_event_t)object);
|
|
break;
|
|
#endif
|
|
#ifdef RT_USING_MAILBOX
|
|
case RT_Object_Class_MailBox:
|
|
rt_mb_delete((rt_mailbox_t)object);
|
|
break;
|
|
#endif
|
|
#ifdef RT_USING_MESSAGEQUEUE
|
|
case RT_Object_Class_MessageQueue:
|
|
rt_mq_delete((rt_mq_t)object);
|
|
break;
|
|
#endif
|
|
#ifdef RT_USING_MEMHEAP
|
|
/* no delete operation */
|
|
#endif
|
|
#ifdef RT_USING_MEMPOOL
|
|
case RT_Object_Class_MemPool:
|
|
rt_mp_delete((struct rt_mempool*)object);
|
|
break;
|
|
#endif
|
|
case RT_Object_Class_Timer:
|
|
rt_timer_delete((rt_timer_t)object);
|
|
break;
|
|
default:
|
|
LOG_E("Unsupported oject type in module.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (module->cmd_line) rt_free(module->cmd_line);
|
|
/* release module symbol table */
|
|
for (i = 0; i < module->nsym; i ++)
|
|
{
|
|
rt_free((void *)module->symtab[i].name);
|
|
}
|
|
if (module->symtab != RT_NULL)
|
|
{
|
|
rt_free(module->symtab);
|
|
}
|
|
|
|
/* destory module */
|
|
rt_free(module->mem_space);
|
|
/* delete module object */
|
|
rt_object_delete((rt_object_t)module);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
struct rt_dlmodule *dlmodule_self(void)
|
|
{
|
|
rt_thread_t tid;
|
|
struct rt_dlmodule *ret = RT_NULL;
|
|
|
|
tid = rt_thread_self();
|
|
if (tid)
|
|
{
|
|
ret = (struct rt_dlmodule*) tid->module_id;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Compatible with old API
|
|
*/
|
|
struct rt_dlmodule *rt_module_self(void)
|
|
{
|
|
return dlmodule_self();
|
|
}
|
|
|
|
struct rt_dlmodule* dlmodule_load(const char* filename)
|
|
{
|
|
int fd, length = 0;
|
|
rt_err_t ret = RT_EOK;
|
|
rt_uint8_t *module_ptr = RT_NULL;
|
|
struct rt_dlmodule *module = RT_NULL;
|
|
|
|
fd = open(filename, O_RDONLY, 0);
|
|
if (fd >= 0)
|
|
{
|
|
length = lseek(fd, 0, SEEK_END);
|
|
lseek(fd, 0, SEEK_SET);
|
|
|
|
if (length == 0) goto __exit;
|
|
|
|
module_ptr = (uint8_t*) rt_malloc (length);
|
|
if (!module_ptr) goto __exit;
|
|
|
|
if (read(fd, module_ptr, length) != length)
|
|
goto __exit;
|
|
|
|
/* close file and release fd */
|
|
close(fd);
|
|
fd = -1;
|
|
}
|
|
|
|
/* check ELF header */
|
|
if (rt_memcmp(elf_module->e_ident, RTMMAG, SELFMAG) != 0 &&
|
|
rt_memcmp(elf_module->e_ident, ELFMAG, SELFMAG) != 0)
|
|
{
|
|
rt_kprintf("Module: magic error\n");
|
|
goto __exit;
|
|
}
|
|
|
|
/* check ELF class */
|
|
if (elf_module->e_ident[EI_CLASS] != ELFCLASS32)
|
|
{
|
|
rt_kprintf("Module: ELF class error\n");
|
|
goto __exit;
|
|
}
|
|
|
|
module = dlmodule_create();
|
|
if (!module) goto __exit;
|
|
|
|
/* set the name of module */
|
|
_dlmodule_set_name(module, filename);
|
|
|
|
LOG_D("rt_module_load: %.*s", RT_NAME_MAX, module->parent.name);
|
|
|
|
if (elf_module->e_type == ET_REL)
|
|
{
|
|
ret = dlmodule_load_relocated_object(module, module_ptr);
|
|
}
|
|
else if (elf_module->e_type == ET_DYN)
|
|
{
|
|
ret = dlmodule_load_shared_object(module, module_ptr);
|
|
}
|
|
else
|
|
{
|
|
rt_kprintf("Module: unsupported elf type\n");
|
|
goto __exit;
|
|
}
|
|
|
|
/* check return value */
|
|
if (ret != RT_EOK) goto __exit;
|
|
|
|
/* release module data */
|
|
rt_free(module_ptr);
|
|
|
|
/* increase module reference count */
|
|
module->nref ++;
|
|
|
|
/* deal with cache */
|
|
#ifdef RT_USING_CACHE
|
|
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, module->mem_space, module->mem_size);
|
|
rt_hw_cpu_icache_ops(RT_HW_CACHE_INVALIDATE, module->mem_space, module->mem_size);
|
|
#endif
|
|
|
|
/* set module initialization and cleanup function */
|
|
module->init_func = dlsym(module, "module_init");
|
|
module->cleanup_func = dlsym(module, "module_cleanup");
|
|
module->stat = RT_DLMODULE_STAT_INIT;
|
|
/* do module initialization */
|
|
if (module->init_func)
|
|
{
|
|
module->init_func(module);
|
|
}
|
|
|
|
return module;
|
|
|
|
__exit:
|
|
if (fd >= 0) close(fd);
|
|
if (module_ptr) rt_free(module_ptr);
|
|
if (module) dlmodule_destroy(module);
|
|
|
|
return RT_NULL;
|
|
}
|
|
|
|
struct rt_dlmodule* dlmodule_exec(const char* pgname, const char* cmd, int cmd_size)
|
|
{
|
|
struct rt_dlmodule *module = RT_NULL;
|
|
|
|
module = dlmodule_load(pgname);
|
|
if (module)
|
|
{
|
|
if (module->entry_addr)
|
|
{
|
|
/* exec this module */
|
|
rt_thread_t tid;
|
|
|
|
module->cmd_line = rt_strdup(cmd);
|
|
|
|
/* check stack size and priority */
|
|
if (module->priority > RT_THREAD_PRIORITY_MAX) module->priority = RT_THREAD_PRIORITY_MAX - 1;
|
|
if (module->stack_size < 2048 || module->stack_size > (1024 * 32)) module->stack_size = 2048;
|
|
|
|
tid = rt_thread_create(module->parent.name, _dlmodule_thread_entry, (void*)module,
|
|
module->stack_size, module->priority, 10);
|
|
if (tid)
|
|
{
|
|
tid->module_id = module;
|
|
module->main_thread = tid;
|
|
|
|
rt_thread_startup(tid);
|
|
}
|
|
else
|
|
{
|
|
/* destory dl module */
|
|
dlmodule_destroy(module);
|
|
module = RT_NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return module;
|
|
}
|
|
|
|
void dlmodule_exit(int ret_code)
|
|
{
|
|
rt_thread_t thread;
|
|
struct rt_dlmodule *module;
|
|
|
|
module = dlmodule_self();
|
|
if (!module) return;
|
|
|
|
/* disable scheduling */
|
|
rt_enter_critical();
|
|
|
|
/* module is not running */
|
|
if (module->stat != RT_DLMODULE_STAT_RUNNING)
|
|
{
|
|
/* restore scheduling */
|
|
rt_exit_critical();
|
|
|
|
return;
|
|
}
|
|
|
|
/* set return code */
|
|
module->ret_code = ret_code;
|
|
|
|
/* do exit for this module */
|
|
_dlmodule_exit();
|
|
/* the stat of module was changed to CLOSING in _dlmodule_exit */
|
|
|
|
thread = module->main_thread;
|
|
if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE)
|
|
{
|
|
/* main thread already closed */
|
|
rt_exit_critical();
|
|
|
|
return ;
|
|
}
|
|
|
|
/* delete thread: insert to defunct thread list */
|
|
rt_thread_delete(thread);
|
|
/* enable scheduling */
|
|
rt_exit_critical();
|
|
}
|
|
|
|
rt_uint32_t dlmodule_symbol_find(const char *sym_str)
|
|
{
|
|
/* find in kernel symbol table */
|
|
struct rt_module_symtab *index;
|
|
|
|
for (index = _rt_module_symtab_begin; index != _rt_module_symtab_end; index ++)
|
|
{
|
|
if (rt_strcmp(index->name, sym_str) == 0)
|
|
return (rt_uint32_t)index->addr;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rt_system_dlmodule_init(void)
|
|
{
|
|
#if defined(__GNUC__) && !defined(__CC_ARM)
|
|
extern int __rtmsymtab_start;
|
|
extern int __rtmsymtab_end;
|
|
|
|
_rt_module_symtab_begin = (struct rt_module_symtab *)&__rtmsymtab_start;
|
|
_rt_module_symtab_end = (struct rt_module_symtab *)&__rtmsymtab_end;
|
|
#elif defined (__CC_ARM)
|
|
extern int RTMSymTab$$Base;
|
|
extern int RTMSymTab$$Limit;
|
|
|
|
_rt_module_symtab_begin = (struct rt_module_symtab *)&RTMSymTab$$Base;
|
|
_rt_module_symtab_end = (struct rt_module_symtab *)&RTMSymTab$$Limit;
|
|
#elif defined (__IAR_SYSTEMS_ICC__)
|
|
_rt_module_symtab_begin = __section_begin("RTMSymTab");
|
|
_rt_module_symtab_end = __section_end("RTMSymTab");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
INIT_COMPONENT_EXPORT(rt_system_dlmodule_init);
|
|
|
|
/**
|
|
* This function will find the specified module.
|
|
*
|
|
* @param name the name of module finding
|
|
*
|
|
* @return the module
|
|
*/
|
|
struct rt_dlmodule *dlmodule_find(const char *name)
|
|
{
|
|
rt_object_t object;
|
|
struct rt_dlmodule *ret = RT_NULL;
|
|
|
|
object = rt_object_find(name, RT_Object_Class_Module);
|
|
if (object)
|
|
{
|
|
ret = (struct rt_dlmodule*) object;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
RTM_EXPORT(dlmodule_find);
|
|
|
|
int list_symbols(void)
|
|
{
|
|
extern int __rtmsymtab_start;
|
|
extern int __rtmsymtab_end;
|
|
|
|
/* find in kernel symbol table */
|
|
struct rt_module_symtab *index;
|
|
|
|
for (index = _rt_module_symtab_begin;
|
|
index != _rt_module_symtab_end;
|
|
index ++)
|
|
{
|
|
rt_kprintf("%s => 0x%08x\n", index->name, index->addr);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
MSH_CMD_EXPORT(list_symbols, list symbols information);
|
|
|
|
int list_module(void)
|
|
{
|
|
struct rt_dlmodule *module;
|
|
struct rt_list_node *list, *node;
|
|
struct rt_object_information *info;
|
|
|
|
info = rt_object_get_information(RT_Object_Class_Module);
|
|
list = &info->object_list;
|
|
|
|
rt_kprintf("module ref address \n");
|
|
rt_kprintf("-------- -------- ------------\n");
|
|
for (node = list->next; node != list; node = node->next)
|
|
{
|
|
module = (struct rt_dlmodule *)(rt_list_entry(node, struct rt_object, list));
|
|
rt_kprintf("%-*.*s %-04d 0x%08x\n",
|
|
RT_NAME_MAX, RT_NAME_MAX, module->parent.name, module->nref, module->mem_space);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
MSH_CMD_EXPORT(list_module, list modules in system);
|