【原文 使用Keil的MicroLIB時自動化佈建堆大小】
Keil編譯項目,如果使用微庫MicroLIB,就可以使用malloc。微庫內部位置一個堆管理模組。
晶片的RAM大小是固定了的,前面分為全域變數,後面分給堆和棧,這是一般開發方式。
但是我們在開發項目的過程中,市場遇到各種各樣問題,棧穿透到堆裡面,或者堆不夠大,相當煩人。
有時候就在想,何不讓全域變數以外的所有RAM給堆棧共用。
因為堆從低到高分配,而棧從高到低分配,理論上是可行的。
但是堆的分配由__heap_base和__heap_limit兩個標籤決定,不是變數又不能改。
因為我們使用很多種晶片,每一種晶片的RAM大小都有可能不同。
而SmartOS追求跨平台,不想為不同晶片做太多設定。
之前我們已經實現了通過修改MSP把棧頂移到RAM最高處,這樣子棧可以得到最大利用。
但是堆還是不好搞。
今晚再次遇到堆不夠用的情況,__heap_limit如果分配過大,在小容量晶片就會出錯。
忍無可忍,決定分析一下微庫是怎麼管理堆的。
首先開啟項目編譯後產生的連結地址對應檔Linker Address Map,我們這裡是SmartOSF0_Debug.map
找到符號表段Global Symbols
__heap_base 0x200005a0 Data 0 startup_stm32f0xx.o(HEAP)
__heap_limit 0x200005a0 Data 0 startup_stm32f0xx.o(HEAP)
__initial_sp 0x200005c0 Data 0 startup_stm32f0xx.o(STACK)
從這裡可以看出,堆棧已經分配好了。
堆分配使用的是malloc函數,上圖找到它位於Keil庫檔案mc_p.l中
我的目錄是D:\Keil\ARM\ARMCC\lib\armlib
輪到法寶IDA上陣,選擇malloc.o,太簡單了,只有malloc/free兩個函數
彙編圖形介面如下:
手頭的IDA沒有ARM外掛程式,否則一個F5就可以得到malloc的C原始碼。
好好工作賺錢賣ARM外掛程式吧。。。
沒有工具輔助,那就自己來寫吧。
上面是手工寫的C代碼,被注釋的是最原始的彙編寫法,然後逐步精簡最佳化。
大概摸清楚了malloc的機制,關鍵點在於初始化那裡,用到了__heap_limit
而__heap_limit作為常量被編譯到Flash裡面去了,記憶體裡面無法動態修改。
實在沒辦法,只好位元組寫代碼來接替它初始化整個堆了。
我寫的初始化代碼如下:
附上malloc/free代碼,不完整,只能大概瞭解它的機制:
void free(void* p) { if(!p) return 0; r3 = __microlib_freelist; void* r2 = 0; p -= 4; void* r1 = *r3; // r1 = r3->node while(r1) { if(r1 > p) break; r2 = r1; r1 = *(r1 + 4); // r1 = r1->next } if(!r2) r3->node = p; else { r3 = *r2; r4 = p - r2; if(r4 != r3) *(r2 + 4) = p; else { p = *p; p += r3; } } } typedef struct { uint size; void* next; } Node; __microlib_freelist: Node* _freelist; __microlib_freelist_initialised: int _freelist_initialised = 0; void* malloc(int size) { /*r0 += 0x0b; r0 >>= 3; r0 <<= 3;*/ r1 = (size + 11) & 0xFFFFFFF8; r7 = __microlib_freelist_initialised; r6 = 0; /*r2 = __microlib_freelist; if(!*r2 && !*r7)*/ if(!_freelist && !_freelist_initialised) { /*r2 = __microlib_freelist; r0 = __heap_base + 4; *r2 = r0; r2 = __heap_limit; r2 -= r0; r2 &= 0xFFFFFFF0; *r0 = r2; *(r0 + 4) = 0; *r7 = 1;*/ _freelist = (Node*)(__heap_base + 4); _freelist->size = (__heap_limit - __heap_base - 4) & 0xFFFFFFF8; _freelist->next = 0; _freelist_initialised = 1; } //r2 = __microlib_freelist; r0 = _freelist; while(true) { /*r0 = *r2; if(!r0) { r0 = r6; break; }*/ //r0 = *r2; if(!r0) return 0; //r3 = *r0; r3 = r0->size; if(r3 <= r1) break; //r2 = r0 + 4; //r2 = r0->next; r0 = *(r0->next); } if(r3 <= r1) { //r3 = *(r0 + 4); /*r3 = _freelist->next; *r2 = r3;*/ _freelist->size = r1; _freelist = _freelist->next; return &; } else { /*r4 = r3 - r1; r3 = r0 + r1; r5 = *(r0 + 4); *r3 = r4; r3 +=4 ; *(r3 + 4) = r5; r3 +=4 ; r3 -= 8;// 減8為了回到r3開始*/ Node* lst = (void*)_freelist + r1; lst->size = _freelist->size - r1; lst->next = _freelist->next; _freelist->size = r1; _freelist = lst; return &lst->next; } return r0; }