RT-Thread版本:4.0.5
MCU型号:STM32F103RCT6(ARM Cortex-M3 内核)
1 功能介绍
$Super$$foo
标识原函数foo()
,调用$Super$$foo
相当于直接调用foo()
。$Sub$$foo
标识被调用的新函数,而不是原函数foo
。在原函数之前或之后添加处理,即可在$Super$$foo
调用前后都可以执行一些语句。
2 应用场景
- 对于无法修改或重新编译现有符号的情况,可以使用
$Sub
和$Super
链接机制。例如,它位于外部库或ROM代码中。在这种情况下,您可以使用$Super$$
和$Sub$$
模式来修补现有的符号。 - 通常,将所有系统初始化代码与主应用程序分开是有益的。但是,系统初始化的某些组件,例如缓存和中断的启用,必须在执行 C 库初始化代码之后发生。
- 在RT-Thread中的应用:在进入
main
函数前,需要对系统进行初始化(时钟、中断、动态内存堆、定时器、调度器、main
线程、idle
线程等),使用$Sub$$main
先调用一些要补充在main
之前的功能函数,然后在再调用$Super$$main
转到main
函数执行。
3 应用示例
extern int $Super$$main(void); // 外部声明函数(由链接器导入)
void $Sub$$main(void)
{
HSE_SetSysClk(RCC_PLLMul_9);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SysTick_Config(SystemCoreClock / 1000);
Usart_Init(115200);
UART_DEBUG("$Sub$$main: init over!");
$Super$$main();
// main(); // 不能直接调用main
}
//__inline void func(void) // 不可以是内联函数 Error:Not enough information to list image symbols.
//{
// UART_DEBUG("func: hello");
//}
void func(void)
{
UART_DEBUG("func: hello");
}
extern int $Super$$func(void);
void $Sub$$func(void)
{
UART_DEBUG("$Sub$$func: start");
$Super$$func();
UART_DEBUG("$Sub$$func: end");
}
int main(void)
{
UART_DEBUG("main: hello");
// $Sub$$main(); // 递归调用$Sub$$main
// $Super$$main(); // 递归调用main自身
UART_DEBUG("----------------------");
func(); // 这里的func相当于$Sub$$func();
UART_DEBUG("----------------------");
$Super$$func(); // 真正的func
while(1) // 不加while(1)程序会一直复位,这是mdk做的处理, 避免程序跑飞
{
}
}
打印信息:
[UART DEBUG] [17]$Sub$$main: init over!
[UART DEBUG] [38]main: hello
[UART DEBUG] [41]----------------------
[UART DEBUG] [31]$Sub$$func: start
[UART DEBUG] [25]func: hello
[UART DEBUG] [33]$Sub$$func: end
[UART DEBUG] [44]----------------------
[UART DEBUG] [25]func: hello
4 注意事项
$Sub
与$Super
要成对使用,而且要在$Sub$$foo
中调用$Super$$foo
,不能直接调用foo
,否则会一直卡在$Sub$$foo
中,找不到出口,陷入死循环。- 在
$Super$$foo
函数调用上方需要用extern
关键字外部声明它,即extern void $Super$$foo(void)
。 $Sub$$foo
、$Super$$foo
可以与原函数foo
放在不同的C文件中,而且放在同一个C文件中也没有先后顺序之分。- 不能在原函数
foo
中,调用$Sub$$foo
和$Super$$foo
,这属于递归调用。 $Sub
和$Super
机制只在静态链接时起作用,$Super
引用不能导入或导出到动态符号表中。foo
函数必须是全局或弱定义,如果编译器内联了一个函数,那么就不可以再用替换函数$Sub
来修补内联的函数。$Sub
和$Super
链接器机制只能对工具可见的符号定义和引用进行操作。例如,编译器可以在C程序中将对printf("Hello\n")
的调用替换为puts("Hello")
。只有对符号puts
的引用对链接器可见,因此定义$Sub$$printf
不会重定向此调用。(ps:这是官方解释,个人猜测应该是做了宏替换处理)
参考资料:
- Use of S u p e r Super Super$ and S u b Sub Sub$ to patch symbol definitions
- Use of $Sub and $Super
- 关于 $ Super $ $ 和 $ Sub $ $ 的用法
END