久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 2649|回復: 0
打印 上一主題 下一主題
收起左側

淺談RISC-V GCC之:鏈接腳本學習筆記(一)

[復制鏈接]
跳轉到指定樓層
樓主
ID:809235 發表于 2021-11-5 15:28 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
我們在用RISC-V GCC做嵌入式開發的時候,免不了要和啟動文件和鏈接文件等打交道,本篇文章記錄了一些鏈接腳本相關的學習筆記。
1.基礎概念
鏈接腳本的主要作用是描述輸入文件中的段應當如何映射到輸出文件中,并控制輸出文件的內存布局。多數鏈接腳本都執行類似功能。但是,如果需要,鏈接腳本也可以使用下面所描述的命令指揮鏈接器進行很多其他操作。
鏈接器通常使用一個鏈接腳本。如果沒有為其提供一個,鏈接器將會使用默認的編譯在鏈接器執行文件內部的腳本。可以使用命令’–verbose’顯示默認的鏈接腳本。
為了描述鏈接腳本語言,我們需要定義一些基本概念和詞匯。
鏈接器將許多輸入文件組合成一個輸出文件。輸出文件和每個輸入文件都有一個特定的已知格式成為目標文件格式。每個文件都被稱為目標文件。輸出文件通常叫做可執行文件,但我們仍將其稱為目標文件。每個目標文件在其他東西之間,都有一個段列表。有時把輸入文件的段稱作輸入段,類似的,輸出文件的段稱作輸出段。
每個目標文件中的段都有名字和大小。多數段還有一個相關的數據塊,稱為 段內容。一個段可能被標記為可加載,表示當輸出文件運行時,段內容需要先加載到內存中。一個沒有內容的段可能是可分配段,即在內存中留出一段空間(有時還需要清零)。一個即不是加載又不是可分配的段,通常含有一些調試信息。
每個加載或可分配輸出段有兩個地址。第一個地址為VMA,或者叫做虛地址。這是當輸出文件運行時段所擁有的地址。第二個地址是LMA,或者叫加載內存地址。這是段將會被加載的地址。一個它們會產生區別的例子是,當一個數據段加載到ROM, 此后在程序啟動時被復制到RAM中(這個技術通常被用來初始化全局變量)。此種情況下,ROM使用LMA地址,RAM使用VMA地址。
如果想查看目標文件中的段,可以用objdump程序的’-h’選項。
每個目標文件還有一個符號列表,稱為符號列表。一個符號可能是被定義的或者未定義的。每個符號都有一個名字,且所有已定義的符號在其他信息中間都有一個地址。如果將一個c或者c++程序編譯成目標文件,會將所有定義過的函數和全局變量以及靜態變量作為已定義符號。所有輸入文件引用的未定義的函數或者全局變量會成為未定義符號。
2.常用關鍵詞與用法
ENTRY(symbol) 用來指定程序執行的入口點
MEMORY            內存分配命令
SECTIONS            段命令 描述輸出文件的內存和布局
.text                 程序代碼段
.rodata              只讀數據
.data                可讀寫且需要初始化的數據
.bss                        可讀寫的清零初始化數據
ASSERT                     斷言
PROVIDE(symbol=expression)   定義一個符號
AT                         后跟MEMORY定義的內存區域或者地址
ALIGN                      字節對齊
3 . MEMORY
鏈接器默認的設置允許分配所有可用的內存。你通過MEMORY命令可以重載這些。
MEMORY命令描述了一個內存塊在目標中的位置和大小。你可以使用它描述一個可能會在鏈接器中使用的內存區域,以及那些必須避免使用的內存區域。此后你可以把段放到特定的內存區域里。鏈接器將會基于內存區域設置段地址,如果區域趨于飽和將會產生警告信息。鏈接器不會為了把段更好的放入內存區域而打亂段的順序。
一個鏈接腳本可能含有許多MEMORY命令,但是,所有定義的內存塊都被當作他們是在一個MEMORY命令中定義的一樣。MEMORY的語法是:
MEMORY
   {
     name [(attr)] : ORIGIN = origin, LENGTH =len
     ...
   }
name是鏈接腳本用來引用內存區域的名字。區域名在鏈接腳本外部沒有任何意義。區域名被存儲在一個獨立的名字空間,且不會與符號名,文件名,或者段名起沖突。每個內存區域必須在MEMORY命令中有一個不同的名字。但是你此后可以使用REGION_ALIAS命令為已存在的內存區域添加別名。
attr字符是一個可選的屬性列表,用來決定是否讓一個腳本中沒有顯式指定映射的輸入段使用一個特定的內存區域。就像SECTIONS中進行過的說明,如果你不為一個輸入段指定一個輸出段,鏈接器將會創建一個與輸入段名字相同的輸出段。如果你定義了區域屬性,鏈接器會使用他們來決定創建的輸出段存放的內存區域。
attr字符串只能使用下面的字符組成:
‘R’只讀段
‘W’讀寫段
‘X’可執行段
‘A’可分配段
‘I’已初始化段
‘L’類似于’I’
‘!’反轉其后面的所有屬性
如果一個未映射段匹配了上面除’!’之外的一個屬性,它就會被放入該內存區域。’!’屬性對該測試取反,所以只有當它不匹配上面列出的行何屬性時,一個未映射段才會被放入到內存區域。
origin是一個數字表達式,代表了內存區域的起始地址。表達式必須等價于一個常數并且不能含有任何符號。關鍵字ORIGIN縮短為org或者o(但不能寫成ORG)。
len是一個表達式用來給出內存區域中的字節數大小。類似于origin表達式,表達式必須只能為數字的切必須求值為常數。關鍵字LENGTH可以被縮寫為len或者l。
下面的例子里,我們制定了有兩個可分配的內存區域:一個從’0’開始有256k字節,另一個從’0x40000000’開始,由4兆字節。鏈接器把所有沒有顯式映射到一個內存區域的段放到’rom’內存區域內,段可以是只讀的或者可執行的。鏈接器將把其它沒顯式指定內存區域映射的段放到’ram’內存區域。
MEMORY
   {
     rom (rx) : ORIGIN = 0, LENGTH = 256K
     ram (!rx) : org = 0x40000000, l = 4M
   }
一旦你定義了一個內存區域,你可以使用’>region’輸出段屬性指引鏈接器把特殊輸出段放到該內存區域。例如,如果你擁有一個內存區域名為’mem’,你可以在輸出段定義中使用’>mem’。參考Output Section Region。如果沒有給輸出段指出地址,鏈接器將會把地址放到最先符合要求的內存區域中的可用地址。如果指引給一個內存區域的組合輸出段比區域還大,鏈接器將會提交錯誤。
可以通過ORIGIN(memory)和LENGTH(memory)函數獲得內存區域的起始地址以及長度:
   _fstack = ORIGIN(ram) + LENGTH(ram) - 4;
4. 段描述
4.1輸出段
完整的輸出段描述如下
section [address] [(type)] :
   [AT(lma)]
   [ALIGN(section_align) | ALIGN_WITH_INPUT]
   [SUBALIGN(subsection_align)]
   [constraint]
   {
     output-section-command
     output-section-command
     ...
   } [>region] [AT>lma_region] [:phdr:phdr ...] [=fillexp] [,]
地址(address)是一個輸出段VMA(虛地址)的表達式。此地址為可選參數,但如果給出了地址,則輸出地址就會被精確的設置到給定值。
如果輸出的地址沒有給定,則依照下面的嘗試選擇一個地址。此地址將會被調整到符合輸出端要求的對齊地址。輸出段的對齊要求是所有輸入節中含有的對齊要求中最嚴格的一個。
輸出段地址探索如下:
如果為段設置了內存區域,則段被放如該區域,并且段地址為區域中的下一個空閑位置。
如果使用MEMORY命令創建了一個內存區域列表,此時第一個屬性匹配段的區域被選擇來加載段,段地址為區域中的下一個空閑位置。參見MEMORY。
如果沒有指定的內存區域,或者沒有匹配段的,則輸出地址將會基于當前位置計數器的值
  4.2輸入段
輸入段存在于輸出段的內容中,用來指定不同輸入段在輸出段中的位置,常見的有.text .data  .rodat  .bss COMMOM等,一個輸入段描述由跟隨在段名稱后面括號包含的一個可選的文件名稱列表構成。也可以使用通配符,例如
*main.o(.text)或者直接*(.text)
前一個代表main.o 文件中所有.text段,后一個代表所有參與鏈接文件中的.text段,當然也可以排除一些文件
EXCLUDE_FILE (*文件名.o) *(.text)
5. 一些內建函數
ABSOLUTE(exp)
返回表達式exp的絕對(非可重分配的,而不是非負)值。主要用來在段定義內為符號分配一個絕對值,通常段定義內的符號值都是相對段地址的。
ADDR(section)
返回名為’section’的段的地址(VMA)。你的腳本必須事先未該段定義了位置。在下面的例子里,start_of_output_1, symbol_1, symbol_2分配了同樣的值,除了symbol_1為與段.output1相關的值而其他兩個為絕對值:
SECTIONS { ...
        .output1 :
          {
         start_of_output_1 = ABSOLUTE(.);
          ...
          }
        .output :
          {
          symbol_1 =ADDR(.output1);
          symbol_2 =start_of_output_1;
          }
      ... }
LENGTH(memory)
返回名為memory的內存的長度。
MAX(exp1, exp2)
返回exp1和exp2最大的
MIN(exp1, exp2)
返回exp1和exp2最小的。
ORIGIN(memory)
返回名為memory的內存區域的起始地址。
SIZEOF(section)
返回名為section段的字節數。如果段還沒被分配就是用函數求值,將會產生錯誤。
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 国产精品自拍一区 | 国产成人一区在线 | 国产在线精品一区二区三区 | 羞羞视频网站在线观看 | 开操网 | 精品久久久久久久久久 | 成人久久久 | 成人在线视频网址 | 高清欧美性猛交 | 中文字幕日韩欧美一区二区三区 | 欧美最猛黑人xxxⅹ 粉嫩一区二区三区四区公司1 | 欧美成人精品一区二区男人看 | 日一区二区| 日韩一区二区三区在线视频 | 性高朝久久久久久久3小时 av一区二区三区四区 | 精品国产乱码 | 在线观看日韩精品视频 | 天天亚洲| 欧美福利网站 | 国产日韩电影 | 欧美一区二区三区大片 | 国产目拍亚洲精品99久久精品 | 男女国产网站 | 亚洲视频精品 | 成人影院在线视频 | 日韩一区二区久久 | 亚洲精彩视频 | 亚洲精品在线免费看 | 国产精品色综合 | 在线成人免费视频 | 一区在线播放 | 九九久久这里只有精品 | 国产成人精品久久二区二区91 | 日韩成人在线网站 | 日韩在线看片 | 男女羞羞免费视频 | 2018天天干天天操 | 91高清免费观看 | 日韩在线视频一区 | 午夜精品视频在线观看 | 男人天堂网址 |