打印
[开发工具]

详解KEIL的分散加载文件

[复制链接]
376|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
vivilyly|  楼主 | 2023-11-13 22:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
使用分散文件指定栈和堆
ARM C 库提供了该函数的多种实现__user_setup_stackheap(),并且可以从分散文件中提供的信息中自动为您选择正确的一种。
要选择两个区域内存模型,请在名为ARM_LIB_HEAP和的分散文件中定义两个特殊的执行区域ARM_LIB_STACK。两个区域都有该EMPTY属性。这会导致库选择__user_setup_stackheap()使用符号值的非默认实现:
Image$$ARM_LIB_STACK$$BaseImage$$ARM_LIB_STACK$$ZI$$LimitImage$$ARM_LIB_HEAP$$BaseImage$$ARM_LIB_HEAP$$ZI$$Limit1234567
只能指定一个ARM_LIB_STACKARM_LIB_HEAP区域,并且必须分配一个大小,例如:
ARM_LIB_HEAP 0x20100000 EMPTY 0x100000-0x8000  ; Heap starts at 1MB                                               ; and grows upwardsARM_LIB_STACK 0x20200000 EMPTY -0x8000         ; Stack space starts at the end                                               ; of the 2MB of RAM                                               ; And grows downwards for 32KB12345
可以通过定义名为单一执行区域使用组合的栈和堆区域ARM_LIB_STACKHEAP,与EMPTY属性。这会导致__user_setup_stackheap()使用符号Image$$ARM_LIB_STACKHEAP$$BaseImage$$ARM_LIB_STACKHEAP$$ZI$$Limit的值。
注意如果您重新实现__user_setup_stackheap(),这将覆盖所有库里面的实现。
创建root执行区
要将区域指定为分散文件中的根区域,您可以:
  • 指定
    ABSOLUTE

    为执行区的属性(显式或允许它默认),并为第一个执行区和封闭加载区使用相同的地址。要使执行区地址与加载区地址相同,请执行以下任一操作:
    • 为执行区的基地址和加载区的基地址指定相同的数值。
    • 指定[size=0.9em]+0加载区中第一个执行区的偏移量。如果[size=0.9em]+0为加载区中的所有后续执行区指定零偏移[size=0.9em](+0),则所有不跟随包含 ZI 的执行区的执行区也是根区。
以下示例显示了隐式定义的根区域:
LR_1 0x040000          ; load region starts at 0x40000   {                      ; start of execution region descriptions           ER_RO 0x040000     ; load address = execution address    {        * (+RO)        ; all RO sections (must include section with                        ; initial entry point)    }    ...                ; rest of scatter-loading description}123456789
  • 使用[size=0.9em]FIXED执行区属性可以确保特定区域的加载地址和执行地址相同。您可以使用该[size=0.9em]FIXED属性将任何执行区放置在 ROM 中的特定地址。例如,以下内存映射显示了固定执行区:图 8. 固定执行区的内存映射

The following example shows the corresponding scatter-loading description: 下面的例子给出了相应分散加载描述:
LR_1 0x040000              ; load region starts at 0x40000   {                          ; start of execution region descriptions               ER_RO 0x040000         ; load address = execution address    {        * (+RO)            ; RO sections other than those in init.o    }    ER_INIT 0x080000 FIXED ; load address and execution address of this                           ; execution region are fixed at 0x80000    {        init.o(+RO)        ; all RO sections from init.o    }    ...                    ; rest of scatter-loading description}12345678910111213
Examples of misusing the FIXED attribute误用 FIXED 属性 例子
The following example shows common cases where the FIXED execution region attribute is misused:
LR1 0x8000{    ER_LOW +0 0x1000    {        *(+RO)    }; At this point the next available Load and Execution address is 0x8000 + size of; contents of ER_LOW. The maximum size is limited to 0x1000 so the next available Load; and Execution address is at most 0x9000    ER_HIGH 0xF0000000 FIXED    {        *(+RW+ZI)    }; The required execution address and load address is 0xF0000000. The linker inserts; 0xF0000000 - (0x8000 + size of(ER_LOW)) bytes of padding so that load address matches; execution address}; The other common misuse of FIXED is to give a lower execution address than the next; available load address.LR_HIGH 0x100000000{    ER_LOW 0x1000 FIXED    {        *(+RO)    }; The next available load address in LR_HIGH is 0x10000000. The required Execution; address is 0x1000. Because the next available load address in LR_HIGH must increase; monotonically the linker cannot give ER_LOW a Load Address lower than 0x10000000}12345678910111213141516171819202122232425262728293031
使用 FIXED 属性创建根区域
您可以FIXED在执行区分散文件中使用该属性来创建在固定地址加载和执行的根区。FIXED用于在单个加载区域内创建多个根区域,因此通常是单个 ROM 设备。例如,您可以使用它来将函数或数据块(例如常量表或校验和)放置在 ROM 中的固定地址,以便可以通过指针轻松访问。
例如,如果您指定将一些初始化代码放置在 ROM 的开头并在 ROM 的末尾放置一个校验和,则某些内存内容可能未被使用。使用*或.ANY模块选择器来填充初始化块末尾和数据块开头之间的区域。
为了使您的代码更易于维护和调试,建议您在分散文件中使用最少的布局规范,并将函数和数据的详细布局留给链接器。
您不能指定已部分链接的组件对象。例如,如果您将对象obj1.oobj2.o和部分链接obj3.o在一起以产生obj_all.o,则在生成的对象中会丢弃组件对象名称。因此,您不能按名称引用其中一个对象,例如,obj1.o。您只能引用组合对象obj_all.o
注意在某些情况下,使用FIXED和 单个加载区域是不合适的。指定固定位置的其他方式是:
  • 如果您的加载程序可以处理多个加载区域,请将 RO 代码或数据放在其自己的加载区域中。
  • 如果您不要求函数或数据位于 ROM 中的固定位置,请使用[size=0.9em]ABSOLUTE代替[size=0.9em]FIXED。然后加载器将数据从加载区复制到 RAM 中的指定地址。ABSOLUTE是默认属性。
  • 要将数据结构放置在内存映射 I/O 的位置??,请使用两个加载区域并指定[size=0.9em]UNINIT. UNINIT确保内存位置不会被初始化为零。
在特定地址放置函数和数据
通常,编译器从单个源文件生成 RO、RW 和 ZI 节。这些区域包含源文件中的所有代码和数据。要将单个函数或数据项放置在固定地址,您必须使链接器能够将函数或数据与其余输入文件分开处理。
链接器有两种方法可以让您将段放置在特定地址:
  • 您可以创建一个分散文件,该文件在所需地址处定义一个执行区,并带有仅选择一个段的段描述。
  • 对于特殊命名的段,链接器可以从段名中获取放置地址。这些专门命名的部分称为[size=0.9em]__at段。
要将函数或变量放置在特定地址,必须将其放置在其自己的段中。有几种方法可以做到这一点:
  • 将函数或数据项放在其自己的源文件中。
  • 使用到地方变量在一个单独的部分,在一个特定的地址[size=0.9em]__attribute__((at(address)))
  • 用于在指定段中放置函数和变量[size=0.9em]__attribute__((section("name")))
  • 使用[size=0.9em]AREA汇编语言中的指令。在汇编代码中,最小的可定位单元是[size=0.9em]AREA.
  • 使用[size=0.9em]--split_sections编译器选项为源文件中的每个函数生成一个 ELF 部分。此选项会导致某些函数的代码大小略有增加,因为它降低了在函数之间共享地址、数据和字符串文字的可能性。但是,当您指定[size=0.9em]armlink --remove这可以帮助减少最终固件镜像整体大小,使链接器能够删除未使用的函数。
在没有分散加载的情况下将变量放置在特定地址的示例
此示例显示如何修改源代码以将代码和数据放置在特定地址,并且不需要分散文件:1、创建main.c包含以下代码的源文件:
#include <stdio.h>extern int sqr(int n1);int gSquared __attribute__((at(0x5000)));  // Place at 0x5000int main(){    gSquared=sqr(3);    printf("Value squared is: %d\n", gSquared);}12345678910
2、创建function.c包含以下代码的源文件:
int sqr(int n1){    return n1*n1;}1234
3、编译并链接源:
armcc -c -g function.carmcc -c -g main.carmlink --map function.o main.o -o squared.axf123
--map选项用于生成内存映射文件即.map文件,同样--autoat是默认值
在此示例中,__attribute__((at(0x5000)))指定将全局变量gSquared放置在绝对地址处0x20000gSquared被放置在执行区ER$$.ARM.__AT_0x00005000和加载区中LR$$.ARM.__AT_0x00005000
The memory map shows:
...  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           15    .ARM.__AT_0x00005000  main.o123456789
使用分散加载将变量放置在指定段中的示例
此示例显示如何使用分散文件修改源代码以将代码和数据放置在特定部分中:1、创建main.c包含以下代码的源文件:
#include <stdio.h>extern int sqr(int n1);int gSquared __attribute__((section("foo")));  // Place in section fooint main(){    gSquared=sqr(3);    printf("Value squared is: %d\n", gSquared);}12345678910
2、创建function.c包含以下代码的源文件:
int sqr(int n1){    return n1*n1;}1234
3、创建scatter.scat包含以下加载区域的分散文件:
LR1 0x0000 0x20000{    ER1 0x0 0x2000    {        *(+RO)                      ; rest of code and read-only data    }    ER2 0x8000 0x2000    {        main.o    }    ER3 0x10000 0x2000    {        function.o        *(foo)                      ; Place gSquared in ER3    }    RAM 0x200000 (0x1FF00-0x2000)   ; RW & ZI data to be placed at 0x200000    {        *(+RW, +ZI)    }    ARM_LIB_STACK 0x800000 EMPTY -0x10000    {    }    ARM_LIB_HEAP  +0 EMPTY 0x10000    {    }}1234567891011121314151617181920212223242526
ARM_LIB_STACKARM_LIB_HEAP都需要,因为程序将与半主机库链接。4、编译并链接
armcc -c -g function.carmcc -c -g main.caarmlink --map --scatter=scatter.scat function.o main.o -o squared.axf123
内存映射显示:
  Load Region LR1 (Base: 0x00000000, Size: 0x00001778, Max: 0x00020000, ABSOLUTE)...    Execution Region ER3 (Base: 0x00010000, Size: 0x00000004, Max: 0x00002000, ABSOLUTE)    Base Addr    Size         Type   Attr      Idx    E Section Name        Object    0x00010000   0x00000004   Data   RW           15    foo                 main.o...12345678



使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

86

主题

1697

帖子

1

粉丝