STM32程序分散加载文件全面解析,彻底搞透

一起学嵌入式 2023-10-17 08:15

点击下方一起学嵌入式】关注,一起学习,一起成长

前两篇文章介绍了 STM32 程序的分散加载过程,并对分散文件进行了刨析:

STM32 程序是如何被分散加载的

MDK 分散加载文件(.sct)剖析及应用

这一篇文章,深入分析一下 MDK 的分散加载文件,力求能够将分散文件以及程序的加载过程贯穿起来,彻底搞懂这个知识点。

使用分散文件指定栈和堆

ARM C 库提供了函数 __user_setup_stackheap() 的多个实现,并且可以根据分散文件中提供的信息自动选择正确的一种。

要选择双区域内存模型,请在分散文件中定义两个名为 ARM_LIB_HEAPARM_LIB_STACK的特殊执行区域。这两个区域都有 EMPTY 属性,会导致库选择__user_setup_stackheap()的非默认实现,使用以下的符号值:

Image$$ARM_LIB_STACK$$Base
Image$$ARM_LIB_STACK$$ZI$$Limit
Image$$ARM_LIB_HEAP$$Base
Image$$ARM_LIB_HEAP$$ZI$$Limit

只能指定一个ARM_LIB_STACKARM_LIB_HEAP区域,并且必须分配一个大小,例如:

ARM_LIB_HEAP 0x20100000 EMPTY 0x100000-0x8000  ; Heap starts at 1MB
                                               ; and grows upwards
ARM_LIB_STACK 0x20200000 EMPTY -0x8000         ; Stack space starts at the end
                                               ; of the 2MB of RAM
                                               ; And grows downwards for 32KB
可以通过定义名为 ARM_LIB_STACKHEAP 单一执行区域使用组合的栈和堆区域ARM_LIB_STACKHEAP,与EMPTY属性。这会导致__user_setup_stackheap()使用符号Image$$ARM_LIB_STACKHEAP$$BaseImage$$ARM_LIB_STACKHEAP$$ZI$$Limit的值。
注意,如果重新实现__user_setup_stackheap(),这将覆盖所有库里面的实现。

创建 root执行区

要将区域指定为分散文件中的根区域,您可以:

  • 指定 ABSOLUTE为执行区的属性(显式或允许它默认),并为第一个执行区和封闭加载区使用相同的地址。要使执行区地址与加载区地址相同,请执行以下任一操作:

    • 为执行区的基地址和加载区的基地址指定相同的数值。
    • 指定+0加载区中第一个执行区的偏移量。如果+0为加载区中的所有后续执行区指定零偏移(+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
}
  • 使用FIXED执行区属性可以确保特定区域的加载地址和执行地址相同。您可以使用该FIXED属性将任何执行区放置在 ROM 中的特定地址。例如,以下内存映射显示了固定执行区:图 8. 固定执行区的内存映射

下面的例子给出了相应分散加载的描述:

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
}

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
}

使用 FIXED 属性创建根区域

您可以FIXED在执行区分散文件中使用该属性来创建在固定地址加载和执行的根区。FIXED用于在单个加载区域内创建多个根区域,因此通常是单个 ROM 设备。例如,您可以使用它来将函数或数据块(例如常量表或校验和)放置在 ROM 中的固定地址,以便可以通过指针轻松访问。

例如,如果您指定将一些初始化代码放置在 ROM 的开头并在 ROM 的末尾放置一个校验和,则某些内存内容可能未被使用。使用*或.ANY模块选择器来填充初始化块末尾和数据块开头之间的区域。

为了使您的代码更易于维护和调试,建议您在分散文件中使用最少的布局规范,并将函数和数据的详细布局留给链接器。

您不能指定已部分链接的组件对象。例如,如果您将对象obj1.oobj2.o和部分链接obj3.o在一起以产生obj_all.o,则在生成的对象中会丢弃组件对象名称。因此,您不能按名称引用其中一个对象,例如,obj1.o。您只能引用组合对象obj_all.o

注意在某些情况下,使用FIXED和 单个加载区域是不合适的。指定固定位置的其他方式是:

  • 如果您的加载程序可以处理多个加载区域,请将 RO 代码或数据放在其自己的加载区域中。
  • 如果您不要求函数或数据位于 ROM 中的固定位置,请使用ABSOLUTE代替FIXED。然后加载器将数据从加载区复制到 RAM 中的指定地址。ABSOLUTE是默认属性。
  • 要将数据结构放置在内存映射 I/O 的位置??,请使用两个加载区域并指定UNINIT. UNINIT确保内存位置不会被初始化为零。

在特定地址放置函数和数据

通常,编译器从单个源文件生成 RO、RW 和 ZI 节。这些区域包含源文件中的所有代码和数据。要将单个函数或数据项放置在固定地址,您必须使链接器能够将函数或数据与其余输入文件分开处理。

链接器有两种方法可以让您将段放置在特定地址:

  • 您可以创建一个分散文件,该文件在所需地址处定义一个执行区,并带有仅选择一个段的段描述。
  • 对于特殊命名的段,链接器可以从段名中获取放置地址。这些专门命名的部分称为__at段。

要将函数或变量放置在特定地址,必须将其放置在其自己的段中。有几种方法可以做到这一点:

  • 将函数或数据项放在其自己的源文件中。
  • 使用到地方变量在一个单独的部分,在一个特定的地址__attribute__((at(address)))
  • 用于在指定段中放置函数和变量__attribute__((section("name")))
  • 使用AREA汇编语言中的指令。在汇编代码中,最小的可定位单元是AREA.
  • 使用--split_sections编译器选项为源文件中的每个函数生成一个 ELF 部分。此选项会导致某些函数的代码大小略有增加,因为它降低了在函数之间共享地址、数据和字符串文字的可能性。但是,当您指定armlink --remove这可以帮助减少最终固件镜像整体大小,使链接器能够删除未使用的函数。

在没有分散加载的情况下将变量放置在特定地址的示例

此示例显示如何修改源代码以将代码和数据放置在特定地址,并且不需要分散文件:1、创建main.c包含以下代码的源文件:

#include 

extern int sqr(int n1);
int gSquared __attribute__((at(0x5000)));  // Place at 0x5000

int main()
{
    gSquared=sqr(3);
    printf("Value squared is: %d\n", gSquared);
}

2、创建function.c包含以下代码的源文件:

int sqr(int n1)
{
    return n1*n1;
}

3、编译并链接源:

armcc -c -g function.c
armcc -c -g main.c
armlink --map function.o main.o -o squared.axf
--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.o
使用分散加载将变量放置在指定段中的示例

此示例显示如何使用分散文件修改源代码以将代码和数据放置在特定部分中:1、创建main.c包含以下代码的源文件:

#include 

extern int sqr(int n1);
int gSquared __attribute__((section("foo")));  // Place in section foo

int main()
{
    gSquared=sqr(3);
    printf("Value squared is: %d\n", gSquared);
}

2、创建function.c包含以下代码的源文件:

int sqr(int n1)
{
    return n1*n1;
}

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
    {
    }
}

ARM_LIB_STACKARM_LIB_HEAP都需要,因为程序将与半主机库链接。4、编译并链接

armcc -c -g function.c
armcc -c -g main.c
aarmlink --map --scatter=scatter.scat function.o main.o -o squared.axf

内存映射显示:

  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
...

注意

如果*(foo)从分散文件中省略,则该部分将放置在相同类型的区域中。在这个例子中就是RAM区。

使用分散加载将变量放置在特定地址的示例

1、创建main.c包含以下代码的源文件

#include 

extern int sqr(int n1);

// Place at address 0x10000
const int gValue __attribute__((section(".ARM.__at_0x10000"))) = 3;

int main()
{
    int squared;
    squared=sqr(gValue);
    printf("Value squared is: %d\n", squared);
}

2、创建function.c包含以下代码的源文件:

int sqr(int n1)
{
    return n1*n1;
}

3、创建scatter.scat包含以下加载区域的分散文件:

LR1 0x0
{
    ER1 0x0
    {
        *(+RO)                      ; rest of code and read-only data
    }
    ER2 +0
    {
        function.o
        *(.ARM.__at_0x10000)         ; Place gValue at 0x10000
    }
    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
    {
    }
}

ARM_LIB_STACKARM_LIB_HEAP都需要,因为程序将与半主机库链接。

4、编译并链接

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在地址处的执行区中0x11000:

...
    Execution Region ER2 (Base: 0x00001598, Size: 0x0000ea6c, Max: 0xffffffff, ABSOLUTE)

    Base Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x00001598   0x0000000c   Code   RO            3    .text               function.o
    0x000015a4   0x0000ea5c   PAD
    0x00010000   0x00000004   Data   RO           15    .ARM.__at_0x10000   main.o...

在这个例子中,ER1的大小是未知的。因此,gValue可能放在ER1或 中ER2。要确保将gValue其放置在ER2中,您必须包含相应的选择器ER2并与--no_autoat命令行选项链接。如果省略--no_autoat, gValue将被放在一个单独的加载区域LR$$.ARM.__AT_0x00010000,包含执行区域ER$$.ARM.__AT_0x00020000

变量指定段

方式一

int variable __attribute__((section("foo"))) = 10;
1
FLASH 0x24000000 0x4000000
{
    ...                                ; rest of code

    ADDER 0x08000000
    {
        file.o (foo)                  ; select section foo from file.o
    }
}

方式二

// place variable1 in a section called .ARM.__at_0x00008000
int variable1 __attribute__((at(0x8000))) = 10;

// place variable2 in a section called .ARM.__at_0x8000
int variable2 __attribute__((section(".ARM.__at_0x8000"))) = 10;
12345
ER_FLASH 0x8000 0x2000
{
    *(+RO)
    *(.ARM.__at_0x8000) ;
}

函数地址指定

int sqr(int n1) __attribute__((section(".ARM.__at_0x20000")));

int sqr(int n1)
{
    return n1*n1;
}

注意

  • 如果不使用分散加载,则该部分将放置在加载区的默认ER_RW执行区中LR_1
  • 如果源码中使用了未定义段名(分散加载文件中无此段名),则该部分将放置在定义的 RW 执行区中
  • --autoat or --no_autoat 不影响放置

使用分散加载显式放置命名部分

以下示例显示如何使用分散加载显式放置命名部分:

LR1 0x0 0x10000
{
    ER1 0x0 0x2000                   ; Root Region, containing init code
    {
        init.o (INIT, +FIRST)        ; place init code at exactly 0x0
        *(+RO)                       ; rest of code and read-only data  
    }
    RAM_RW 0x400000 (0x1FF00-0x2000) ; RW & ZI data to be placed at 0x400000
    {
        *(+RW)
    }
    RAM_ZI +0
    {
        *(+ZI)
    }
    DATABLOCK 0x1FF00 0xFF           ; execution region at 0x1FF00
    {                                ; maximum space available for table is 0xFF
        data.o(+RO-DATA)             ; place RO data between 0x1FF00 and 0x1FFFF
    }
}

在这个例子中,分散加载描述放置:

  • 初始化代码放在文件的INIT段中init.o。此示例显示该INIT段中的代码首先放置在地址 处0x0,然后是 RO 代码的其余部分以及除对象中的 RO 数据之外的所有 RO 数据data.o。
  • RAM 中的所有全局 RW 变量位于0x400000
  • data.o中的所有RO-DATA数据放置在0x1FF00

使用.ANY模块选择器放置未分配的段

链接器尝试将输入节放入特定的执行区。对于无法解析的任何输入部分,并且这些部分的放置不重要,您可以使用.ANY分散文件中的模块选择器。

在大多数情况下,使用单个.ANY选择器等同于使用*模块选择器。但是不同的是,您可以.ANY在多个执行区中指定。

放置未分配段的默认规则

默认情况下,链接器使用以下条件放置未分配的段:

  • 在当前拥有最多可用空间的执行区中放置一个未分配的段。您可以使用执行区域属性指定用于未分配段的最大空间量ANY_SIZE
  • 按大小降序对部分进行排序。

使用多个.ANY选择器时的放置规则

如果分散文件中存在多个.ANY 选择器,则链接器采用最大大小的未分配段并将该段分配给具有足够可用空间的最具体的.ANY执行区。例如,.ANY(.text)被判断为比 .ANY(+RO)更具体。

如果多个执行区具有相同的特性,则该段将分配给具有最多可用剩余空间的执行区。

例如:

  • 如果您有两个同样特定的执行区,其中一个的大小限制为0x2000,另一个没有限制,则所有段都分配给第二个无界.ANY区域。
  • 如果你有两个同样的特定执行区,其中一个大小限制为0x2000和另一个大小限制为0x3000,然后第一个段将被分配到第二个.ANY(区域大小限制0x3000),直到第二个.ANY剩余的大小减少到0x2000。从这一点开始,section在两个.ANY执行区域之间交替分配。

.ANY优先段

如果您有多个.ANY带有选择器的部分,您可以给出优先顺序,其中是从零向上的正整数。最高优先级被赋予具有最高整数的选择器。.ANYnum以下示例显示了如何使用:.ANYnum

lr1 0x8000 1024
{
    er1 +0 512
    {
        .ANY1(+RO) ; evenly distributed with er3
    }
    er2 +0 256
    {
        .ANY2(+RO) ; Highest priority, so filled first
    }
    er3 +0 256
    {
        .ANY1(+RO) ; evenly distributed with er1
    }
}

控制多个.ANY选择器的输入段的放置

.ANY通过使用不同的放置算法或不同的排序顺序,您可以修改链接器在使用多个选择器时放置未分配输入段的方式。以下命令行选项可用:

  • --any_placement=algorithm, 其中algorithm是first_fit, worst_fit, best_fit, 或next_fit之一
  • --any_sort_order=order,其中order是cmdlinedescending_size之一

first_fit当您想要按顺序填充区域时使用。best_fit当您想最大程度地填充区域时使用。worst_fit当您想要均匀填充区域时使用。使用相同大小的区域和部分worst_fit循环填充区域。当您需要更具确定性的填充模式时,请使用 next_fit。

如果链接器尝试将区域填充到其极限,就像使用first_fit和 一样best_fit,它可能会过度填充该区域。这是因为在将节分配给.ANY选择器之前,链接器生成的内容(例如填充和单板)是未知的。如果发生这种情况,您可能会看到以下错误:

Error: L6220E: Execution region regionname size (size bytes) exceeds limit (limit bytes). 错误:L6220E:执行区regionname大小(size字节)超过限制(limit字节)。

--any_contingency选项可防止链接器将区域填充到最大值。它为链接器生成的内容保留了该区域大小的一部分,并且仅当其他区域没有空间时才填充此应急区域。默认情况下为first_fitbest_fit算法启用它,因为它们最有可能表现出这种行为。

指定允许放置未分配段的最大尺寸

执行区属性使您能够指定armlink可以用未分配的节填充的区域中的最大大小。ANY_SIZE max_size

使用此关键字时请注意以下限制:

  • max_size必须小于或等于区域大小
  • 您可以ANY_SIZE在没有.ANY选择器的区域上使用,但它会被armlink忽略

当ANY_SIZE存在时,armlink:

  • 不覆盖给定的.ANY大小。也就是说,它不会降低优先级,然后尝试在稍后放入更多段。
  • 从不重新计算意外事件。
  • 从不分配应急空间中的段。

ANY_SIZE不需要--any_contingency指定。但是,无论何时--any_contingency指定和ANY_SIZE未指定,armlink 都会尝试调整意外情况。目标是:

  • 永远不会溢出一个.ANY区域
  • 永远不要拒绝在应急保留空间中放置一个段。

如果您--any_contingency在命令行上指定,则对于已ANY_SIZE指定的区域将忽略它。它通常用于未ANY_SIZE指定的区域。

以下示例显示了如何使用ANY_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
   }
}

在这个例子中:

ER_1为链接器生成的内容保留了0x100。ER_2为链接器生成的内容保留了0x50。这和--any_contingency的自动应急保留类似。ER_3没有预留空间。因此,100%的区域被填满,没有应急保留。省略ANY_SIZE参数会导致98%的区域被填满,只有2%的应急保留。

使用 __at 在外设寄存器上放置

要将未初始化的变量放置在外设寄存器上,您可以使用 ZI__at部分。假设一个寄存器可用于0x10000000,定义一个__at名为.ARM.__at_0x10000000. 例如:

int foo __attribute__((section(".ARM.__at_0x10000000"), zero_init));
1
ER_PERIPHERAL 0x10000000 UNINIT
{
    *(.ARM.__at_0x10000000)
}

使用自动放置,并假设附近没有其他执行区0x10000000,链接器会自动创建一个UNINIT属性为 at的区域0x10000000。该UNINIT属性创建一个包含未初始化数据或内存映射 I/O 的执行区。

预留一个空区域

可以EMPTY在执行区分散加载描述中使用该属性来为堆栈保留一块空内存。

内存块不构成加载区的一部分,而是在执行时分配使用。因为它是作为虚拟 ZI 区域创建的,所以链接器使用以下符号来访问它:

  • Image$$region_name$$ZI$$Base
  • Image$$region_name$$ZI$$Limit
  • Image$$region_name$$ZI$$Length

如果长度为负值,则该地址被视为区域的结束地址。这必须是绝对地址而不是相对地址。

在以下示例中,执行区定义STACK 0x800000 EMPTY -0x10000定义了一个名为的区域STACK,该区域从 address 开始并在 address0x7F0000结束0x800000:

LR_1 0x80000                          ; load region starts at 0x80000   
{
    STACK 0x800000 EMPTY -0x10000     ; region ends at 0x800000 because of the
                                      ; negative length. The start of the region
                                      ; is calculated using the length.
    {
                                      ; Empty region used to place stack
    }
    HEAP +0 EMPTY 0x10000             ; region starts at the end of previous
                                      ; region. End of region calculated using
                                      ; positive length
    {
                                      ; Empty region used to place heap
    }
    .   ..                               ; rest of scatter-loading description...
}

注意

为EMPTY执行区域创建的虚拟ZI区域在运行时不会初始化为零。

如果地址是相对的(+offset)形式并且长度是负的,链接器会产生一个错误。下图显示了该示例的图解表示。

图 9. 为堆栈保留一个区域


在本例中,链接器生成符号:

Image$$STACK$$ZI$$Base      = 0x7f0000
Image$$STACK$$ZI$$Limit     = 0x800000
Image$$STACK$$ZI$$Length    = 0x10000
Image$$HEAP$$ZI$$Base       = 0x800000
Image$$HEAP$$ZI$$Limit      = 0x810000
Image$$HEAP$$ZI$$Length     = 0x10000

该EMPTY属性仅适用于执行区。链接器生成警告并忽略EMPTY加载区定义中使用的属性。

链接器检查用于该EMPTY区域的地址空间是否与任何其他执行区域不一致。

在分散文件中使用预处理命令

您可以通过 C 预处理器传递分散文件。这允许访问 C 预处理器的所有功能。

使用分散文件中的第一行指定链接器调用以处理文件的预处理器命令。命令的格式如下:

#! preprocessor [
pre_processor_flags
]

最典型的命令是#! armcc -E. 这会通过armcc预处理器传递分散文件。

你可以:

  • 将预处理指令添加到分散文件的顶部
  • 在分散文件中使用简单的表达式评估。

例如,分散文件file.scat, 可能包含:

#! armcc -E

#define ADDRESS 0x20000000
#include "include_file_1.h"

lr1 ADDRESS
{
    ...
}

链接器解析预处理后的分散文件并将指令视为注释。

您还可以将分散文件的预处理与–predefine命令行选项结合使用。对于这个例子:

  • 修改file.scat以删除指令。#define ADDRESS 0x20000000
  • 指定命令:armlink --predefine="-DADDRESS=0x20000000" --scatter=file.scat

在分散文件中使用表达式求值以避免填充

使用ALIGN,ALIGNALL或FIXED在分散的文件属性可导致在镜像中的大量填充的。要删除此填充,您可以使用表达式计算来指定加载区和执行区的起始地址。内置函数AlignExpr可用于帮助您指定地址表达式。

避免在分散文件中填充的示例 以下分散文件生成带有填充的图像:

LR1 0x4000
{
    ER1 +0 ALIGN 0x8000
    {
        ...
    }
}

使用ALIGN关键字ER10x8000加载视图和执行视图中的边界对齐。要在加载视图中对齐,链接器必须插入0x4000填充字节。

以下分散文件生成没有填充的图像:

LR1 0x4000
{
    ER1 AlignExpr(+0, 0x8000)
    {
        ...
    }
}

使用AlignExpr的结果+00x8000边界对齐。这将创建一个执行区,其加载地址为0x4000但执行地址为0x8000。

参考 : 

https://developer.arm.com/documentation/dui0474/f/using-scatter-files

文章来源于网络,版权归原作者所有,如有侵权,请联系删除。



关注我【一起学嵌入式】,一起学习,一起成长。



觉得文章不错,点击“分享”、“”、“在看” 呗!

一起学嵌入式 公众号【一起学嵌入式】,RTOS、Linux编程、C/C++,以及经验分享、行业资讯、物联网等技术知
评论
  • 日前,商务部等部门办公厅印发《手机、平板、智能手表(手环)购新补贴实施方案》明确,个人消费者购买手机、平板、智能手表(手环)3类数码产品(单件销售价格不超过6000元),可享受购新补贴。每人每类可补贴1件,每件补贴比例为减去生产、流通环节及移动运营商所有优惠后最终销售价格的15%,每件最高不超过500元。目前,京东已经做好了承接手机、平板等数码产品国补优惠的落地准备工作,未来随着各省市关于手机、平板等品类的国补开启,京东将第一时间率先上线,满足消费者的换新升级需求。为保障国补的真实有效发放,基于
    华尔街科技眼 2025-01-17 10:44 211浏览
  • 嘿,咱来聊聊RISC-V MCU技术哈。 这RISC-V MCU技术呢,简单来说就是基于一个叫RISC-V的指令集架构做出的微控制器技术。RISC-V这个啊,2010年的时候,是加州大学伯克利分校的研究团队弄出来的,目的就是想搞个新的、开放的指令集架构,能跟上现代计算的需要。到了2015年,专门成立了个RISC-V基金会,让这个架构更标准,也更好地推广开了。这几年啊,这个RISC-V的生态系统发展得可快了,好多公司和机构都加入了RISC-V International,还推出了不少RISC-V
    丙丁先生 2025-01-21 12:10 65浏览
  • Ubuntu20.04默认情况下为root账号自动登录,本文介绍如何取消root账号自动登录,改为通过输入账号密码登录,使用触觉智能EVB3568鸿蒙开发板演示,搭载瑞芯微RK3568,四核A55处理器,主频2.0Ghz,1T算力NPU;支持OpenHarmony5.0及Linux、Android等操作系统,接口丰富,开发评估快人一步!添加新账号1、使用adduser命令来添加新用户,用户名以industio为例,系统会提示设置密码以及其他信息,您可以根据需要填写或跳过,命令如下:root@id
    Industio_触觉智能 2025-01-17 14:14 91浏览
  • 高速先生成员--黄刚这不马上就要过年了嘛,高速先生就不打算给大家上难度了,整一篇简单但很实用的文章给大伙瞧瞧好了。相信这个标题一出来,尤其对于PCB设计工程师来说,心就立马凉了半截。他们辛辛苦苦进行PCB的过孔设计,高速先生居然说设计多大的过孔他们不关心!另外估计这时候就跳出很多“挑刺”的粉丝了哈,因为翻看很多以往的文章,高速先生都表达了过孔孔径对高速性能的影响是很大的哦!咋滴,今天居然说孔径不关心了?别,别急哈,听高速先生在这篇文章中娓娓道来。首先还是要对各位设计工程师的设计表示肯定,毕竟像我
    一博科技 2025-01-21 16:17 62浏览
  • 现在为止,我们已经完成了Purple Pi OH主板的串口调试和部分配件的连接,接下来,让我们趁热打铁,完成剩余配件的连接!注:配件连接前请断开主板所有供电,避免敏感电路损坏!1.1 耳机接口主板有一路OTMP 标准四节耳机座J6,具备进行音频输出及录音功能,接入耳机后声音将优先从耳机输出,如下图所示:1.21.2 相机接口MIPI CSI 接口如上图所示,支持OV5648 和OV8858 摄像头模组。接入摄像头模组后,使用系统相机软件打开相机拍照和录像,如下图所示:1.3 以太网接口主板有一路
    Industio_触觉智能 2025-01-20 11:04 125浏览
  •  光伏及击穿,都可视之为 复合的逆过程,但是,复合、光伏与击穿,不单是进程的方向相反,偏置状态也不一样,复合的工况,是正偏,光伏是零偏,击穿与漂移则是反偏,光伏的能源是外来的,而击穿消耗的是结区自身和电源的能量,漂移的载流子是 客席载流子,须借外延层才能引入,客席载流子 不受反偏PN结的空乏区阻碍,能漂不能漂,只取决于反偏PN结是否处于外延层的「射程」范围,而穿通的成因,则是因耗尽层的过度扩张,致使跟 端子、外延层或其他空乏区 碰触,当耗尽层融通,耐压 (反向阻断能力) 即告彻底丧失,
    MrCU204 2025-01-17 11:30 160浏览
  • 2024年是很平淡的一年,能保住饭碗就是万幸了,公司业绩不好,跳槽又不敢跳,还有一个原因就是老板对我们这些员工还是很好的,碍于人情也不能在公司困难时去雪上加霜。在工作其间遇到的大问题没有,小问题还是有不少,这里就举一两个来说一下。第一个就是,先看下下面的这个封装,你能猜出它的引脚间距是多少吗?这种排线座比较常规的是0.6mm间距(即排线是0.3mm间距)的,而这个规格也是我们用得最多的,所以我们按惯性思维来看的话,就会认为这个座子就是0.6mm间距的,这样往往就不会去细看规格书了,所以这次的运气
    wuliangu 2025-01-21 00:15 97浏览
  • 数字隔离芯片是一种实现电气隔离功能的集成电路,在工业自动化、汽车电子、光伏储能与电力通信等领域的电气系统中发挥着至关重要的作用。其不仅可令高、低压系统之间相互独立,提高低压系统的抗干扰能力,同时还可确保高、低压系统之间的安全交互,使系统稳定工作,并避免操作者遭受来自高压系统的电击伤害。典型数字隔离芯片的简化原理图值得一提的是,数字隔离芯片历经多年发展,其应用范围已十分广泛,凡涉及到在高、低压系统之间进行信号传输的场景中基本都需要应用到此种芯片。那么,电气工程师在进行电路设计时到底该如何评估选择一
    华普微HOPERF 2025-01-20 16:50 41浏览
  •  万万没想到!科幻电影中的人形机器人,正在一步步走进我们人类的日常生活中来了。1月17日,乐聚将第100台全尺寸人形机器人交付北汽越野车,再次吹响了人形机器人疯狂进厂打工的号角。无独有尔,银河通用机器人作为一家成立不到两年时间的创业公司,在短短一年多时间内推出革命性的第一代产品Galbot G1,这是一款轮式、双臂、身体可折叠的人形机器人,得到了美团战投、经纬创投、IDG资本等众多投资方的认可。作为一家成立仅仅只有两年多时间的企业,智元机器人也把机器人从梦想带进了现实。2024年8月1
    刘旷 2025-01-21 11:15 53浏览
  • 本文介绍瑞芯微开发板/主板Android配置APK默认开启性能模式方法,开启性能模式后,APK的CPU使用优先级会有所提高。触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。源码修改修改源码根目录下文件device/rockchip/rk3562/package_performance.xml并添加以下内容,注意"+"号为添加内容,"com.tencent.mm"为AP
    Industio_触觉智能 2025-01-17 14:09 132浏览
  • 随着消费者对汽车驾乘体验的要求不断攀升,汽车照明系统作为确保道路安全、提升驾驶体验以及实现车辆与环境交互的重要组成,日益受到业界的高度重视。近日,2024 DVN(上海)国际汽车照明研讨会圆满落幕。作为照明与传感创新的全球领导者,艾迈斯欧司朗受邀参与主题演讲,并现场展示了其多项前沿技术。本届研讨会汇聚来自全球各地400余名汽车、照明、光源及Tier 2供应商的专业人士及专家共聚一堂。在研讨会第一环节中,艾迈斯欧司朗系统解决方案工程副总裁 Joachim Reill以深厚的专业素养,主持该环节多位
    艾迈斯欧司朗 2025-01-16 20:51 166浏览
  • 在物联网(IoT)短距无线通信生态系统中,低功耗蓝牙(BLE)数据透传是一种无需任何网络或基础设施即可完成双向通信的技术。其主要通过简单操作串口的方式进行无线数据传输,最高能满足2Mbps的数据传输速率,可轻松实现设备之间的快速数据同步和实时交互,例如传输传感器数据、低采样率音频/图像与控制指令等。低功耗蓝牙(BLE)数据透传解决方案组网图具体而言,BLE透传技术是一种采用蓝牙通信协议在设备之间实现数据透明传输的技术,设备在通信时会互相验证身份和安全密钥,具有较高的安全性。在不对MCU传输数据进
    华普微HOPERF 2025-01-21 14:20 60浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦