|
雄辯是銀, 沉默是金 |
這個系列的最後一篇是 2016, 過去 6 年了, 很可惜我還是沒寫出一個 os for stm32f4discovery, 有些忘記 linker script 的用法了, 用這篇文章來介紹 linker script, 當然, 只是入門等級的介紹。這個應該要早點介紹的, 不過怎麼開發 stm32f4 比較要緊, linker script 就這麼被耽擱了。
找資料期間發現一篇寫的很不錯的文章「
linker script 簡單教學」, 真的很高興不用總是讀那些簡體中文文章, 不要誤會, 簡體中文文章本身沒問題, 但我總是不習慣中國人的用語, 難得可以看到本土的技術文章。
linker script 最主要是要讓執行檔長成可以執行的樣子, 這是什麼意思呢? 就是字面上的意思。每個平台都不同, 例如 cortex-m 前 4 個 byte 是 stack address, 所以就必須想辦法讓執行檔的前 4 byte 指向設定好的 stack 區域, 那你說不是應該要指定 sp 暫存器來設定嗎? 一般來說是這樣, 不過 cortex-m 不需要使用組合語言就可以設定 sp 暫存器。可以參考:
作業系統之前的程式 for stm32f4 - discovery (1) - 1 加到 10 , asm version
因為這是 bare-metal 程式, 所以才會有這樣的需求, 如果是 linux/windows 執行檔, 就不需要這麼嚴格讓執行檔長成某個樣子。
當然, 我自己是寫不出來這個 linker script, 這是從某本書上抄來的, 一般開發商付的 sdk 也會有一個 linker script。
最容易讓我困擾的是 VMA/LMA, 老是記不住誰是誰? 可以使用以下指令觀察 VMA/LMA arm-none-eabi-objdump -h mygpio_led.elf, 這個指令很有幫助, 讓我可以區分 VMA/LMA, 使用 readelf 我不知道怎麼讀出這個資訊。
觀察 list 2. 就可以發現 .data section VMA 是 20000000, LMA 是 08000138。mygpio_led.c L4 變數 val 就是位於這裡, 那應該會有個疑問, 當我 printf &val 時, 位址是 20000000 還是 08000138?
list 5. L28 拿掉 AT (_etext), 再觀察 list 6. L8 就可以發現 .data section VMA, LMA 都是 20000000。
val 變數位址是 0x20000000 還是 0x08000138 有什麼差異呢? 這大概要有碰過 embedded system 的開發人員才有體會, 0x08000138 是 flash 的位址, 讀的時候沒問題, 可以 int c = val, 但是寫的話 val = 6, 是無法作到的 (ddr 位址範圍才可以這麼做), flash 有一套寫的指令, 需要一點複雜的操作才可以寫入, 但對 c 程式員來說, val=6 就要完成這個操作, c 程式員不應該還要去煩心這是 flash 還是 ddr 位址。
所以當作 val = 6 時, 是使用 0x20000000 位址, 也就是 6 會寫到 ddr 0x20000000 的位址上。那 0x08000138 是幹麻用的? 如果你 printf("val: %#x\n", val), 應該會預期要得到 0x123456ab, 但開機時, ddr 應該是隨機值, 有可能會是 0x123456ab 嗎? 所以 c runtime 有一段 code 把 0x123456ab 複製到 0x20000000, 那麼要從哪裡找到 0x123456ab, 答對了, 就是從 0x08000138 把 0x123456ab 複製到 0x20000000 (stm32.h L20, 21)。放在 data section 的變數都要這樣處理, 這就是 c runtime 的祕密, 每個平台的 c runtime 要做的事情都不同, 像是 rpi2 就不用這麼做, 這是 cortex M 獨有的。
這引發另外一個問題, 要怎麼確定把這個執行檔燒錄到 flash 時, 0x08000138 正好是 0x123456ab? list 7 0x138 剛好是 123456ab, 這個就是 val, 在寫入 mygpio_led.bin 到 flash 位址 0x08000000, 就變成 0x08000138, 這就是整個被 linker script 安排好的魔法, 除了靠 linker script, 也要靠 c runtime 的 code。
另外 bss section 的值都要是 0, 也是用類似的作法完成的, 取得 bss 開始位址和結束位址 (list 1 L34 ~ L41, stm32.h L22 ~ L23), 把整個 bss 設定為 0 (stm32.h L22, 23)。cortex M 只要做這樣, 就完成 c runtime, 之後就可以用 c 語言了。當然, 如果寫程式都只用組合語言, 就不必做這些事情。
linker script 當然不是只有這麼簡單, 這只是很基本的介紹。
ref:
GNU LD 手冊略讀 (2): Chapter 3.6 SETCIONS (這是一位朋友寫的, 前陣子得知其往生的消息, 令人感傷, 感謝他的文章)
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。