Dynamic Modules

In traditional desktop operating systems, user space and kernel space are separate. Applications run in user space, while kernels and kernel modules run in kernel space. Kernel modules can be dynamically loaded and deleted to extend kernel functions. dlmoduleUnder RT-Thread, it is a software component that provides a dynamic module loading mechanism in kernel space. In versions before RT-Thread v3.1.0, this is also called an application module. In RT-Thread v3.1.0 and later, it returns to tradition and is named as a dynamic module.

dlmoduleThe component is more of an ELF format loader, which loads the code segment and data segment of a separately compiled elf file into memory, parses the symbols in it, and binds them to the API address exported by the kernel. The dynamic module elf file is mainly placed on the file system under RT-Thread.

Dynamic modules provide RT-Thread with a mechanism for dynamically loading program modules. Since they are also independent of kernel compilation, they are more flexible to use. In terms of implementation, this is a mechanism that separates the kernel and dynamic modules. Through this mechanism, the kernel and dynamic modules can be compiled separately, and the compiled dynamic modules can be loaded into the kernel through the module loader in the kernel at runtime.

In RT-Thread's dynamic modules, two formats are currently supported:

  • .mo.moIt is an executable dynamic module with the suffix of as compiled ; it can be loaded, and the system will automatically create a main thread to execute mainthe function in this dynamic module; at the same time, this main(int argc, char**argv)function can also accept parameters on the command line.

  • .so.soIt is a dynamic library with as the suffix when compiled ; it can be loaded and reside in the memory, and provides some function sets to be used by other programs (code in the kernel or dynamic modules).

Currently, RT-Thread supports dynamic modules for architectures such as ARM and x86, and will be extended to MIPS and RISC-V in the future. The RT-Thread kernel firmware can use a variety of compiler tool chains, such as GCC, ARMCC, IAR, etc., but the dynamic module part currently only supports GNU GCC tool chain compilation. Therefore, to compile the RT-Thread module, you need to download the GCC tool, such as CodeSourcery's arm-none-eabi tool chain. In general, it is best to compile the kernel and dynamic modules with the same tool chain (so that inconsistent behavior will not occur on libc). In addition, dynamic modules can generally only be loaded into RAM for use, and symbol resolution is performed and bound to the API address exported by the kernel, and they cannot be run directly in XIP mode based on Flash (because the code segments in Flash cannot be modified anymore).

When you want to test and use dynamic modules in the system, you need to compile a firmware that supports dynamic modules and the dynamic modules that need to be run. The following describes the compilation methods of firmware and dynamic modules in two parts.

When you want to use dynamic modules, you need to open the corresponding options in the firmware configuration. Use menuconfig to open the following configuration:

   RT-Thread Components  --->
       POSIX layer and C standard library  --->
           [*] Enable dynamic module with dlopen/dlsym/dlclose featurecopymistakeCopy Success

Also open the file system configuration options:

   RT-Thread Components  --->
        Device virtual file system  --->
               [*] Using device virtual file systemcopymistakeCopy Success

In rtconfig.py corresponding to bsp, set the configuration parameters needed for dynamic module compilation:

M_CFLAGS = CFLAGS + ' -mlong-calls -fPIC '
M_CXXFLAGS = CXXFLAGS + ' -mlong-calls -fPIC'
M_LFLAGS = DEVICE + CXXFLAGS + ' -Wl,--gc-sections,-z,max-page-size=0x4' +\
                                ' -shared -fPIC -nostartfiles -nostdlib -static-libgcc'
M_POST_ACTION = STRIP + ' -R .hash $TARGET\n' + SIZE + ' $TARGET \n'
M_BIN_PATH = r'E:\qemu-dev310\fatdisk\root'copymistakeCopy Success

The relevant explanations are as follows:

  • M_CFLAGS - C code compilation parameters used when compiling dynamic modules. Generally, it is compiled in PIC mode (that is, the code address supports floating mode execution);

  • M_CXXFLAGS - C++ code compilation parameters used when compiling dynamic modules. The parameters M_CFLAGSare similar to those above;

  • M_LFLAGS - parameters for dynamic modules when linking. Also PIC mode, and linking as a shared library (partial link);

  • M_POST_ACTIOn - the action to be performed after the dynamic module is compiled. Here, the elf file will be stripped to reduce the size of the elf file;

  • M_BIN_PATH - when the dynamic module is compiled successfully, whether the corresponding dynamic module file needs to be copied to a unified place;

Basically, these compilation configuration parameters for ARM9, Cortex-A, and Cortex-M series are the same.

The kernel firmware will also RTM(function)export some function APIs for dynamic modules to use. These exported symbols can be used in msh through the command:

list_symbols

List all exported symbol information in the firmware. dlmoduleThe loader also parses the symbols that need to be parsed in the dynamic module according to the symbol table exported here to complete the final binding action.

This symbol table will be placed in a special section named RTMSymTab, so the corresponding firmware link script also needs to retain this area and will not be removed by linker optimization. You can add the corresponding information in the link script:

/* section information for modules */
. = ALIGN(4);
__rtmsymtab_start = .;
KEEP(*(RTMSymTab))
__rtmsymtab_end = .;copymistakeCopy Success

Then sconsexecute the following command in the BSP project directory:

scons --target=ua -s

To generate the kernel header file search path and global macro definitions that need to be included when compiling dynamic modules.

There is an independent repository on github: rtthread-apps , which contains some examples related to dynamic modules and dynamic libraries.

The directory structure is as follows:

You can clone this git repository locally, and then compile it using the scons tool in the command line. If you are using a Windows platform, it is recommended to use the RT-Thread/ENV tool.

After entering the console command line, go to the directory where the rtthread-apps repo is located (again, please ensure that the full path of this directory does not contain spaces, Chinese characters, etc.) and set two variables:

  • RTT_ROOT - points to the root directory of RT-Thread code;

  • BSP_ROOT - points to the BSP project directory;

It can be used under Windows (assuming the BSP used is qemu-vexpress-a9):

set RTT_ROOT=d:\your_rtthread
set BSP_ROOT=d:\your_rtthread\bsp\qemu-vexpress-a9copymistakeCopy Success

To set the corresponding environment variables. Then use the following command to compile the dynamic module, such as the hello example:

scons --app=hello

After successful compilation, it will generate hello.mo file in rtthread-apps/hello directory.

You can also compile dynamic libraries, such as lib:

scons --lib=lib

After successful compilation, it will generate lib.so file in rtthread-apps/lib directory.

We can put these mo and so files into the RT-Thread file system. In msh, we can simply helloexecute hello.mothe dynamic module in command mode:

msh />ls
Directory /:
hello.mo            1368
lib.so              1376
msh />hello
msh />Hello, worldcopymistakeCopy Success

After calling hello, the main function in hello.mo will be executed, and the corresponding dynamic module will be exited after the execution is completed. hello/main.cThe code is as follows:

#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("Hello, world\n");

    return 0;
}copymistakeCopy Success

In addition to directly loading and executing dynamic modules through msh, you can also use the dynamic module API provided by RT-Thread in the main program to load or unload dynamic modules.

struct rt_dlmodule *dlmodule_load(const char* pgname);copymistakeCopy Success

This function loads a dynamic module from the file system into memory and returns a pointer to the module if it is loaded correctly. This function does not create a thread to execute the dynamic module, but only loads the module into memory and resolves the symbol address.

struct rt_dlmodule *dlmodule_exec(const char* pgname, const char* cmd, int cmd_size);copymistakeCopy Success

This function pgnameloads the dynamic module according to the path and starts a thread to execute mainthe function of this dynamic module. At the same time, cmdit is passed as a command line parameter to mainthe function entry of the dynamic module.

void dlmodule_exit(int ret_code);copymistakeCopy Success

This function is called by the module runtime to set the module's exit return value ret_codeand then exit from the module.

struct rt_dlmodule *dlmodule_find(const char *name);copymistakeCopy Success

This function namechecks whether there is a dynamic module already loaded in the system.

struct rt_dlmodule *dlmodule_self(void);copymistakeCopy Success

This function returns a pointer to the dynamic module in the calling context.

rt_uint32_t dlmodule_symbol_find(const char *sym_str);copymistakeCopy Success

This function returns the address of a symbol given its name.

The POSIX standard libdl API is also supported in RT-Thread dlmodule, which is similar to loading a dynamic library into memory (and parsing some symbol information in it), and the dynamic library provides the corresponding function operation set. The header files that libdl API needs to include are:

#include <dlfcn.h>

void * dlopen (const char * pathname, int mode);copymistakeCopy Success

This function is similar to dlmodule_load, which will load the dynamic library from the file system and return the handle pointer of the dynamic library.

void* dlsym(void *handle, const char *symbol);copymistakeCopy Success

This function handlesearches for symbolthe symbol of in the dynamic library and returns its address if it exists.

int dlclose (void *handle);copymistakeCopy Success

This function will close handlethe dynamic library pointed to by and unload it from the memory. It should be noted that when the dynamic library is closed, dlsymthe symbol address originally returned by will no longer be available. If you still try to access it, it may cause a fault error.

A: Please update the RT-Thread source code to version 3.1.0 or above.

A: Please refer to the GCC link script file link.lds of the qemu-vexpress-a9 BSP and add the following content to the TEXT section of the GCC link script of the project.

    /* section information for modules */
    . = ALIGN(4);
    __rtmsymtab_start = .;
    KEEP(*(RTMSymTab))
    __rtmsymtab_end = .;copymistakeCopy Success

Last updated

Assoc. Prof. Wiroon Sriborrirux, Founder of Advance Innovation Center (AIC) and Bangsaen Design House (BDH), Electrical Engineering Department, Faculty of Engineering, Burapha University