armlink 第四章 scatter文件舉例

armlink 第四章 scatter文件舉例

在前面學習了基本術語和概念之後,本章是加強scatter編寫能力的章節。

4.1 什麼時候使用scatter文件

scatter文件通常用於嵌入式系統中,因爲這些系統含有ROM,RAM,還有一些內存映射的外設。下面的場景常使用scatter文件:

  1. 複雜的內存映射:放在不同內存區域的section,需要使用scatter文件來更精細的操控放置的位置

  2. 不同的存儲類型:許多系統包含各種各樣的存儲設備,如flash,ROM,SDRAM,SRAM等。這時可以使用scatter文件,將更適合的存儲區域放置更適合的代碼。例如:中斷代碼放置在SRAM中,已達到快速響應的目的;而不頻繁訪問的配置信息可以放置在flash存儲中。

  3. 內存映射的外設:在內存映射機制下,scatter文件可以在一個精確的地址放置數據section。這樣訪問這個數據section就相當於訪問對應的外設。

  4. 在固定地址存放函數:即使修改並重新編譯了應用程序,而跟應用程序緊密相關的函數還是可以放置在一個固定的位置。這個對於跳轉表的實現非常有用。

  5. 使用符號標記堆和棧:當應用被鏈接時,可以爲堆和棧定義符號

4.2 在scatter文件中指定堆和棧

在c語言中,常常需要兩個存儲區域,堆和棧。在所有的內存都由我們分配的情況下,堆和棧也需要我們進行分配。

在程序開始運行之前,會調用__user_setup_stackheap()函數,它負責初始化堆和棧。而這個函數根據我們在scatter文件中的設置來初始化。

要想正確的初始化堆和棧。我們需要在scatter文件中定義兩個特殊的執行region。分別叫做ARM_LIB_HEAP 和ARM_LIB_STACK。這兩段內存由c庫進行初始化,所以不能放置任何輸入section,此時應該設置EMPTY屬性。同時也可以給這兩個內存區域設置基址和大小。如下:

LOAD_FLASH{ARM_LIB_STACK 0x40000 EMPTY -0x20000 ; 棧區,向下增長
    { }
    ARM_LIB_HEAP 0x28000000 EMPTY 0x80000 ; 堆區向上增長
    { }}

當然還有更簡單的用法,只需要定義一個特殊的執行region,叫做ARM_LIB_STACKHEAP,同樣他需要有EMPTY屬性,並設置基址和大小

4.3 使用scatter文件描述一個簡單的鏡像

如下圖,是一個簡單的鏡像內存視圖。

在這裏插入圖片描述

下面根據這個圖,來寫一個scatter文件

LOAD_ROM 0x0000 0x8000 ; 加載region的名字叫LOAD_ROM
; 基址0x0000
; 最大大小0x8000
{
    EXEC_ROM 0x0000 0x8000 ; 第一執行region的名字叫做EXEC_ROM
    ; 基址0x000
    ; 最大大小0x8000
    {
        * (+RO) ; 放置所有的代碼和RO數據
    }
    SRAM 0x10000 0x6000 ; 第二個執行region叫SRAM
    ; 基址 0x10000
    ; 最大大小 0x6000
    {
        * (+RW, +ZI) ; 放置所有的RW數據和ZI數據
    }
}

4.4 使用scatter文件描述一個稍微複雜的鏡像

如下圖

---插入圖片-----

下面的例子展示了上圖對應的scatter描述

LOAD_ROM_1 0x0000 ; 第一個加載region的基址爲0x0000
{
    EXEC_ROM_1 0x0000 ; 第一個執行region的基址爲0x0000
    {
        program1.o (+RO) ; 放置programe1.o中的所有的代碼和RO數據
    }
    DRAM 0x18000 0x8000 ; 這個執行region的基址爲0x18000,最大大小爲0x8000
    {
        program1.o (+RW, +ZI) ; 放置program1.o中的所有的RW數據和ZI數據
    }
}
LOAD_ROM_2 0x4000 ; 第二個加載region的基址爲0x4000
{
    EXEC_ROM_2 0x4000
    {
        program2.o (+RO) ; 放置programe2.o中的所有的代碼和RO數據
    }
    SRAM 0x8000 0x8000
    {
        program2.o (+RW, +ZI) ; 放置program2.o中所有的RW數據,和ZI數據
    }
}

注意:在上面這個例子中,如果再次新增一個program3.o文件。我們需要將program3.o也放置進去。當然,你也可以使用通配符*,或者.ANY來匹配剩下的所有文件。

4.5 在指定地址放置函數和數據

爲了單獨放置函數和數據,需要將這些函數和數據與源文件中的剩下的部分分開來對待。

鏈接器有兩種方法使我們能夠在指定位置放置section:

  1. 在scatter文件中定義一個指定位置的執行region,然後在這個region中放置需要的section。
  2. 定義"__at" section .這些特殊section能夠根據名字而獲得放置地址。

爲了將函數或者數據放置在一個特殊位置,他們必須放置在某個section中。下面幾種方式可以達到此目的:

  1. 放置函數和數據在他們自己單獨的源文件中
  2. 使用__attribute__((at(address)))將變量放置在指定位置的section中
  3. 使用__attribute__((section(“name”)))將函數或者變量放置在指定名字的section中。
  4. 在彙編代碼中,使用AREA僞指令。因爲AREA僞指令是彙編當中最小的可定位的單元
  5. 使用–split_sectoins 編譯選項,爲每個源文件中的函數生成一個section

下面舉例說明。

4.5.1 不使用scatter,在指定的地址放置變量

  1. 創建main.c源文件包含下面的代碼
#include <stdio.h>
extern int sqr(int n1);
int gSquared __attribute__((at(0x5000))); //放在0x5000
int main()
{
    gSquared=sqr(3);
    printf("Value squared is: %d\n", gSquared);
}
  1. 創建function.c源文件包含下面的代碼
int sqr(int n1)
{
    return n1*n1;
}
  1. 編譯並連接源文件:
armcc -c -g function.c
armcc -c -g main.c
armlink --map function.o main.o -o squared.axf

--map表示顯示內存映射。

在上面例子中,__attribute__((at(0x5000)))指示全局變量gSquared放置於絕對地址0x5000處。

內存映射如下:

…
Load Region LR$$.ARM.__at_0x00005000 (Base: 0x00005000, Size: 0x00000000, Max: 0x00000004,ABSOLUTE)
    Execution Region ER$$.ARM.__at_0x00005000 (Base: 0x00005000, Size: 0x00000004, Max:0x00000004, ABSOLUTE, UNINIT)
    Base Addr  Size       Type Attr Idx E Section Name       Object
    0x00005000 0x00000004 Zero RW   13  .ARM.__at_0x00005000 main.o

4.5.2 使用scatter,在一個命名的section中放置變量

  1. 創建main.c包含如下源文件
#include <stdio.h>
extern int sqr(int n1);
int gSquared __attribute__((section("foo"))); //放置在名字叫foo的section中
int main()
{
    gSquared=sqr(3);
    printf("Value squared is: %d\n", gSquared);
}
  1. 創建functio.c包含下面代碼
int sqr(int n1)
{
    return n1*n1;
}
  1. 創建scatter文件scatter.scat 包含如下配置
LR1 0x0000 0x20000
{
    ER1 0x0 0x2000
    {
        *(+RO) ; 餘下的代碼和只讀數據放置在此處
    }
    ER2 0x8000 0x2000
    {
        main.o
    }
    ER3 0x10000 0x2000
    {
        function.o
        *(foo) ; 將gSquared放置在此處
    }
    ; RWZI數據放置在0x200000RAM 0x200000 (0x1FF00-0x2000)
    {
        *(+RW, +ZI)
    }
    ARM_LIB_STACK 0x800000 EMPTY -0x10000
    {
    }
    ARM_LIB_HEAP +0 EMPTY 0x10000
    {
    }
}

ARM_LIB_STACK 和 ARM_LIB_HEAP region是必須的,因爲程序和c庫進行鏈接

  1. 編譯並鏈接源文件
armcc -c -g function.c
armcc -c -g main.c
armlink --map --scatter=scatter.scat function.o main.o -o squared.axf

上例,__attribute__((section(“foo”))) 指示了gSquared 被放置在名字叫foo的section中。scatter文件也說明了將foo section放置在ER3執行region中。

內存映射如下:

Load Region LR1 (Base: 0x00000000, Size: 0x00001570, Max: 0x00020000, ABSOLUTE)
…
    Execution Region ER3 (Base: 0x00010000, Size: 0x00000010, Max: 0x00002000, ABSOLUTE)
    Base Addr  Size       Type Attr Idx E Section Name Object
    0x00010000 0x0000000c Code RO   3   .text     function.o
    0x0001000c 0x00000004 Data RW   15  foo       main.o
…

4.5.3 在指定位置放置變量

  1. 創建main.c文件,如下;
#include <stdio.h>
extern int sqr(int n1);
// 在0x10000處放置
const int gValue __attribute__((section(".ARM.__at_0x10000"))) = 3;
int main()
{
    int squared;
    squared=sqr(gValue);
    printf("Value squared is: %d\n", squared);
}
  1. 創建functon.c源文件,如下:
int sqr(int n1)
{
    return n1*n1;
}
  1. 創建scatter文件scatter.scat如下:
LR1 0x0
{
    ER1 0x0
    {
        *(+RO) ; 剩下的只讀代碼
    }
    ER2 +0
    {
        function.o
        *(.ARM.__at_0x10000) ; 放置gValue在0x10000
    }
    ; RWZI放置在0x200000
    RAM 0x200000 (0x1FF00-0x2000)
    {
        *(+RW, +ZI)
    }
    ARM_LIB_STACK 0x800000 EMPTY -0x10000
    {
    }
    ARM_LIB_HEAP +0 EMPTY 0x10000
    {
    }
}
  1. 編譯並鏈接源文件
armcc -c -g function.c
armcc -c -g main.c
armlink --no_autoat --scatter=scatter.scat --map function.o main.o -o squared.axf

從內存映射圖中,可以看到變量在ER2中的0x10000處

…
Execution Region ER2 (Base: 0x00001578, Size: 0x0000ea8c, Max: 0xffffffff, ABSOLUTE)
Base Addr  Size       Type Attr Idx   E Section Name     Object
0x00001578 0x0000000c Code RO   3     .text              function.o
0x00001584 0x0000ea7c PAD
0x00010000 0x00000004 Data RO   15    .ARM.__at_0x10000  main.o
…

在這個例子中,ER1的大小未知。因此,gValue可能被放置ER1也可能放置在ER2中。

爲了保證放在ER2中,你必須在ER2中包含對應的section匹配文字。並且在鏈接的時候,還必須指定--no_autoat命令行選項。

如果忽略了--no_autoat選項,gValue將被單獨放置,對應於

LR$$.ARM.__at_0x10000加載region。

該region包含的執行region爲

ER$$.ARM.__at_0x10000

注意:at形式的縮寫。

//放置 variable1 在 .ARM.__AT_0x00008000處
int variable1 __attribute__((at(0x8000))) = 10;
//放置 variable2 在.ARM.__at_0x8000處
int variable2 __attribute__((section(".ARM.__at_0x8000"))) = 10;

上面的section名字,忽略大小寫

__at具有如下的限制:

  1. __at section的地址範圍內不能覆蓋。
  2. __at section不準放在位置無關的執行region中
  3. __at section不能引用鏈接器定義的這些符號:
$$Base,$$Limit,$$Length.
  1. __at section 不準用在SysV,BPABI,以及BPABI的動態鏈接庫上。
  2. __at section 的地址必須是對齊的整數倍
  3. __at section 忽略+FIRST後者+LAST

4.6 __at section的自動放置

鏈接器自動放置__at section。當然也可以手動放置,在下一小節中介紹

鏈接器通過--autoat指示鏈接器自動放置__at setcion。這個選項默認是打開的。

當使用--autoat鏈接時,__at section 不會被放置在scatter文件中的與section模式字符串匹配的region中。而是將這個section放在一個兼容的region中。如果沒有兼容的region,則創建兼容的region。

帶有--autoat選項的所有鏈接器,創建的region都有UNINIT屬性。如果需要將這個__at section放置在一個ZI region中,則必須放置在兼容region中。

兼容region滿足如下條件:

  1. __at 的地址剛好處在執行region的地址範圍內。如果一個region沒有設置最大大小,鏈接器將排除__at section之後,計算大小,這個大小再加上一個常量作爲其最後的大小。這個常量默認值爲10240字節。他可以通過--max_er_extension命令行選項來調整。

  2. 這個執行region還需要滿足如下的條件:

  • 具有模式字符串,並能夠匹配這個section
  • 至少有一個section和__at section 具有相同的類型(RO,RW,ZI)
  • 沒有EMPTY屬性

來個例子:

//放置RW變量在叫做.ARM.__at_0x02000的section中
int foo __attribute__((section(".ARM.__at_0x2000"))) = 100;
//放置ZI變量在.ARM.__at_0x4000的section中
int bar __attribute__((section(".ARM.__at_0x4000"),zero_init));
//放置ZI變量在.ARM.__at_0x8000的section中
int variable __attribute__((section(".ARM.__at_0x8000"),zero_init));

對應的scatter文件如下:

LR1 0x0
{
    ER_RO 0x0 0x2000
    {
        *(+RO) ; .ARM.__at_0x0000 lies within the bounds of ER_RO
    }
    ER_RW 0x2000 0x2000
    {
        *(+RW) ; .ARM.__at_0x2000 lies within the bounds of ER_RW
    }
    ER_ZI 0x4000 0x2000
    {
        *(+ZI) ; .ARM.__at_0x4000 lies within the bounds of ER_ZI
    }
}
; 鏈接器爲.ARM.__at_0x8000創建一個加載和執行region。因爲它超出了所有候選region的大小。

4.7 手動放置__at section

使用--no_autoat命令行選項,然後使用標準的模式匹配字符串去控制__at section的放置。

舉例如下:

//放置RO變量在.ARM.__at_0x2000
const int FOO __attribute__((section(".ARM.__at_0x2000"))) = 100;
//放置RW變量在.ARM.__at_0x4000
int bar __attribute__((section(".ARM.__at_0x4000")));

對應的scatter文件如下:

LR1 0x0
{
    ER_RO 0x0 0x2000
    {
        *(+RO) ; .ARM.__at_0x0000 is selected by +RO
    }
    ER_RO2 0x2000
    {
        *(.ARM.__at_0x02000) ; .ARM.__at_0x2000 is selected by the section named
    ; .ARM.__at_0x2000
    }
    ER2 0x4000
    {
        *(+RW +ZI) ; .ARM.__at_0x4000 is selected by +RW
    }
}

4.8 使用__at 映射一個外設寄存器

爲了將一個未初始化的變量映射爲一個外設寄存器。可以使用ZI __at section。

假設這個寄存器的地址爲0x10000000,定義一個section叫做.ARM.__at_0x10000000.如下:

int foo __attribute__((section(".ARM.__at_0x10000000"),zero_init))

手動放置的scatter文件如下:

ER_PERIPHERAL 0x10000000 UNINIT
{
    *(.ARM.__at_0x10000000)
}

4.9 使用.ANY 來放置未分配的 section

在大多數情況下,單個.ANY 等價於使用*。但是,.ANY 可以出現在多個執行region中。

4.9.1 多個.ANY 的放置規則

當使用多個.ANY時,鏈接器有自己默認的規則來組織section。

當多個.ANY 存在於scatter文件中時,鏈接器以section的大小,從大到小排序。

如果多個執行region都有相同特性(後文稱爲等價)的.ANY,那麼這個section會被分配到具有最多可用空間的region中。

例如:

  1. 如果有兩個等價的執行region,一個大小爲0x2000,另一個沒有限制。那麼.ANY匹配的section會放置在第二個中。
  2. 如果有兩個等價的執行region,一個大小爲0x2000,另一個爲0x3000. 那麼.ANY匹配的section會先放置在第二個中,直到第二個的大小小於第一個。
    相當於這個兩個執行region在交替放置。

4.9.2 命令行選項控制多個.ANY的放置

可以通過命令行選項,控制.ANY的排序,下面的命令行選項是可用的:

  1. --any_placement=algorithm algorithm是如下之一:first_fit,worst_fit,best_fit,或者next_fit

  2. --any_sort_order=order.此處order是如下之一:cmdline或者descending_size

當你想要按照順序填充region時,使用first_fit

當你想要填滿整個region時,使用best_fit

當你想要均勻填充region時,使用worst_fit

當你想要更精確的填充時,使用next_fit

因爲,鏈接器會產生veneer代碼以及填充數據,而這些代碼的是在.ANY匹配之後產生的。所以,如果.ANY將region填滿,則很有可能導致整個regoin無法放置,鏈接器產生的代碼。鏈接器會產生如下的錯誤。

Error: L6220E: Execution region regionname size (size bytes) exceeds limit (limit bytes)

--any_contingency選項防止鏈接器將region的大小填滿。它保留region的一部分空間。當鏈接器產生的代碼沒有空間時,就使用這部分保留的空間。

first_fit和best_fit 默認打開這個選項。

4.9.3 優先級

.ANY 還可以指定優先級。

優先級通過後面接一個數字來表示,從0開始遞增。數字越大優先級越高。

例子如下:

lr1 0x8000 1024
{
    er1 +0 512
    {
        .ANY1(+RO) ; 優先級較低,和er3交替均勻填充
    }
    er2 +0 256
    {
        .ANY2(+RO) ; 優先級最高,先填充這個
    }
    er3 +0 256
    {
        .ANY1(+RO) ;優先級較低,和er1交替均勻填充
    }
}

4.9.4 指定.ANY的最大大小

使用ANY_SIZE max_size 指定最大大小。

例子如下:

LOAD_REGION 0x0 0x3000
{
    ER_1 0x0 ANY_SIZE 0xF00 0x1000
    {
        .ANY
    }
    ER_2 0x0 ANY_SIZE 0xFB0 0x1000
    {
        .ANY
    }
    ER_3 0x0 ANY_SIZE 0x1000 0x1000
    {
        .ANY
    }
}

上面例子中:

  1. ER_1 有0x100的保留空間,該保留空間用於鏈接器產生的內容
  2. ER_2 有0x50的保留空間
  3. ER_3 沒有保留空間。region將會被填滿。應該將ANY_SIZE的大小,限制在region大小的98%以內。以預留2%用於鏈接器產生的內容。

4.9.5 例子1

有6個同樣大小的section。如下:

名字 大小
sec1 0x4
sec2 0x4
sec3 0x4
sec4 0x4
sec5 0x4
sec6 0x4

對應的scatter文件如下:

LR 0x100
{
    ER_1 0x100 0x10
    {
        .ANY
    }
    ER_2 0x200 0x10
    {
        .ANY
    }
}
  1. 對於first_fit: 首先分配所有section到ER_1中,然後再是ER_2中
  2. 對於next_fit:跟first_fit一樣,但是ER_1會被填滿,然後被標記爲FULL。
  3. 對於best_fit: 首先sec1分配到ER_1中,然後ER_2和ER_1優先級相同,且ER_2空間比ER_1空間大,接着分配sec2到ER_1中。直到ER_1填滿
  4. 對於worst_fit:首先分配sec1到ER_1中,然後ER_2空間比ER_1大,接着分配sec2到ER_2中。剩下的兩個region空間一樣大,且優先級相同,然後選擇scatter的第一個,將sec3分配到ER_1中,依次類推。

4.9.6 例子2——使用next_fit

有下面的section:

名字 大小
sec1 0x14
sec2 0x14
sec3 0x10
sec4 0x4
sec5 0x4
sec6 0x4

對應的scatter如下:

LR 0x100
{
    ER_1 0x100 0x20
    {
        .ANY1(+RO-CODE)
    }
    ER_2 0x200 0x20
    {
        .ANY2(+RO)
    }
    ER_3 0x300 0x20
    {
        .ANY3(+RO)
    }
}

詳細步驟如下:

  1. 首先sec1 被分配給ER_1.因爲ER_1有更佳的匹配。ER_1現在還剩下0x6個字節
  2. 鏈接器嘗試將sec2分配給ER_1,因爲它有更佳的匹配。但是ER_1沒有足夠的空間。因此ER_1被標記爲FULL,並且在後續的過程中再也不會考慮給ER_1分配section。鏈接器選擇ER_3,因爲它有更高的優先級
  3. 鏈接器嘗試將sec3分配給ER_3,但是無法放入,因此被標記爲FULL,接着鏈接器將sec3放在ER_2中。
  4. 鏈接器現在處理sec4.它大小爲0x4,適合ER_1和ER_3.但是這兩個在前面步驟中被標記爲FULL。因此剩下的section被放置在ER_2中。
  5. 如果還有一個section叫做sec7,且大小爲0x8.他將鏈接失敗。

4.9.7 例子三

有兩個文件sections_a.o和sections_b.o,如下:

名字 大小
seca_1 0x4
seca_2 0x4
seca_3 0x10
seca_4 0x14
名字 大小
secb_1 0x4
secb_2 0x4
secb_3 0x10
secb_4 0x14

使用如下命令:

--any_sort_order=descending_size sections_a.o sections_b.o --scatter scatter.txt

排序之後,如下:

名字 大小
seca_4 0x14
secb_4 0x14
seca_3 0x10
secb_3 0x10
seca_1 0x4
seca_2 0x4
secb_1 0x4
secb_2 0x4

如果使用如下命令:

--any_sort_order=cmdline sections_a.o sections_b.o --scatter scatter.txt

排序之後如下:

名字 大小
seca_1 0x4
secb_1 0x4
seca_2 0x4
secb_2 0x4
seca_3 0x10
secb_3 0x10
seca_4 0x14
secb_4 0x14

4.10 控制venner的放置

在scatter文件中,還可以放置venner代碼。使用Venner$$Code來匹配venner代碼。

4.11 帶有OVERLAY屬性的放置

可以在同一個地址中,放置多個執行region。因此在某一個時刻,只有一個執行region被激活。

如下面的例子:

EMB_APP 0x8000
{
    ...
    STATIC_RAM 0x0
    {
        *(+RW,+ZI)
    }
    OVERLAY_A_RAM 0x1000 OVERLAY
    {
        module1.o (+RW,+ZI)
    }
    OVERLAY_B_RAM 0x1000 OVERLAY
    {
        module2.o (+RW,+ZI)
    }
    ...
}

被OVERLAY標記的region,在啓動的時候,不會被c庫初始化。而這部分內存的內容由overlay 管理器負責。如果這部分region包含有初始化數據。需要使用NOCOMPRESS屬性來阻止RW 數據的壓縮。

OVERLAY 屬性還可以用在單個執行region中,因此,這個region可以被用作:防止c庫初始化某個region

OVERLAY region也可以使用相對基址。如果他們有相同的偏移,則連續放置在一起。

如下例子:

EMB_APP 0x8000{
    CODE 0x8000
    {
        *(+RO)
    }
    # REGION1 的基址爲 CODE的結尾
    REGION1 +0 OVERLAY
    {
        module1.o(*)
    }
    # REGION2 的基址爲REGION1的基址
    REGION2 +0 OVERLAY
    {
        module2.o(*)
    }
    # REGION3 的基址和REGION2的基址相同
    REGION3 +0 OVERLAY
    {
        module3.o(*)
    }
    # REGION4 的基址爲 REGION3的結尾+4
    Region4 +4 OVERLAY
    {
        module4.o(*)
    }
}

4.12 預留一個空region

可以在scatter文件中,預留一個空的內存區域,比如:將此區域用於棧。使用EMPTY屬性可以達到此效果。

爲了預留一個空的內存用於棧。對應的加載region沒有,執行region在執行時被分配。它被當做dummy ZI region對待,鏈接器使用下面的符號訪問它:

1. Image$$region_name$$ZI$$Base
2. Image$$region_name$$ZI$$Limit
3. Image$$region_name$$ZI$$Length

注意:dummy ZI region 在運行時並不會被初始化爲0

如果長度爲負數,給定的地址就是結束地址。

例子如下:

LR_1 0x80000 ;加載region從0x80000開始
{
    STACK 0x800000 EMPTY -0x10000 ;region 結束地址爲0x800000,開始地址使用長度進行計算
    {
        ;空region用於放置棧
    }
    HEAP +0 EMPTY 0x10000 ; region從上一個region結束處開始。
    {

    }
    ...
}

下圖展示了這個例子:

----插入圖片----

4.13 c和c++ 庫代碼的放置

可以在scatter文件中,放置c和c++ 庫代碼。

在scatter文件中使用,*armlib* 或者 *cpplib* 來索引庫名字。一些ARM c c++庫的section必須放在root region中。例如:__main.o,__scatter*.o,__dc*.o,*Region$$Table.

鏈接器可以在InRoot$$Sections中自動的,可靠的,放置這些section。

例子1如下:

ROM_LOAD 0x0000 0x4000
{
    ROM_EXEC 0x0000 0x4000 ;0x0處的root region
    {
        vectors.o (Vect, +FIRST) ; 向量表
        * (InRoot$$Sections) ; 所有的庫section 必須放置在root region中。如__main.o,__scatter*.o,__dc*.o,*Region$$Table
    }
    RAM 0x10000 0x8000
    {
        * (+RO, +RW, +ZI) ; 所有的其他的section
    }
}

例子2:arm c庫的例子

ROM1 0
{
    * (InRoot$$Sections)
    * (+RO)
}
ROM2 0x1000
{
    *armlib/c_* (+RO) ; 所有arm支持的c庫函數
}
ROM3 0x2000
{
    *armlib/h_* (+RO) ; just the ARM-supplied __ARM_*
    ; redistributable library functions
}
RAM1 0x3000
{
    *armlib* (+RO) ; 其他的arm支持的庫,如,浮點庫
}
RAM2 0x4000
{
    * (+RW, +ZI)
}

名稱ARM lib表示位於install_directory\lib\armlib目錄中的ARM C庫文件

例子3:arm c++ 庫代碼的放置

#include <iostrem>
using namespace std;
extern "C" int foo(){
    cout << "Hello" << endl;
    return 1;
}

爲了放置c++ 庫代碼,定義如下的scatter文件

LR 0x0
{
    ER1 0x0
    {
        *armlib*(+RO)
    }
    ER2 +0
    {
        *cpplib*(+RO)
        *(.init_array) ; .init_array 必須顯示放置,因爲它被兩個region共享,鏈接器無法決定怎麼放置
    }
    ER3 +0
    {
        *(+RO)
    }
    ER4 +0
    {
        *(+RW,+ZI)
    }
}

名稱install_directory\lib\armlib表示位於armlib目錄中的ARM C庫文件

名稱install_directory\lib\cpplib表示位於cpplib目錄中的ARM c++庫文件

4.14 scatter文件的預處理

在scatter文件的第一行,設置一個預處理命令。然後鏈接器會調用相應的預處理器先處理這個文件。預處理命令的格式如下:

#! preprocessor [pre_processor_flags]

最常見的預處理命令如下:

#! armcc -E 

舉例如下:

#! armcc -E 
#define ADDRESS 0x20000000
#include "include_file_1.h"
lr1 ADDRESS 
{

}

也可以在命令行中,進行預處理,如下:

armlink --predefine="-DADDRESS=0x20000000" --scatter=file.scat

armlink 系列完。

下一篇 從arm彙編到使用匯編點亮一個LED。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章