在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