色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何開發與位置無關的 STM32 完整工程

MCU開發加油站 ? 來源:STM32單片機 ? 作者:STM32單片機 ? 2022-09-15 09:59 ? 次閱讀

1、前言

最近有客戶詢問,能否使用 STM32CubeIDE 在編譯時通過設置某個編譯選項,讓STM32 應用與存儲位置無關。這樣的優勢是能使同一個固件被燒在 STM32 Flash 里的不同位置, 而在系統 Bootloader 里只需要跳到相應的位置就可以正常執行固件代碼。客戶希望STM32 代碼從 Flash 里執行,不復制到 RAM 里;客戶希望是一個完整的映像,而不僅僅是其中某個函數做到了位置無關。

2、分析

嵌入式場景下,不一定有操作系統。即使有操作系統,一般也是 RTOS。一般 RTOS沒有一個通用的程序加載器。因此,存儲位置無關的需求,在這時可以說無關緊要。但是,如果客戶需要進行在線固件更新,例如 IoT 應用的固件升級,那么位置無關就存在價值了。位置無關之后,對于不同的軟件版本,不需要頻繁的為燒寫位置的反復改變而修改編譯鏈接腳本。也不需要在代碼里顯式的在兩個 Bank 之間進行切換。

最簡單的情況是所有的代碼都復制到內存執行。因為 Flash 的功能只是進行存儲,自然對 Flash 的位置沒有任何要求。但大部分 MCU 用戶面臨的真實案例都是 Flash 比較大,例如 ,1M 字節 ;RAM 比較小,例如,128K 字節。在這種情況下,代碼在 Flash 原地執行就是一個必須的選擇。Flash 位置改變,會影響從 Bootloader 跳轉之后的固件執行時的 PC 指針,也就是 PC指針值會發生相應的變化。位置無關的原理,是讓應用程序經過編譯后所生成的映像,其中的代碼和數據,都是基于相對代碼的位置進行引用。那么,當應用被搬到不同位置時,他們的相對位置不變,從而執行不受影響。

代碼和數據基于絕對地址還是基于相對地址,是由編譯器所決定。以客戶要求的

STM32CubeIDE 編譯工具為例,我們可以看到在[Project]->[Properties]->[C/C++ Build]->[Settings]->[Tool Settings]->[MCU GCC Compiler]->[Miscellaneous]已經有一項[Position Independent Code (-fPIC)]。

是否只要選一下-fPIC 選項就大功告成了呢?答案是沒有那么簡單。

f79506e8-341f-11ed-ba43-dac502259ad0.png

事實上,對于完整應用程序工程,用戶應該經過這些步驟將其變成位置無關:? 選擇正確的編譯器選項

? 去掉或者替換掉那些包含絕對位置的庫文件

? 修改代碼中的 Flash 絕對地址(這里以 STM32H7 的 CRC_Example 例程為例,

其他情況下有可能要修改更多) o 在 startup_xxx.s 匯編代碼里的 sidata

o 在 system_xxx.c 里的 SCB->VTOR 以及中斷向量表內容

o GOT

對于完整工程,要正確的跳轉到應用程序進行執行,還需要由 Bootloader 向應用程序提供或者由應用程序在鏈接時自身解析計算,得到以下信息

? Flash 偏移量

? 中斷向量表的開始以及結束地址

? GOT 的開始以及結束地址

我們接下來就舉例說明這些步驟。

3、步驟

3.1. 選擇正確的編譯器選項

如果我們不使用任何編譯選項,編出來的代碼會怎么樣?我們可以通過.list 文件進行查看。.list 文件在 STM32 例程中默認生成,如果沒有請勾選如下選項, 在 [Project]->[Properties]->[C/C++ Build]->[Settings]->[Tool Settings]->[MCU Post Build outputs]->[Generate list file],可參考下圖。

f7a7d3a4-341f-11ed-ba43-dac502259ad0.png

f7bb0e42-341f-11ed-ba43-dac502259ad0.png

f7d2fee4-341f-11ed-ba43-dac502259ad0.png

我們看到代碼中直接使用了變量的絕對地址,例如 0x2000 0028。我們不要被 literal pool 文字池的使用所迷惑,那個基于 PC 的操作只是為了取變量的絕對地址,例如, 0x2000 0028,并沒有將絕對地址變成相對地址。

當然大家說這里是 RAM 地址,沒有關系。我們選擇這個函數來說明,是因為位置無關的編譯器選項是不區分 RAM 還是 Flash 里的變量,而這個函數最簡單容易理解。如果我們查看另外一個復雜一點的函數,例如,HAL_RCC_ClockConfig,我們可以看到以下對Flash 里變量的直接使用。這就不妙了,因為一旦改變了 Flash 下載的位置,在絕對地址處就取不出變量的真實內容了。

f7e3c436-341f-11ed-ba43-dac502259ad0.png

我們沒有辦法一個一個查找修改所有的變量。當然這里的變量是指全局變量。如果要修改,我們希望編譯器能把他們集中在一起。對于此,編譯器提供了多個編譯選項。例如,PIC 是位置無關代碼, PIE 是位置無關執行。PIC 和 PIE 這兩者類似,但是存在一個顯著的差異是 PIE 會對部分全局變量優化。我們可以觀察到用兩種不同編譯選項的效果。

f7f47cf4-341f-11ed-ba43-dac502259ad0.png

其中 80004C0 地址處包含的是 GOT 自身的偏移量,存在 r2 里,要在兩次取全局變量 uwTickFreq 和 uwTick 時引用。GCC 編譯器引入 GOT 全局偏移量表來解決全局變量的絕對地址的問題。在之前對絕對地址的直接使用,現在被轉化成先取得 GOT 入口相對于 PC 的偏移,再獲得實際變量相對于 GOT 入口的偏移,從而得到實際變量的地址。計算公式如下:

實際變量的絕對地址=PC + GOT 相對于 PC 的偏移 + 變量地址相對于 GOT 的偏移

GOT 只有一個,如果代碼放在不同的位置,代碼自身就可以根據 Bootloader 傳遞過來的信息,或者自行計算來對 GOT 進行更新。這樣變量的地址就和新的 Flash 偏移相匹配。

f807ca2a-341f-11ed-ba43-dac502259ad0.png

這里可以看到 80004c0 對應的 uwTick(可以從 str 指令結合 C 語言源代碼快速知道它對應于 uwTick)不再使用 GOT 偏移,而是相對于 PC 的偏移(與前文相比,多了一條指令 “add r3,pc”)。換句話說,PIE 對局部的全局變量做了優化。這個優化顯然不是我們所需要的。因為如此以來,RAM 變量的地址就會隨著 PC 的不同而不同。而我們則希望所有對RAM 的用法不發生變化。

為了能夠修改 GOT 內容,我們選擇將 GOT 最終存放在 RAM 中,導致代碼中對 GOT的尋址也是使用了相對于 PC 的偏移。而因為 RAM 有限,或者因為沒有虛擬內存的緣故,我們不希望 RAM 的用法有所不同,否則,可能代價很大。這時,一旦 Flash 代碼位置發生變化引起 PC 指針變化,GOT 就無法找到。因此,即使我們不使用 PIE,PIC 也沒有辦法單獨使用。為了確保沒有任何存放在 RAM 里的變量的位置是相對于 PC 的偏移。我們應該使用如下所有編譯選項,single-pic-base 讓系統只使用一個 PIC 基址,就是下文反匯編中看到

r9;no-pic-data-is-text-relative 則讓編譯器不要讓任何變量相對于 PC 尋址。

f81762be-341f-11ed-ba43-dac502259ad0.png

f8276736-341f-11ed-ba43-dac502259ad0.png

這樣實際變量的絕對地址,就變成實際變量的絕對地址=PIC 基址 + GOT 相對于 PIC 基址的偏移 + 變量地址相對于 GOT的偏移使用以上編譯選項,這樣我們看到 HAL_IncTick 就如下所示:

f83fb610-341f-11ed-ba43-dac502259ad0.png

這樣所有在 RAM 里的全局變量都是相對于 GOT 的偏移。注意,這個時候你編譯出來的代碼現在沒有辦法進行測試,盡管你只是改了編譯選項。這是因為 PIC 的基址需要你通過寄存器 r9 顯式指定。在本例中,我們在鏈接腳本里如下定義 GOT 的位置:

f8542d3e-341f-11ed-ba43-dac502259ad0.png

因此,我們可以很容易的從.map 文件中獲得 GOT_START 的 RAM 地址,0x2000 0000,它就是 PIC 的基址。如果想測試編譯器選項是否如我們所期望,我們可以在Reset_Handler 開始部分加上如下語句(參考后文內存布局的代碼):

f86b1986-341f-11ed-ba43-dac502259ad0.png

經過測試,我們可以確信,編譯器選項的改動對我們最終執行結果沒有影響。

值得注意的是,STM32 用戶的代碼,例如 RTOS 的移植, 也可能使用寄存器 r9。在這種情況,用戶應當解決沖突。一般情況寄存器 r9 對應用程序并不是必要的。

3.2. 去掉或者替換掉那些包含絕對位置的庫文件

我們要將位置無關的庫去掉或者替換掉。在 STM32 參考代碼里,我們需要

startup_xxx.s 里 C 庫調用去掉。示例如下:

f883fc62-341f-11ed-ba43-dac502259ad0.png

3.3. 修改 Flash 絕對地址

3.3.1. 內存布局

如果要對代碼中的 Flash 絕對地址進行修改,我們需要知道存放 Flash 絕對地址的 RAM起始和結束地址,以及需要增加或減少的 Flash 偏移量。存放 Flash 絕對地址的 RAM 起始和結束地址,在編譯時可以讓應用代碼本身借助自身鏈接腳本在鏈接時導出的變量得到,然后由應用程序在運行時存放在 RAM 中的固定位置;也可以在編譯后從.map 文件或使用工具解析 elf 文件獲得,然后作為應用程序一部分的元信息,例如,給應用程序加個頭部存放元信息,由 Bootloader 下載并解析,將其放入到 RAM 固定位置。

我們規劃在一段 RAM 里按如下順序存放如下元信息,它可以是應用程序本身在最初階段自我存放在這里,也可以簡單的由 Bootloader 解析元信息后,跳轉到應用程序之前就存放在這里。

f893b15c-341f-11ed-ba43-dac502259ad0.png

我們在前文已經在鏈接腳本中定義了 GOT_START 和 GOT_END,我們還需要在鏈接腳本中定義 VT_START 和 VT_END。如下圖所示:

f8a1f0dc-341f-11ed-ba43-dac502259ad0.png

如果我們希望 Bootloader 僅僅是做簡單的跳轉,我們可以將規劃這段內存的工作,交給應用程序的初始化部分(在 “ldr sp, =_estack”之前)。假定 0x0 處對應為 0x2400 0000,參考代碼如下:

f8b484fe-341f-11ed-ba43-dac502259ad0.png

3.3.2. 匯編代碼

3.3.2.1. _sidata

在默認的 STM32 工程中,還有一些對變量絕對地址的使用。在 startup_xxx.s 有許多地方使用絕對地址,它們不能被編譯器收集到 GOT 中。其中,默認在鏈接腳本里的_sidata,標志 flash 里 RAM 數據區的 Flash 位置,需要修改。

f8d1235c-341f-11ed-ba43-dac502259ad0.png

注意,變量絕對地址本身不是個問題,而對它解應用,取它的內容才會發生錯誤。而這里的 _sidata 是要被初始化代碼使用,目的是將 Flash 的內容搬移到 RAM 里。我們顯然要對_sidata 進行修改,否則無法取得正確的內容到 RAM 里。

根據前文的內存布局,我們可以把 Flash 的偏移量從內存中放置在寄存器 r8 里,例如:

f8e20532-341f-11ed-ba43-dac502259ad0.png

則我們只需要一行簡單的代碼 “add r3,r8” 就可以修正_sidata 的地址。

f8f6f37a-341f-11ed-ba43-dac502259ad0.png

3.3.3. C 代碼

3.3.3.1. 公共函數

如果一段內存的數據都是硬編碼,我們只需要一個公共函數就可以對其循環進行修正。我們需要知道什么樣的地址之外不是 Flash 地址,那么就對這樣的值不做修改。例如,我們定義 0x1fff ffff 之外的就不是 Falsh 地址,相應的宏定義如下:

f901f82e-341f-11ed-ba43-dac502259ad0.png

3.3.3.2. SCB->VTOR

在 C 語言中如果使用賦值語句進行硬編碼,編譯器也無法進行收集。例如在

system_stm32xxxx.c 中的 SystemInit 有如下語句:

f9172eba-341f-11ed-ba43-dac502259ad0.png

中斷向量表相關的內容需要修改,包括兩部分:

? 中斷向量表的內存位置

? 中斷向量表的內容

我們應該將中斷向量表復制到 RAM 里,通過 UpdateOffset 函數修正其中包含的所有Flash 絕對地址的值,同時通過對 SCB->VTOR 賦值來將中斷向量表的位置指向我們修改過內容的 RAM 地址。注意,VTOR 所指向的地址 VT_RAM_START 要按照 ARM 要求,根據中斷總大小向上進行 2 的冪次對齊,例如,37 個字大小要使用 64 個字對齊。另外,中斷向量表的內容,也包含有 RAM 地址,對此,我們并不需要修改。當然,UpdateOffset 函數已

經考慮到這一點,所以我們可以直接使用它。更新中斷向量表以及 VTOR 的參考代碼如下:

f932eb96-341f-11ed-ba43-dac502259ad0.png

3.3.3.3. GOT

編譯器已經將 C 語言中所有全局變量的地址都收集到 GOT 中,因此我們很容易對其Flash 地址的內容進行修正,參考代碼如下:

f94ba028-341f-11ed-ba43-dac502259ad0.png

4、總結

除非你僅僅是運行一小塊代碼,否則開發位置無關的 STM32 完整工程,不僅僅要設置正確的編譯器選項,還要保證它所鏈接的預編譯的庫不含有絕對地址引用,要保證所有源代碼里沒有對絕對地址的硬編碼,包括修改 data 區的 Flash 起始地址,中斷向量表的內容與位置,以及 GOT 的內容。

審核編輯:彭靜
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 存儲
    +關注

    關注

    13

    文章

    4353

    瀏覽量

    86065
  • STM32
    +關注

    關注

    2270

    文章

    10923

    瀏覽量

    357077
  • 函數
    +關注

    關注

    3

    文章

    4345

    瀏覽量

    62874
  • 代碼
    +關注

    關注

    30

    文章

    4823

    瀏覽量

    68897

原文標題:如何開發與存儲位置無關的 STM32 應用

文章出處:【微信號:mcugeek,微信公眾號:MCU開發加油站】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    STM32應用與存儲位置無關

    Bootloader 里只需要跳到相應的位置就可以正常執行固件代碼。客戶希望STM32 代碼從 Flash 里執行,不復制到 RAM 里;客戶希望是一個完整的映像,而不僅僅是其中某個函數做到了
    發表于 09-05 11:43 ?843次閱讀

    ARM的位置無關程序設計在Bootloader中的應用

    ARM的位置無關程序設計在Bootloader中的應用
    發表于 10-26 21:33

    轉:使用STM32CubeMX生成用于STM32-V5開發板的USART工程

    本期教程使用STM32CubeMX生成可用于STM32-V5開發板的USART1工程,通過本期教程讓大家對STM32CubeMX有個整體的認
    發表于 09-09 11:17

    位置無關的代碼

    ) }--------------------------------------------這里把代碼段等鏈接到外存地址,那么確實在引導代碼里要注意“位置無關的代碼”問題,個人查了一些資料,只是提到bl,adr等相對pc的一些指令用法 .rodata ALIGN(4
    發表于 06-17 05:45

    ARM的位置無關程序設計在Bootloader中的應用

    ARM的位置無關程序設計在Bootloader中的應用 ARM處理器支持位置無關的程序設計,這種程序加載到存儲器的任意地址空間都可以正常運行,其設計方法在嵌
    發表于 03-29 15:12 ?1265次閱讀

    ARM處理器的位置無關程序設計

    ARM處理器支持位置無關的程序設計,這種程序加載到存儲器的任意地址空間都可以正常運行,其設計方法在嵌入式應用系統開發中具有重要的作用。尤其在裸機狀態下開發Bootloader程序及
    發表于 09-22 17:03 ?1044次閱讀

    EM-STM3210E開發完整資料

    EMBEST(英蓓特)的STM32開發板EM-STM3210E的完整資料。EMBEST是ST的官方合作伙伴。 內容: 1 開發板用戶手冊(P
    發表于 05-13 16:40 ?21次下載

    ARM處理器位置無關的程序設計方案解析

    ARM處理器支持位置無關的程序設計,這種程序加載到存儲器的任意地址空間都可以正常運行,其設計方法在嵌入式應用系統開發中具有重要的作用。尤其在裸機狀態下開發Bootloader程序及進行
    發表于 10-27 13:00 ?4次下載

    ARM處理器的位置無關程序設計

    ARM處理器支持位置無關的程序設計,這種程序加載到存儲器的任意地址空間都可以正常運行,其設計方法在嵌入式應用系統開發中具有重要的作用。尤其在裸機狀態下開發Bootloader程序及進行
    發表于 12-01 01:16 ?589次閱讀

    Linux下C語言共享庫的位置無關實現原理分析

    如何創建一個*可執行*的共享庫 一文談完了如何讓共享庫可直接執行,本文再來談談共享庫的運行時位置無關(PIC)是如何做到的。
    的頭像 發表于 11-28 16:20 ?3099次閱讀

    實例介紹如何使用STM32開發工具Keil建立工程

    ????本文通過點亮LED的簡單例子,梳理以下如何使用STM32開發工具Keil建立工程。 ????假設你手里有STM32開發板。 ???
    的頭像 發表于 02-01 14:00 ?6580次閱讀
    實例介紹如何使用<b class='flag-5'>STM32</b><b class='flag-5'>開發</b>工具Keil建立<b class='flag-5'>工程</b>

    畢業論文 | 基于STM32的雙輪平衡小車設計(基于Keil5的完整注釋版代碼工程,原器件清單)

    畢業論文 | 基于STM32的雙輪平衡小車設計(基于Keil5的完整注釋版代碼工程,原器件清單)
    發表于 11-21 20:06 ?0次下載
    畢業論文 | 基于<b class='flag-5'>STM32</b>的雙輪平衡小車設計(基于Keil5的<b class='flag-5'>完整</b>注釋版代碼<b class='flag-5'>工程</b>,原器件清單)

    STM32開發,使用IAR軟件建立工程

    STM32開發,使用IAR軟件建立工程1 概述1.1 資源概述1.2 在IAR中建立工程2 IAR建立STM32
    發表于 12-03 10:51 ?14次下載
    <b class='flag-5'>STM32</b><b class='flag-5'>開發</b>,使用IAR軟件建立<b class='flag-5'>工程</b>

    ESP8266 WIFI的stm32f103的完整工程

    ESP8266WIFI 的stm32f103 的完整工程資料分享
    發表于 11-17 15:23 ?29次下載

    如何開發與存儲位置無關STM32應用?

    如何開發與存儲位置無關STM32應用?
    的頭像 發表于 10-18 16:46 ?629次閱讀
    如何<b class='flag-5'>開發</b>與存儲<b class='flag-5'>位置</b><b class='flag-5'>無關</b>的<b class='flag-5'>STM32</b>應用?
    主站蜘蛛池模板: 亚洲AV久久无码精品蜜桃 | 亚洲九九视频 | 2022久久精品国产色蜜蜜麻豆 | 欧美又粗又长又大AAAA片 | 亚洲国产在线视频精品 | 久久亚洲高清观看 | 中文字幕无码一区二区免费 | 在线精品视频成人网 | 射漂亮黑b丝女 | 亚洲高清在线精品一区 | YY6080A旧里番在线观看 | 小莹的性荡生活40章 | 一边啪啪的一边呻吟声口述 | 欧美 亚洲 中文字幕 高清 | 国产人妻XXXX精品HD电影 | 久久婷婷五月综合色丁香花 | 亚洲 色 欧美 爱 视频 日韩 | 色综合久久五月 | 亚洲人人为我我为人人 | 麻豆国产精品AV色拍综合 | 小莹的性荡生活 | 国产欧美日韩网站 | 亚洲精品久久久992KVTV | 国产成人a视频在线观看 | 男人私gay挠脚心vk视频 | 精品国产午夜肉伦伦影院 | jzz大全18 | 兽交白浆喷水高潮 | 奇虎成人网 | 国产99小视频 | 蜜桃传媒星空传媒在线播放 | 日本无码色哟哟婷婷最新网站 | 恋夜影视列表免费安卓手机版 | 9420高清完整版在线电影免费观看 | 98久久无码一区人妻A片蜜 | 亚洲人女同志video | 国产人妻人伦精品久久无码 | 精品国产乱码久久久久久人妻 | 欧美性动漫3d在线观看完整版 | 亚洲熟妇无码乱子AV电影 | 日日噜噜夜夜狠狠扒开双腿 |