在MDK開發環境下,對於某些無法被更改的函數,我們需要更改這些函數執行前后的邏輯,偏偏又無法更改到調用這些函數並已經被封裝的代碼,這真是讓人一籌莫展。
幸好MDK給我們留了一個后門,讓我們充分使用“$Sub$$”和“$Super$$”來完成這個目標。
比如某個函數
extern void foo(void);
我們要在它執行的前后進行一段特定的處理,那么參考下面的代碼:
/*定義 foo 函數*/
void foo(void)
{
printf("f()\n");
}
/*定義foo的補丁函數*/
void $Sub$$foo(void)
{
/*需要在 foo() 之前執行的代碼塊*/
printf("before_foo()\n");
/*調用 foo(),要注意使用前綴"$Super$$"*/
$Super$$foo();
/*需要在 foo() 之后執行的代碼塊*/
printf("execute_after_f()\n");
}
/*
疑問:我定義 printf 的補丁函數 $Sub$$printf,
實際調用 printf 的時候,並沒有進入 $Sub$$printf,
這是為何?
*/
int $Sub$$printf(const char *fmt, ...)
{
$Super$$printf("<*> ");
return $Super$$printf(fmt);
}
int main(void)
{
uint32_t limitedTick;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
tick_initialize();
trace_initialize(user_command_deal);
printf("\n execute \"foo();\" result:\n\n");
/*
直接寫作"foo();",理論上應該看到
<*> before_foo(),
<*> f(),
<*> after_foo()
三行日志,實際上前面的“<*>”沒有輸出。
也就是說,printf函數的補丁沒生效,foo的補丁生效了。
為什么呢?
*/
foo();
/*采用 $Sub$$printf 的形式去調用的話就失去意義了。*/
$Sub$$printf("i am printed by \"$Sub$$printf\".\n");
while(1);
return 0;
}


注意:
1、必須定義一個新的以“$Sub$$”作為前綴,后面緊跟“欲被處理的函數名”的函數,例如 “$Sub$$foo()”.
2、在補丁函數里面需要以 “$Super$$foo()” 的方式去調用原始的 foo 函數,而不應該直接寫作 “foo()”去調用。
(不要“$Super$$”前綴也能調用到,不建議這么做。感覺要是不加前綴,就好像進入無限嵌套一樣的,不斷的調用自己。)
3、這個特性僅在ARM CC編譯器內被支持,所以一般都會使用宏“#if defined(__CC_ARM)”來囊括處理。
4、既然是針對 foo 的補丁,在調用的地方自然就應該寫作 "foo();",而不是 "$Sub$$foo();",要不然補丁有何意義呢?
疑問:
為什么我針對printf的補丁不生效呢?
經過調試,發現printf被替換成了 __2printf,如果針對 printf 這個“詞”進行 $Sub$$ 前綴化,是達不到目標的,改成 $Sub$$__2printf 之后,再使用 printf 的時候就實際執行了 $Sub$$__2printf 。
搞不懂為什么 printf 會變成 __2printf。



參考:
https://www.cnblogs.com/raswin/p/10031117.html
https://blog.csdn.net/qq_42860728/article/details/89495882
