前言
- RT-Thread的自动初始化机制,挺好用的,但是,bsp中模拟器simulator,不支持。
- 研究了一下自动初始化机制,发现,可以让pc的模拟器,也可以实现自动初始化
研究过程
- 运行bsp的模拟器,使用vs2019。
- 发现:INIT_BOARD_EXPORT INIT_COMPONENT_EXPORT,没有效果
- 分析代码发现默认不支持PC端的模拟器:
/* initialization export */
#ifdef RT_USING_COMPONENTS_INIT
typedef int (*init_fn_t)(void);
#ifdef _MSC_VER /* we do not support MS VC++ compiler */
#define INIT_EXPORT(fn, level)
#else
- 经过一番的搜索、验证分析,发现,自动初始化机制如下:
- 需要使用section(段、节),让编译器把函数指针,放在里面
- __attribute__((section(x))) 在VS2019上不支持(_MSC_VER)
- 找到基于VS2019 _MSC_VER的section操作,即可实现自动初始化
- MSH_CMD_EXPORT,支持模拟器的section的操作,用于导出MSH CMD函数
基于VS2019的section的操作:
#pragma section(".rti_fn.$0")
#define INIT_EXPORT_LEVEL0(fn) \
__declspec(allocate(".rti_fn.$0")) const init_fn_t __rt_init_##fn = fn
- 为啥不直接使用:(".rti_fn." level),因为编译报错
修改代码
rtdef.h 修改
/* initialization export */
#ifdef RT_USING_COMPONENTS_INIT
typedef int (*init_fn_t)(void);
#ifdef _MSC_VER /* we do not support MS VC++ compiler */
#pragma section(".rti_fn.$0")
#pragma section(".rti_fn.$1")
#pragma section(".rti_fn.$2")
#pragma section(".rti_fn.$3")
#pragma section(".rti_fn.$4")
#pragma section(".rti_fn.$5")
#pragma section(".rti_fn.$6")
#pragma section(".rti_fn.$0.end")
#pragma section(".rti_fn.$1.end")
#pragma section(".rti_fn.$6.end")
#define INIT_EXPORT_LEVEL0(fn) \
__declspec(allocate(".rti_fn.$0")) const init_fn_t __rt_init_##fn = fn
#define INIT_EXPORT_LEVEL0_END(fn) \
__declspec(allocate(".rti_fn.$0.end")) const init_fn_t __rt_init_##fn = fn
#define INIT_EXPORT_LEVEL1_END(fn) \
__declspec(allocate(".rti_fn.$1.end")) const init_fn_t __rt_init_##fn = fn
#define INIT_EXPORT_LEVEL6_END(fn) \
__declspec(allocate(".rti_fn.$6.end")) const init_fn_t __rt_init_##fn = fn
#define INIT_EXPORT_LEVEL1(fn) \
__declspec(allocate(".rti_fn.$1")) const init_fn_t __rt_init_##fn = fn
#define INIT_EXPORT_LEVEL2(fn) \
__declspec(allocate(".rti_fn.$2")) const init_fn_t __rt_init_##fn = fn
#define INIT_EXPORT_LEVEL3(fn) \
__declspec(allocate(".rti_fn.$3")) const init_fn_t __rt_init_##fn = fn
#define INIT_EXPORT_LEVEL4(fn) \
__declspec(allocate(".rti_fn.$4")) const init_fn_t __rt_init_##fn = fn
#define INIT_EXPORT_LEVEL5(fn) \
__declspec(allocate(".rti_fn.$5")) const init_fn_t __rt_init_##fn = fn
#define INIT_EXPORT_LEVEL6(fn) \
__declspec(allocate(".rti_fn.$6")) const init_fn_t __rt_init_##fn = fn
#else
#if RT_DEBUG_INIT
struct rt_init_desc
{
const char* fn_name;
const init_fn_t fn;
};
#define INIT_EXPORT(fn, level) \
const char __rti_##fn##_name[] = #fn; \
RT_USED const struct rt_init_desc __rt_init_desc_##fn RT_SECTION(".rti_fn." level) = \
{ __rti_##fn##_name, fn};
#else
#define INIT_EXPORT(fn, level) \
RT_USED const init_fn_t __rt_init_##fn RT_SECTION(".rti_fn." level) = fn
#endif
#endif
#else
#define INIT_EXPORT(fn, level)
#endif
/* board init routines will be called in board_init() function */
#ifdef _MSC_VER
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT_LEVEL1(fn)
#else
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
#endif
/* pre/device/component/env/app init routines will be called in init_thread */
/* components pre-initialization (pure software initilization) */
#ifdef _MSC_VER
#define INIT_PREV_EXPORT(fn) INIT_EXPORT_LEVEL2(fn)
#else
#define INIT_PREV_EXPORT(fn) INIT_EXPORT(fn, "2")
#endif
/* device initialization */
#ifdef _MSC_VER
#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT_LEVEL3(fn)
#else
#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3")
#endif
/* components initialization (dfs, lwip, ...) */
#ifdef _MSC_VER
#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT_LEVEL4(fn)
#else
#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")
#endif
/* environment initialization (mount disk, ...) */
#ifdef _MSC_VER
#define INIT_ENV_EXPORT(fn) INIT_EXPORT_LEVEL5(fn)
#else
#define INIT_ENV_EXPORT(fn) INIT_EXPORT(fn, "5")
#endif
/* appliation initialization (rtgui application etc ...) */
#ifdef _MSC_VER
#define INIT_APP_EXPORT(fn) INIT_EXPORT_LEVEL6(fn)
#else
#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")
#endif
components.c 修改:
static int rti_start(void)
{
return 0;
}
#ifdef _MSC_VER
INIT_EXPORT_LEVEL0(rti_start);
#else
INIT_EXPORT(rti_start, "0");
#endif
static int rti_board_start(void)
{
return 0;
}
#ifdef _MSC_VER
INIT_EXPORT_LEVEL0_END(rti_board_start);
#else
INIT_EXPORT(rti_board_start, "0.end");
#endif
static int rti_board_end(void)
{
return 0;
}
#ifdef _MSC_VER
INIT_EXPORT_LEVEL1_END(rti_board_end);
#else
INIT_EXPORT(rti_board_end, "1.end");
#endif
static int rti_end(void)
{
return 0;
}
#ifdef _MSC_VER
INIT_EXPORT_LEVEL6_END(rti_end);
#else
INIT_EXPORT(rti_end, "6.end");
#endif
/**
* RT-Thread Components Initialization for board
*/
void rt_components_board_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
if (*fn_ptr != RT_NULL)
(*fn_ptr)();
}
#endif
}
/**
* RT-Thread Components Initialization
*/
void rt_components_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
rt_kprintf("do components initialization.\n");
for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
if (*fn_ptr != RT_NULL)
(*fn_ptr)();
}
#endif
}
#endif /* RT_USING_COMPONENTS_INIT */
使能:RT_USING_COMPONENTS_INIT
可以配置Kconfig实现定义
在 simulator的:application.c中,增加:
void rt_init_thread_entry(void *parameter)
{
rt_kprintf("Hello RT-Thread!\n");
rt_components_board_init();
rt_components_init();
platform_init();
mnt_init();
增加自动初始化测试例程:
#include <rtthread.h>
int func_a1(void)
{
rt_kprintf("%s: init ok!\n", __func__);
return 0;
}
int func_a2(void)
{
rt_kprintf("%s: init ok!\n", __func__);
return 0;
}
int func_a3(void)
{
rt_kprintf("%s: init ok!\n", __func__);
return 0;
}
int func_a4(void)
{
rt_kprintf("%s: init ok!\n", __func__);
return 0;
}
int func_a5(void)
{
rt_kprintf("%s: init ok!\n", __func__);
return 0;
}
int func_a6(void)
{
rt_kprintf("%s: init ok!\n", __func__);
return 0;
}
int func_a7(void)
{
rt_kprintf("%s: init ok!\n", __func__);
return 0;
}
int func_a8(void)
{
rt_kprintf("%s: init ok!\n", __func__);
return 0;
}
INIT_BOARD_EXPORT(func_a1);
INIT_PREV_EXPORT(func_a2);
INIT_DEVICE_EXPORT(func_a3);
INIT_COMPONENT_EXPORT(func_a4);
INIT_ENV_EXPORT(func_a5);
INIT_APP_EXPORT(func_a6);
INIT_DEVICE_EXPORT(func_a7);
INIT_ENV_EXPORT(func_a8);
功能验证
- 编译通过(最新的RT-Thread 代码:报timegm 未定义,先注释掉)
- 运行:
\ | /
- RT - Thread Operating System
/ | \ 4.0.3 build Mar 13 2021
2006 - 2021 Copyright by rt-thread team
Hello RT-Thread!
func_a1: init ok!
func_a2: init ok!
func_a3: init ok!
func_a7: init ok!
func_a4: init ok!
func_a5: init ok!
func_a8: init ok!
func_a6: init ok!
finsh shell already init.
dfs already init.
File System on root initialized!
[E/DFS] There is no space to mount this file system (elm).
File System on sd initialization failed!
msh />
- 发现自动初始化的函数,执行了。有些手动执行的函数,可以注释掉了。
总结
- 需要了解VS(Visual Studio)环境下,section的操作。
- 可以借助PC端的工具:OD、Die等工具,分析PE文件,了解生成的执行文件section内容
- 使用OD反编译生成的exe文件,查找函数地址、section
- 使用Die,查看exe文件的section