/* * 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 #include "dlfcn.h" #include "dlmodule.h" #include "dlelf.h" #if defined(RT_USING_POSIX) #include #endif #define DBG_TAG "DLMD" #define DBG_LVL DBG_INFO #include // 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) { #if defined(RT_USING_POSIX) int fd = -1, length = 0; #endif rt_err_t ret = RT_EOK; rt_uint8_t *module_ptr = RT_NULL; struct rt_dlmodule *module = RT_NULL; #if defined(RT_USING_POSIX) 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; } else { goto __exit; } #endif if (!module_ptr) goto __exit; /* 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 defined(RT_USING_POSIX) if (fd >= 0) close(fd); #endif 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; } #if defined(RT_USING_CUSTOM_DLMODULE) struct rt_dlmodule* dlmodule_load_custom(const char* filename, struct rt_dlmodule_ops* ops) { #if defined(RT_USING_POSIX) int fd = -1, length = 0; #endif rt_err_t ret = RT_EOK; rt_uint8_t *module_ptr = RT_NULL; struct rt_dlmodule *module = RT_NULL; if (ops) { RT_ASSERT(ops->load); RT_ASSERT(ops->unload); module_ptr = ops->load(filename); } #if defined(RT_USING_POSIX) else { 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; } else { goto __exit; } } #endif if (!module_ptr) goto __exit; /* 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 */ if (ops) { ops->unload(module_ptr); } else { 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 defined(RT_USING_POSIX) if (fd >= 0) close(fd); #endif if (module_ptr) { if (ops) { ops->unload(module_ptr); } else { rt_free(module_ptr); } } if (module) dlmodule_destroy(module); return RT_NULL; } struct rt_dlmodule* dlmodule_exec_custom(const char* pgname, const char* cmd, int cmd_size, struct rt_dlmodule_ops* ops) { struct rt_dlmodule *module = RT_NULL; module = dlmodule_load_custom(pgname, ops); 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; } #endif 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);